@aifabrix/builder 2.51.1 → 2.52.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/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/down.js +16 -1
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +78 -62
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +35 -10
- package/lib/commands/channel.js +27 -12
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/lifecycle-external.js +14 -7
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +131 -114
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +434 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +104 -89
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
- package/lib/role-assistant/test-runner-workhub.js +121 -25
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +64 -32
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +13 -15
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/transient-developer-id.js +28 -0
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +5 -5
- package/templates/applications/dataplane/env.template +8 -7
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +25 -11
- package/templates/applications/miso-controller/env.template +24 -5
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +76 -36
- package/templates/channels/chatgpt/integration/README.md +4 -4
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suite BID load / discovery emit for Work Hub apply waits (512.4 GP).
|
|
3
|
+
* @fileoverview Case BID template + discovery compile + suite stamp
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('../internal/fs-real-sync');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} testCase
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function resolveTargetSystemKey(testCase) {
|
|
16
|
+
const input =
|
|
17
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
18
|
+
for (const key of ['targetSystemKey', 'integrationSystemKey', 'systemKey']) {
|
|
19
|
+
const value = input[key];
|
|
20
|
+
if (typeof value === 'string' && value.trim()) {
|
|
21
|
+
return value.trim();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Object} testCase
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
function shouldStampSuiteBid(testCase) {
|
|
32
|
+
const input =
|
|
33
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
34
|
+
if (input.stampSuiteBid === true) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (input.stampSuiteBid === false) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const expected =
|
|
41
|
+
testCase && typeof testCase.expected === 'object' && testCase.expected
|
|
42
|
+
? testCase.expected
|
|
43
|
+
: {};
|
|
44
|
+
return expected.soft === false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {Object} testCase
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function resolveSuiteRunId(testCase) {
|
|
52
|
+
const fromActive = String(testCase?._activeProcessExecutionId || '').trim();
|
|
53
|
+
if (fromActive) {
|
|
54
|
+
return fromActive;
|
|
55
|
+
}
|
|
56
|
+
const input =
|
|
57
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
58
|
+
return String(input.suiteRunId || '').trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {Object} testCase
|
|
63
|
+
* @param {Object} bid
|
|
64
|
+
*/
|
|
65
|
+
function maybePersistEmittedBid(testCase, bid) {
|
|
66
|
+
const input =
|
|
67
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
68
|
+
const outPath = typeof input.emittedBidPath === 'string' ? input.emittedBidPath.trim() : '';
|
|
69
|
+
if (!outPath || !bid) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const baseDir =
|
|
73
|
+
typeof testCase.sourceFile === 'string' && testCase.sourceFile.trim()
|
|
74
|
+
? path.dirname(testCase.sourceFile)
|
|
75
|
+
: process.cwd();
|
|
76
|
+
const abs = path.isAbsolute(outPath) ? outPath : path.resolve(baseDir, outPath);
|
|
77
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
78
|
+
fs.writeFileSync(abs, `${JSON.stringify(bid, null, 2)}\n`, 'utf8');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {Object} testCase
|
|
83
|
+
* @returns {Object|undefined}
|
|
84
|
+
*/
|
|
85
|
+
function loadBidTemplate(testCase) {
|
|
86
|
+
const input =
|
|
87
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
88
|
+
const bidPath = typeof input.bidPath === 'string' ? input.bidPath.trim() : '';
|
|
89
|
+
if (!bidPath) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const baseDir =
|
|
93
|
+
typeof testCase.sourceFile === 'string' && testCase.sourceFile.trim()
|
|
94
|
+
? path.dirname(testCase.sourceFile)
|
|
95
|
+
: process.cwd();
|
|
96
|
+
const abs = path.isAbsolute(bidPath) ? bidPath : path.resolve(baseDir, bidPath);
|
|
97
|
+
if (!fs.existsSync(abs)) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(fs.readFileSync(abs, 'utf8'));
|
|
102
|
+
return parsed && typeof parsed === 'object' ? parsed : undefined;
|
|
103
|
+
} catch (_err) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {Object} testCase
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
function shouldEmitBidFromAnswers(testCase) {
|
|
113
|
+
const input =
|
|
114
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
115
|
+
if (input.emitBidFromAnswers === false) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (!shouldStampSuiteBid(testCase) && input.emitBidFromAnswers !== true) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
|
|
122
|
+
return answers.some((entry) => {
|
|
123
|
+
const key = String(entry?.when?.questionKey || '')
|
|
124
|
+
.trim()
|
|
125
|
+
.toLowerCase();
|
|
126
|
+
return key === 'system.apigroups';
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {Object} testCase
|
|
132
|
+
* @returns {string|undefined}
|
|
133
|
+
*/
|
|
134
|
+
function resolveAssistantKey(testCase) {
|
|
135
|
+
const input =
|
|
136
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
137
|
+
if (typeof input.digitalWorkerKey === 'string' && input.digitalWorkerKey.trim()) {
|
|
138
|
+
return input.digitalWorkerKey.trim();
|
|
139
|
+
}
|
|
140
|
+
if (typeof input.businessRoleRef === 'string' && input.businessRoleRef.trim()) {
|
|
141
|
+
return input.businessRoleRef.trim();
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resolveConnectionRefs(input, template) {
|
|
147
|
+
if (input.connectionRefs && typeof input.connectionRefs === 'object') {
|
|
148
|
+
return input.connectionRefs;
|
|
149
|
+
}
|
|
150
|
+
if (template && template.system && template.system.connectionRefs) {
|
|
151
|
+
return template.system.connectionRefs;
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function packageField(template, field, fallback) {
|
|
157
|
+
const identity = template && template.packageIdentity;
|
|
158
|
+
if (identity && identity[field]) {
|
|
159
|
+
return identity[field];
|
|
160
|
+
}
|
|
161
|
+
return fallback;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildDiscoveryEmitOptions(testCase, suiteRunId, template, systemKey) {
|
|
165
|
+
const input =
|
|
166
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
167
|
+
const openapiSourceRefs =
|
|
168
|
+
input.openapiSourceRefs && typeof input.openapiSourceRefs === 'object'
|
|
169
|
+
? input.openapiSourceRefs
|
|
170
|
+
: undefined;
|
|
171
|
+
const knowledgePackRefs =
|
|
172
|
+
template && template.meta && Array.isArray(template.meta.knowledgePackRefs)
|
|
173
|
+
? template.meta.knowledgePackRefs
|
|
174
|
+
: undefined;
|
|
175
|
+
return {
|
|
176
|
+
answers: testCase.answers,
|
|
177
|
+
systemKey,
|
|
178
|
+
displayName: packageField(template, 'displayName', systemKey),
|
|
179
|
+
packageVersion: packageField(template, 'packageVersion', '0.1.0'),
|
|
180
|
+
systemKind: (template && template.system && template.system.systemKind) || 'crm',
|
|
181
|
+
connectionRefs: resolveConnectionRefs(input, template),
|
|
182
|
+
openapiSourceRefs,
|
|
183
|
+
suiteRunId,
|
|
184
|
+
definitionId: template && template.definitionId,
|
|
185
|
+
btAssistantKey: resolveAssistantKey(testCase),
|
|
186
|
+
knowledgePackRefs
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {Object} testCase
|
|
192
|
+
* @param {string} suiteRunId
|
|
193
|
+
* @param {Object|undefined} template
|
|
194
|
+
* @returns {Object|undefined}
|
|
195
|
+
*/
|
|
196
|
+
function emitBidFromCaseAnswers(testCase, suiteRunId, template) {
|
|
197
|
+
const systemKey = resolveTargetSystemKey(testCase);
|
|
198
|
+
if (!systemKey) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const { bidFromDiscovery } = require('../integration-definition/from-discovery');
|
|
202
|
+
return bidFromDiscovery(buildDiscoveryEmitOptions(testCase, suiteRunId, template, systemKey));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function cachedEmittedBid(testCase, suiteRunId) {
|
|
206
|
+
if (
|
|
207
|
+
testCase._emittedBid &&
|
|
208
|
+
typeof testCase._emittedBid === 'object' &&
|
|
209
|
+
testCase._emittedBid.meta &&
|
|
210
|
+
testCase._emittedBid.meta.suiteRunId === suiteRunId
|
|
211
|
+
) {
|
|
212
|
+
return testCase._emittedBid;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function tryEmitFromAnswers(testCase, suiteRunId, template) {
|
|
218
|
+
if (!shouldEmitBidFromAnswers(testCase)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const emitted = emitBidFromCaseAnswers(testCase, suiteRunId, template);
|
|
223
|
+
if (emitted) {
|
|
224
|
+
testCase._emittedBid = emitted;
|
|
225
|
+
maybePersistEmittedBid(testCase, emitted);
|
|
226
|
+
return emitted;
|
|
227
|
+
}
|
|
228
|
+
} catch (_err) {
|
|
229
|
+
// Fall through to template stamp.
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Load BID for apply: discovery-compiled (preferred) or stamped template.
|
|
236
|
+
* @param {Object} testCase
|
|
237
|
+
* @returns {Object|undefined}
|
|
238
|
+
*/
|
|
239
|
+
function loadCaseBidDocument(testCase) {
|
|
240
|
+
const input =
|
|
241
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
242
|
+
const template = loadBidTemplate(testCase);
|
|
243
|
+
if (!shouldStampSuiteBid(testCase) && input.emitBidFromAnswers !== true) {
|
|
244
|
+
return template;
|
|
245
|
+
}
|
|
246
|
+
const suiteRunId = resolveSuiteRunId(testCase);
|
|
247
|
+
if (!suiteRunId) {
|
|
248
|
+
return template;
|
|
249
|
+
}
|
|
250
|
+
const cached = cachedEmittedBid(testCase, suiteRunId);
|
|
251
|
+
if (cached) {
|
|
252
|
+
return cached;
|
|
253
|
+
}
|
|
254
|
+
const fromAnswers = tryEmitFromAnswers(testCase, suiteRunId, template);
|
|
255
|
+
if (fromAnswers) {
|
|
256
|
+
return fromAnswers;
|
|
257
|
+
}
|
|
258
|
+
if (!template) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const { emitSuiteBid } = require('../integration-definition/emit-suite-bid');
|
|
262
|
+
const emitted = emitSuiteBid(template, {
|
|
263
|
+
suiteRunId,
|
|
264
|
+
btAssistantKey: resolveAssistantKey(testCase),
|
|
265
|
+
answers: testCase.answers
|
|
266
|
+
});
|
|
267
|
+
testCase._emittedBid = emitted;
|
|
268
|
+
maybePersistEmittedBid(testCase, emitted);
|
|
269
|
+
return emitted;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
resolveTargetSystemKey,
|
|
274
|
+
shouldStampSuiteBid,
|
|
275
|
+
resolveSuiteRunId,
|
|
276
|
+
loadCaseBidDocument,
|
|
277
|
+
shouldEmitBidFromAnswers
|
|
278
|
+
};
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const {
|
|
9
|
+
resolveTargetSystemKey,
|
|
10
|
+
shouldStampSuiteBid,
|
|
11
|
+
resolveSuiteRunId,
|
|
12
|
+
loadCaseBidDocument
|
|
13
|
+
} = require('./test-runner-bid-document');
|
|
14
|
+
|
|
8
15
|
function resolveWorkHubAnswerValue(answer) {
|
|
9
16
|
if (answer === null || answer === undefined) {
|
|
10
17
|
return undefined;
|
|
@@ -69,69 +76,286 @@ function extractObjectiveHints(objective) {
|
|
|
69
76
|
return { email, domain, name, address };
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
function
|
|
79
|
+
function resolveCaseSystemKey(testCase) {
|
|
80
|
+
const input =
|
|
81
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
82
|
+
for (const key of ['digitalWorkerKey', 'systemKey', 'businessRoleRef']) {
|
|
83
|
+
const value = input[key];
|
|
84
|
+
if (typeof value === 'string' && value.trim()) {
|
|
85
|
+
return value.trim();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Target Connected System key for workspace/template lifecycle (not the RA package key).
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* Capability identity for a wait. Prefer structured keys; fall back to prompt
|
|
96
|
+
* text so "Missing … for project.runupload (fields: systemKey)" still routes
|
|
97
|
+
* to the target integration key (not the Role Assistant package key).
|
|
98
|
+
* @param {Object} wait
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
function resolveWaitCapabilityText(wait) {
|
|
102
|
+
return String(
|
|
103
|
+
wait?.questionKey ||
|
|
104
|
+
wait?.objectRef?.capabilityKey ||
|
|
105
|
+
wait?.requestKey ||
|
|
106
|
+
wait?.prompt ||
|
|
107
|
+
''
|
|
108
|
+
).toLowerCase();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isTargetIntegrationLifecycleCapability(capability) {
|
|
112
|
+
const text = String(capability || '').toLowerCase();
|
|
113
|
+
return (
|
|
114
|
+
text.includes('workspace') ||
|
|
115
|
+
text.includes('createsystem') ||
|
|
116
|
+
text.includes('template') ||
|
|
117
|
+
text.includes('applyintegrationdefinition') ||
|
|
118
|
+
text.includes('wizard') ||
|
|
119
|
+
text.includes('repair') ||
|
|
120
|
+
text.includes('validate') ||
|
|
121
|
+
text.includes('upload') ||
|
|
122
|
+
text.includes('project.') ||
|
|
123
|
+
text.includes('document.') ||
|
|
124
|
+
text.includes('certifypillar') ||
|
|
125
|
+
text.includes('approval.')
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function resolveCaseCertifyPillar(testCase) {
|
|
130
|
+
const input =
|
|
131
|
+
testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
|
|
132
|
+
const raw = input.certifyPillar || input.pillar;
|
|
133
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
134
|
+
return raw.trim();
|
|
135
|
+
}
|
|
136
|
+
return 'operations';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {Object} wait
|
|
141
|
+
* @param {Object} testCase
|
|
142
|
+
* @param {string} systemKey
|
|
143
|
+
* @returns {Object|undefined}
|
|
144
|
+
*/
|
|
145
|
+
function synthesizeCertifyAnswer(wait, testCase, systemKey) {
|
|
146
|
+
if (!systemKey) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const capability = resolveWaitCapabilityText(wait);
|
|
150
|
+
if (!capability.includes('certifypillar') && !capability.includes('certify')) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return { systemKey, pillar: resolveCaseCertifyPillar(testCase) };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function isBidApplyWait(wait) {
|
|
157
|
+
const capability = resolveWaitCapabilityText(wait);
|
|
158
|
+
return (
|
|
159
|
+
capability.includes('applyintegrationdefinition') ||
|
|
160
|
+
capability.includes('integrationdefinition') ||
|
|
161
|
+
capability.includes('integration-definition')
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function resolveSystemKeyForWait(testCase, wait) {
|
|
166
|
+
const capability = resolveWaitCapabilityText(wait);
|
|
167
|
+
const target = resolveTargetSystemKey(testCase);
|
|
168
|
+
if (target && isTargetIntegrationLifecycleCapability(capability)) {
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
171
|
+
return resolveCaseSystemKey(testCase);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function valueFromHintField(lower, hints, testCase, systemKey) {
|
|
175
|
+
if (hints[lower]) {
|
|
176
|
+
return hints[lower];
|
|
177
|
+
}
|
|
178
|
+
if (lower === 'systemkey' && systemKey) {
|
|
179
|
+
return systemKey;
|
|
180
|
+
}
|
|
181
|
+
if (lower === 'pillar') {
|
|
182
|
+
return resolveCaseCertifyPillar(testCase);
|
|
183
|
+
}
|
|
184
|
+
if (lower === 'force') {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (lower.includes('mail') && hints.email) {
|
|
188
|
+
return hints.email;
|
|
189
|
+
}
|
|
190
|
+
if (lower.includes('name') && hints.name) {
|
|
191
|
+
return hints.name;
|
|
192
|
+
}
|
|
193
|
+
if (lower.includes('domain') && hints.domain) {
|
|
194
|
+
return hints.domain;
|
|
195
|
+
}
|
|
196
|
+
if (lower.includes('address') && hints.address) {
|
|
197
|
+
return hints.address;
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function fillFieldsFromHints(fields, hints, testCase, wait) {
|
|
73
203
|
const out = {};
|
|
204
|
+
const systemKey = resolveSystemKeyForWait(testCase, wait);
|
|
74
205
|
for (const key of fields) {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
out[key] =
|
|
78
|
-
} else if (lower.includes('mail') && hints.email) {
|
|
79
|
-
out[key] = hints.email;
|
|
80
|
-
} else if (lower.includes('name') && hints.name) {
|
|
81
|
-
out[key] = hints.name;
|
|
82
|
-
} else if (lower.includes('domain') && hints.domain) {
|
|
83
|
-
out[key] = hints.domain;
|
|
84
|
-
} else if (lower.includes('address') && hints.address) {
|
|
85
|
-
out[key] = hints.address;
|
|
206
|
+
const value = valueFromHintField(key.toLowerCase(), hints, testCase, systemKey);
|
|
207
|
+
if (value !== undefined) {
|
|
208
|
+
out[key] = value;
|
|
86
209
|
}
|
|
87
210
|
}
|
|
88
211
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
89
212
|
}
|
|
90
213
|
|
|
91
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Soft-loop upload often rewrites BID-shaped schemas over prior packages.
|
|
216
|
+
* Include force so project.runupload can replace non-overlapping attributes.
|
|
217
|
+
* @param {Object} wait
|
|
218
|
+
* @param {string} systemKey
|
|
219
|
+
* @returns {Object|undefined}
|
|
220
|
+
*/
|
|
221
|
+
function synthesizeUploadAnswer(wait, systemKey) {
|
|
222
|
+
if (!systemKey || !isTargetIntegrationLifecycleCapability(resolveWaitCapabilityText(wait))) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
const capability = resolveWaitCapabilityText(wait);
|
|
226
|
+
if (!capability.includes('upload') && !capability.includes('project.runupload')) {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
return { systemKey, force: true };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function _synthContactFields(hints) {
|
|
92
233
|
const out = {};
|
|
234
|
+
if (hints.email) {
|
|
235
|
+
out.email = hints.email;
|
|
236
|
+
}
|
|
237
|
+
if (hints.name || hints.email) {
|
|
238
|
+
out.firstname = String(hints.email || 'user').split('@')[0];
|
|
239
|
+
out.lastname = hints.name || 'Contact';
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function _synthCustomerFields(hints) {
|
|
245
|
+
const out = {};
|
|
246
|
+
if (hints.name) {
|
|
247
|
+
out.name = hints.name;
|
|
248
|
+
}
|
|
249
|
+
if (hints.domain) {
|
|
250
|
+
out.domain = hints.domain;
|
|
251
|
+
}
|
|
252
|
+
if (hints.address) {
|
|
253
|
+
out.address = hints.address;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function _synthDefaultFields(hints) {
|
|
259
|
+
const out = {};
|
|
260
|
+
if (hints.name) {
|
|
261
|
+
out.name = hints.name;
|
|
262
|
+
}
|
|
263
|
+
if (hints.email) {
|
|
264
|
+
out.email = hints.email;
|
|
265
|
+
}
|
|
266
|
+
if (hints.domain) {
|
|
267
|
+
out.domain = hints.domain;
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function synthesizeFromCapability(capability, hints) {
|
|
273
|
+
let out = {};
|
|
93
274
|
if (capability.includes('contact')) {
|
|
94
|
-
|
|
95
|
-
out.email = hints.email;
|
|
96
|
-
}
|
|
97
|
-
if (hints.name || hints.email) {
|
|
98
|
-
out.firstname = String(hints.email || 'user').split('@')[0];
|
|
99
|
-
out.lastname = hints.name || 'Contact';
|
|
100
|
-
}
|
|
275
|
+
out = _synthContactFields(hints);
|
|
101
276
|
} else if (capability.includes('deal')) {
|
|
102
277
|
if (hints.name) {
|
|
103
278
|
out.dealname = hints.name;
|
|
104
279
|
}
|
|
105
280
|
} else if (capability.includes('customer') || capability.includes('compan')) {
|
|
106
|
-
|
|
107
|
-
out.name = hints.name;
|
|
108
|
-
}
|
|
109
|
-
if (hints.domain) {
|
|
110
|
-
out.domain = hints.domain;
|
|
111
|
-
}
|
|
112
|
-
if (hints.address) {
|
|
113
|
-
out.address = hints.address;
|
|
114
|
-
}
|
|
281
|
+
out = _synthCustomerFields(hints);
|
|
115
282
|
} else {
|
|
116
|
-
|
|
117
|
-
out.name = hints.name;
|
|
118
|
-
}
|
|
119
|
-
if (hints.email) {
|
|
120
|
-
out.email = hints.email;
|
|
121
|
-
}
|
|
122
|
-
if (hints.domain) {
|
|
123
|
-
out.domain = hints.domain;
|
|
124
|
-
}
|
|
283
|
+
out = _synthDefaultFields(hints);
|
|
125
284
|
}
|
|
126
285
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
127
286
|
}
|
|
128
287
|
|
|
129
|
-
function
|
|
288
|
+
function synthesizeFieldAnswer(wait, statusRow, objective, testCase) {
|
|
130
289
|
const prompt = String(wait.prompt || statusRow?.message || '').trim();
|
|
131
290
|
const hints = extractObjectiveHints(objective);
|
|
132
291
|
const fields = parseFieldsFromPrompt(prompt);
|
|
133
|
-
|
|
134
|
-
|
|
292
|
+
const filled = fields.length > 0 ? fillFieldsFromHints(fields, hints, testCase, wait) : undefined;
|
|
293
|
+
if (filled === undefined) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const targetKey = filled.systemKey || resolveTargetSystemKey(testCase);
|
|
297
|
+
const uploadExtra = synthesizeUploadAnswer(wait, targetKey);
|
|
298
|
+
const certifyExtra = synthesizeCertifyAnswer(wait, testCase, targetKey);
|
|
299
|
+
return uploadExtra || certifyExtra ? { ...filled, ...(uploadExtra || {}), ...(certifyExtra || {}) } : filled;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function synthesizePathParameterAnswer(wait, prompt, kind, testCase) {
|
|
303
|
+
if (kind !== 'missingPathParams' && !/missing required path params/i.test(prompt)) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
const targetSystemKey = resolveTargetSystemKey(testCase);
|
|
307
|
+
if (targetSystemKey && isTargetIntegrationLifecycleCapability(resolveWaitCapabilityText(wait))) {
|
|
308
|
+
return (
|
|
309
|
+
synthesizeUploadAnswer(wait, targetSystemKey) ||
|
|
310
|
+
synthesizeCertifyAnswer(wait, testCase, targetSystemKey) ||
|
|
311
|
+
{ systemKey: targetSystemKey }
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const pathSystemKey = resolveCaseSystemKey(testCase);
|
|
315
|
+
return pathSystemKey ? { systemKey: pathSystemKey } : undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function synthesizeBidApplyAnswer(wait, prompt, kind, testCase) {
|
|
319
|
+
if (!isBidApplyWait(wait)) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
const wantsBody = kind === 'missingRequestBody' || /missing required request body/i.test(prompt);
|
|
323
|
+
const wantsPath = kind === 'missingPathParams' || /missing required path params/i.test(prompt);
|
|
324
|
+
const bid = loadCaseBidDocument(testCase);
|
|
325
|
+
if (!bid || (!wantsBody && !wantsPath)) {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
const out = wantsBody ? { integrationDefinition: bid } : {};
|
|
329
|
+
const targetSystemKey = resolveTargetSystemKey(testCase);
|
|
330
|
+
if (targetSystemKey) {
|
|
331
|
+
out.systemKey = targetSystemKey;
|
|
332
|
+
}
|
|
333
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase) {
|
|
337
|
+
const targetSystemKey = resolveTargetSystemKey(testCase);
|
|
338
|
+
const needsBody = kind === 'missingRequestBody' || /missing required request body/i.test(prompt);
|
|
339
|
+
if (!targetSystemKey || (!needsBody && !/systemKey is required/i.test(prompt))) {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
const capability = resolveWaitCapabilityText(wait);
|
|
343
|
+
const supportsTarget =
|
|
344
|
+
capability.includes('workspace') ||
|
|
345
|
+
capability.includes('createsystem') ||
|
|
346
|
+
(capability.includes('template') && !isBidApplyWait(wait)) ||
|
|
347
|
+
capability.includes('wizard') ||
|
|
348
|
+
capability.includes('upload') ||
|
|
349
|
+
capability.includes('project.') ||
|
|
350
|
+
/systemkey is required/i.test(prompt);
|
|
351
|
+
return supportsTarget ? { systemKey: targetSystemKey } : undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function synthesizeWaitAnswer(wait, statusRow, objective, testCase) {
|
|
355
|
+
const prompt = String(wait.prompt || statusRow?.message || '').trim();
|
|
356
|
+
const fieldAnswer = synthesizeFieldAnswer(wait, statusRow, objective, testCase);
|
|
357
|
+
if (fieldAnswer !== undefined) {
|
|
358
|
+
return fieldAnswer;
|
|
135
359
|
}
|
|
136
360
|
const kind = String(wait.objectRef?.kind || '').trim();
|
|
137
361
|
const isMissingInput =
|
|
@@ -141,18 +365,29 @@ function synthesizeWaitAnswer(wait, statusRow, objective) {
|
|
|
141
365
|
if (!isMissingInput) {
|
|
142
366
|
return undefined;
|
|
143
367
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
368
|
+
const pathAnswer = synthesizePathParameterAnswer(wait, prompt, kind, testCase);
|
|
369
|
+
if (pathAnswer !== undefined) {
|
|
370
|
+
return pathAnswer;
|
|
371
|
+
}
|
|
372
|
+
const bidAnswer = synthesizeBidApplyAnswer(wait, prompt, kind, testCase);
|
|
373
|
+
if (bidAnswer !== undefined) {
|
|
374
|
+
return bidAnswer;
|
|
375
|
+
}
|
|
376
|
+
const targetAnswer = synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase);
|
|
377
|
+
return targetAnswer || synthesizeFromCapability(resolveWaitCapabilityText(wait), extractObjectiveHints(objective));
|
|
146
378
|
}
|
|
147
379
|
|
|
148
380
|
function resolveRespondValue(testCase, wait, statusRow, objective) {
|
|
149
381
|
const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
|
|
150
|
-
|
|
382
|
+
// Only use an answer whose when.questionKey matches this wait.
|
|
383
|
+
// Never fall back to answers[0] — that poisons capability waits (e.g. task.runtest)
|
|
384
|
+
// with unrelated discovery payloads (system.apiGroups).
|
|
385
|
+
const matched = pickAnswerForWait(answers, wait);
|
|
151
386
|
const fromCase = matched ? resolveWorkHubAnswerValue(matched) : undefined;
|
|
152
387
|
if (fromCase !== undefined) {
|
|
153
388
|
return fromCase;
|
|
154
389
|
}
|
|
155
|
-
return synthesizeWaitAnswer(wait, statusRow, objective);
|
|
390
|
+
return synthesizeWaitAnswer(wait, statusRow, objective, testCase);
|
|
156
391
|
}
|
|
157
392
|
|
|
158
393
|
function waitRespondFingerprint(wait) {
|
|
@@ -163,7 +398,10 @@ function waitRespondFingerprint(wait) {
|
|
|
163
398
|
|
|
164
399
|
function alreadyResponded(interactions, fingerprint) {
|
|
165
400
|
return interactions.some(
|
|
166
|
-
(row) =>
|
|
401
|
+
(row) =>
|
|
402
|
+
row &&
|
|
403
|
+
(row.type === 'respond' || row.type === 'respond-skipped') &&
|
|
404
|
+
row.fingerprint === fingerprint
|
|
167
405
|
);
|
|
168
406
|
}
|
|
169
407
|
|
|
@@ -196,6 +434,11 @@ module.exports = {
|
|
|
196
434
|
pickAnswerForWait,
|
|
197
435
|
synthesizeWaitAnswer,
|
|
198
436
|
extractObjectiveHints,
|
|
437
|
+
resolveCaseSystemKey,
|
|
438
|
+
resolveTargetSystemKey,
|
|
439
|
+
shouldStampSuiteBid,
|
|
440
|
+
resolveSuiteRunId,
|
|
441
|
+
loadCaseBidDocument,
|
|
199
442
|
resolveRespondValue,
|
|
200
443
|
waitRespondFingerprint,
|
|
201
444
|
alreadyResponded,
|