@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
|
@@ -131,19 +131,6 @@ async function fetchDataplaneDeployReadiness(controllerUrl, environment, authCon
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
/**
|
|
135
|
-
* Deploys via controller and prints readiness summary (config / deployment / runtime layers).
|
|
136
|
-
* @async
|
|
137
|
-
* @param {Object} manifest - Controller manifest
|
|
138
|
-
* @param {string} controllerUrl - Controller base URL
|
|
139
|
-
* @param {string} environment - Environment key
|
|
140
|
-
* @param {Object} authConfig - Authentication configuration
|
|
141
|
-
* @param {Object} options - Deployment options (poll, probe, probeTimeout)
|
|
142
|
-
* @returns {Promise<Object>} Deployment result
|
|
143
|
-
*/
|
|
144
|
-
/**
|
|
145
|
-
* @param {Object} deploymentOutcome - from parseControllerDeploymentOutcome
|
|
146
|
-
*/
|
|
147
134
|
/**
|
|
148
135
|
* Optional dataplane validation/run after external deploy (--probe).
|
|
149
136
|
* @async
|
|
@@ -202,12 +189,21 @@ function logImmediateControllerDeploymentOutcome(deploymentOutcome) {
|
|
|
202
189
|
logger.log(chalk.gray(' Check the controller deployment job or logs for details.'));
|
|
203
190
|
}
|
|
204
191
|
|
|
192
|
+
/** External deploy waits on controller → dataplane publish job (may run prove gates / tests). */
|
|
193
|
+
const EXTERNAL_DEPLOY_POLL_INTERVAL_MS = 2000;
|
|
194
|
+
/** 300 × 2s ≈ 10 minutes — must cover controller publish-job max wait with gates. */
|
|
195
|
+
const EXTERNAL_DEPLOY_POLL_MAX_ATTEMPTS = 300;
|
|
196
|
+
|
|
205
197
|
function buildExternalDeployPollOpts(options) {
|
|
206
198
|
return {
|
|
207
199
|
...options,
|
|
208
200
|
poll: true,
|
|
209
|
-
pollInterval:
|
|
210
|
-
|
|
201
|
+
pollInterval:
|
|
202
|
+
options.pollInterval !== undefined ? options.pollInterval : EXTERNAL_DEPLOY_POLL_INTERVAL_MS,
|
|
203
|
+
pollMaxAttempts:
|
|
204
|
+
options.pollMaxAttempts !== undefined
|
|
205
|
+
? options.pollMaxAttempts
|
|
206
|
+
: EXTERNAL_DEPLOY_POLL_MAX_ATTEMPTS,
|
|
211
207
|
silentPoll: options.silentPoll === true
|
|
212
208
|
};
|
|
213
209
|
}
|
|
@@ -381,12 +377,6 @@ function wrapDeployExternalSystemError(error, appName) {
|
|
|
381
377
|
return new Error(message);
|
|
382
378
|
}
|
|
383
379
|
|
|
384
|
-
/**
|
|
385
|
-
* @async
|
|
386
|
-
* @param {string} appName
|
|
387
|
-
* @param {Object} options
|
|
388
|
-
* @returns {Promise<Object>}
|
|
389
|
-
*/
|
|
390
380
|
/**
|
|
391
381
|
* @param {string} appName
|
|
392
382
|
* @param {Object} options
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const fs = require('../internal/fs-real-sync');
|
|
9
|
-
const fsp = require('fs').promises;
|
|
10
8
|
const path = require('path');
|
|
9
|
+
const fsReal = require('../internal/fs-real-sync');
|
|
11
10
|
const { getIntegrationPath } = require('../utils/paths');
|
|
12
11
|
const { validateIntegrationDefinition } = require('./validate');
|
|
13
12
|
|
|
@@ -20,7 +19,7 @@ const STAMP_FILE = '.aifabrix-integration-definition.json';
|
|
|
20
19
|
async function readStamp(dir) {
|
|
21
20
|
const stampPath = path.join(dir, STAMP_FILE);
|
|
22
21
|
try {
|
|
23
|
-
const raw =
|
|
22
|
+
const raw = fsReal.readFileSync(stampPath, 'utf8');
|
|
24
23
|
return JSON.parse(raw);
|
|
25
24
|
} catch (err) {
|
|
26
25
|
if (err && err.code === 'ENOENT') {
|
|
@@ -339,22 +338,22 @@ function buildPackageArtifacts(bid, systemKey, enrichOptions = {}) {
|
|
|
339
338
|
* @returns {Promise<void>}
|
|
340
339
|
*/
|
|
341
340
|
async function writePackageArtifacts(outDir, artifacts) {
|
|
342
|
-
|
|
343
|
-
|
|
341
|
+
fsReal.mkdirSync(outDir, { recursive: true });
|
|
342
|
+
fsReal.writeFileSync(
|
|
344
343
|
path.join(outDir, 'application.json'),
|
|
345
344
|
JSON.stringify(artifacts.application, null, 2),
|
|
346
345
|
'utf8'
|
|
347
346
|
);
|
|
348
|
-
|
|
347
|
+
fsReal.writeFileSync(
|
|
349
348
|
path.join(outDir, artifacts.systemFile),
|
|
350
349
|
JSON.stringify(artifacts.system, null, 2),
|
|
351
350
|
'utf8'
|
|
352
351
|
);
|
|
353
352
|
for (const file of artifacts.dsFiles) {
|
|
354
|
-
|
|
353
|
+
fsReal.writeFileSync(path.join(outDir, file.name), JSON.stringify(file.body, null, 2), 'utf8');
|
|
355
354
|
}
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
fsReal.writeFileSync(path.join(outDir, 'env.template'), artifacts.envTemplate, 'utf8');
|
|
356
|
+
fsReal.writeFileSync(
|
|
358
357
|
path.join(outDir, STAMP_FILE),
|
|
359
358
|
JSON.stringify(artifacts.nextStamp, null, 2),
|
|
360
359
|
'utf8'
|
|
@@ -390,7 +389,7 @@ function idempotentResult(stamp, bid, outDir, systemKey) {
|
|
|
390
389
|
stamp &&
|
|
391
390
|
stamp.definitionId === bid.definitionId &&
|
|
392
391
|
stamp.definitionHash === bid.definitionHash &&
|
|
393
|
-
|
|
392
|
+
fsReal.existsSync(path.join(outDir, 'application.json'))
|
|
394
393
|
)
|
|
395
394
|
) {
|
|
396
395
|
return null;
|
|
@@ -434,11 +433,10 @@ async function applyIntegrationDefinition(bidInput, options = {}) {
|
|
|
434
433
|
return early;
|
|
435
434
|
}
|
|
436
435
|
assertApplyAllowed(bid, stamp);
|
|
437
|
-
// Prefer explicit openapiBaseDir
|
|
438
|
-
//
|
|
439
|
-
const defaultOpenapiBaseDir = path.join(__dirname, 'openapi-enrichment');
|
|
436
|
+
// Prefer explicit openapiBaseDir (workspace integration/<systemKey>/openapi
|
|
437
|
+
// from 517.2 persist). Do not default product apply to ship-local enrichment.
|
|
440
438
|
const artifacts = buildPackageArtifacts(bid, systemKey, {
|
|
441
|
-
baseDir: options.openapiBaseDir || options.baseDir
|
|
439
|
+
baseDir: options.openapiBaseDir || options.baseDir
|
|
442
440
|
});
|
|
443
441
|
const fileList = [
|
|
444
442
|
'application.json',
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const fs = require('../internal/fs-real-sync');
|
|
9
8
|
const path = require('path');
|
|
9
|
+
const fsReal = require('../internal/fs-real-sync');
|
|
10
10
|
const {
|
|
11
11
|
maybeEnrichCrmStyleFieldMappings
|
|
12
12
|
} = require('./enrich-crm-field-mappings');
|
|
@@ -26,22 +26,21 @@ function resolveOpenApiRefPath(ref, baseDir) {
|
|
|
26
26
|
if (path.isAbsolute(trimmed)) {
|
|
27
27
|
return trimmed;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
// Cwd last avoids false misses when Jest mocks fs.existsSync but readFileSync
|
|
32
|
-
// remains real (fallback would otherwise point at <cwd>/<ref>).
|
|
29
|
+
// Product resolve root is workspace openapi/ (517.2) via explicit baseDir.
|
|
30
|
+
// No ship-local vendor enrichment packs under lib/.
|
|
33
31
|
const candidates = [
|
|
34
32
|
baseDir ? path.resolve(baseDir, trimmed) : null,
|
|
35
|
-
path.
|
|
36
|
-
path.join(moduleEnrichmentDir, path.basename(trimmed)),
|
|
33
|
+
baseDir ? path.resolve(baseDir, path.basename(trimmed)) : null,
|
|
37
34
|
path.resolve(process.cwd(), trimmed)
|
|
38
35
|
].filter(Boolean);
|
|
39
36
|
for (const candidate of candidates) {
|
|
40
|
-
if (
|
|
37
|
+
if (fsReal.existsSync(candidate)) {
|
|
41
38
|
return candidate;
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
|
-
|
|
41
|
+
throw new Error(
|
|
42
|
+
`OpenAPI enrichment document not found for ref "${trimmed}". Tried: ${candidates.join(', ')}`
|
|
43
|
+
);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
/**
|
|
@@ -51,7 +50,7 @@ function resolveOpenApiRefPath(ref, baseDir) {
|
|
|
51
50
|
*/
|
|
52
51
|
function loadOpenApiDocument(ref, baseDir) {
|
|
53
52
|
const abs = resolveOpenApiRefPath(ref, baseDir);
|
|
54
|
-
const raw =
|
|
53
|
+
const raw = fsReal.readFileSync(abs, 'utf8');
|
|
55
54
|
const doc = JSON.parse(raw);
|
|
56
55
|
if (!doc || typeof doc !== 'object' || !doc.paths || typeof doc.paths !== 'object') {
|
|
57
56
|
throw new Error(`OpenAPI document missing paths: ${abs}`);
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-level parity score between a reference integration dir and a candidate.
|
|
3
|
+
* Lab/DoD aid only — reference packages are score baselines, never generation input.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Structured parity facets for two integration package directories
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('../internal/fs-real-sync');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
13
|
+
|
|
14
|
+
const DATASOURCE_FILE_RE = /datasource/i;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} dir
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
function listJsonFiles(dir) {
|
|
21
|
+
if (!dir || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
return fs
|
|
25
|
+
.readdirSync(dir)
|
|
26
|
+
.filter((name) => name.endsWith('.json') || name.endsWith('.yaml') || name.endsWith('.yml'))
|
|
27
|
+
.map((name) => path.join(dir, name))
|
|
28
|
+
.sort();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} dir
|
|
33
|
+
* @returns {Object[]}
|
|
34
|
+
*/
|
|
35
|
+
function loadDatasourceManifests(dir) {
|
|
36
|
+
const out = [];
|
|
37
|
+
for (const filePath of listJsonFiles(dir)) {
|
|
38
|
+
const base = path.basename(filePath);
|
|
39
|
+
if (!DATASOURCE_FILE_RE.test(base)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const parsed = loadConfigFile(filePath);
|
|
44
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
45
|
+
out.push({ filePath, base, manifest: parsed });
|
|
46
|
+
}
|
|
47
|
+
} catch (_err) {
|
|
48
|
+
// Skip unreadable files; facet will report incomplete package
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Object} manifest
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
function datasourceKey(manifest) {
|
|
59
|
+
return String(manifest.key || '').trim();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Object} manifest
|
|
64
|
+
* @returns {string[]}
|
|
65
|
+
*/
|
|
66
|
+
function capabilityKeys(manifest) {
|
|
67
|
+
const caps = manifest.capabilities;
|
|
68
|
+
if (!Array.isArray(caps)) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
return caps
|
|
72
|
+
.map((row) => (row && typeof row === 'object' ? String(row.key || '').trim() : ''))
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.sort();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} dir
|
|
79
|
+
* @returns {string[]}
|
|
80
|
+
*/
|
|
81
|
+
function openapiBasenames(dir) {
|
|
82
|
+
const openapiDir = path.join(dir, 'openapi');
|
|
83
|
+
if (!fs.existsSync(openapiDir) || !fs.statSync(openapiDir).isDirectory()) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return fs
|
|
87
|
+
.readdirSync(openapiDir)
|
|
88
|
+
.filter((name) => /\.(json|yaml|yml)$/i.test(name))
|
|
89
|
+
.map((name) => name.toLowerCase())
|
|
90
|
+
.sort();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {Object} manifest
|
|
95
|
+
* @returns {{ metadataSchema: boolean, fieldMappings: boolean, foreignKeys: boolean, exposed: boolean, cip: boolean }}
|
|
96
|
+
*/
|
|
97
|
+
function contractSurfaceFlags(manifest) {
|
|
98
|
+
const execution = manifest.execution && typeof manifest.execution === 'object' ? manifest.execution : {};
|
|
99
|
+
const cip = execution.cip && typeof execution.cip === 'object' ? execution.cip : null;
|
|
100
|
+
return {
|
|
101
|
+
metadataSchema: Boolean(manifest.metadataSchema),
|
|
102
|
+
fieldMappings: Boolean(manifest.fieldMappings),
|
|
103
|
+
foreignKeys: Boolean(manifest.foreignKeys),
|
|
104
|
+
exposed: Boolean(manifest.exposed),
|
|
105
|
+
cip: Boolean(cip && cip.operations)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {boolean} ok
|
|
111
|
+
* @param {string} id
|
|
112
|
+
* @param {string} detail
|
|
113
|
+
* @param {Object} [extra]
|
|
114
|
+
* @returns {Object}
|
|
115
|
+
*/
|
|
116
|
+
function facet(ok, id, detail, extra = {}) {
|
|
117
|
+
return { id, ok: Boolean(ok), detail, ...extra };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {string} absDir
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
function isPackageDir(absDir) {
|
|
125
|
+
return fs.existsSync(absDir) && fs.statSync(absDir).isDirectory();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {Object[]} rows
|
|
130
|
+
* @returns {Map<string, Object>}
|
|
131
|
+
*/
|
|
132
|
+
function indexDatasourcesByKey(rows) {
|
|
133
|
+
const byKey = new Map();
|
|
134
|
+
for (const row of rows) {
|
|
135
|
+
const key = datasourceKey(row.manifest);
|
|
136
|
+
if (key) {
|
|
137
|
+
byKey.set(key, row);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return byKey;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} refAbs
|
|
145
|
+
* @param {string} candAbs
|
|
146
|
+
* @param {Object[]} facets
|
|
147
|
+
*/
|
|
148
|
+
function pushPresenceFacets(refAbs, candAbs, facets) {
|
|
149
|
+
const refExists = isPackageDir(refAbs);
|
|
150
|
+
const candExists = isPackageDir(candAbs);
|
|
151
|
+
facets.push(
|
|
152
|
+
facet(refExists, 'referencePackagePresent', refExists ? 'Reference package directory found' : 'Reference package directory missing')
|
|
153
|
+
);
|
|
154
|
+
facets.push(
|
|
155
|
+
facet(candExists, 'candidatePackagePresent', candExists ? 'Candidate package directory found' : 'Candidate package directory missing')
|
|
156
|
+
);
|
|
157
|
+
return { refExists, candExists };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {string} refAbs
|
|
162
|
+
* @param {string} candAbs
|
|
163
|
+
* @param {Object[]} facets
|
|
164
|
+
*/
|
|
165
|
+
function pushSystemManifestFacet(refAbs, candAbs, facets) {
|
|
166
|
+
const refHasSystem = listJsonFiles(refAbs).some((f) => /system/i.test(path.basename(f)));
|
|
167
|
+
const candHasSystem = listJsonFiles(candAbs).some((f) => /system/i.test(path.basename(f)));
|
|
168
|
+
facets.push(
|
|
169
|
+
facet(
|
|
170
|
+
!refHasSystem || candHasSystem,
|
|
171
|
+
'systemManifest',
|
|
172
|
+
candHasSystem ? 'Candidate has a system manifest file' : 'Candidate missing system manifest while reference has one'
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {Map<string, Object>} refByKey
|
|
179
|
+
* @param {Map<string, Object>} candByKey
|
|
180
|
+
* @param {Object[]} facets
|
|
181
|
+
* @returns {string[]}
|
|
182
|
+
*/
|
|
183
|
+
function pushDatasourceCoverageFacet(refByKey, candByKey, facets) {
|
|
184
|
+
const missingDatasourceKeys = [...refByKey.keys()].filter((key) => !candByKey.has(key)).sort();
|
|
185
|
+
facets.push(
|
|
186
|
+
facet(
|
|
187
|
+
missingDatasourceKeys.length === 0 && refByKey.size > 0,
|
|
188
|
+
'datasourceKeyCoverage',
|
|
189
|
+
missingDatasourceKeys.length === 0
|
|
190
|
+
? `Candidate covers ${refByKey.size} reference datasource key(s)`
|
|
191
|
+
: `Missing datasource keys: ${missingDatasourceKeys.join(', ')}`,
|
|
192
|
+
{ missingDatasourceKeys, referenceCount: refByKey.size, candidateCount: candByKey.size }
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
return missingDatasourceKeys;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {string} refAbs
|
|
200
|
+
* @param {string} candAbs
|
|
201
|
+
* @param {Object[]} facets
|
|
202
|
+
*/
|
|
203
|
+
function pushOpenapiFacet(refAbs, candAbs, facets) {
|
|
204
|
+
const refOpenapi = openapiBasenames(refAbs);
|
|
205
|
+
const candOpenapi = new Set(openapiBasenames(candAbs));
|
|
206
|
+
const missingOpenapi = refOpenapi.filter((name) => !candOpenapi.has(name));
|
|
207
|
+
facets.push(
|
|
208
|
+
facet(
|
|
209
|
+
refOpenapi.length === 0 || missingOpenapi.length === 0,
|
|
210
|
+
'openapiDir',
|
|
211
|
+
refOpenapi.length === 0
|
|
212
|
+
? 'Reference has no openapi/ files — facet skipped'
|
|
213
|
+
: missingOpenapi.length === 0
|
|
214
|
+
? `Candidate has ${refOpenapi.length} matching openapi basename(s)`
|
|
215
|
+
: `Missing openapi files: ${missingOpenapi.join(', ')}`,
|
|
216
|
+
{ missingOpenapi }
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @param {string} key
|
|
223
|
+
* @param {Object} refManifest
|
|
224
|
+
* @param {Object} candManifest
|
|
225
|
+
* @param {Object[]} capabilityGaps
|
|
226
|
+
* @returns {boolean}
|
|
227
|
+
*/
|
|
228
|
+
function collectCapabilityGaps(key, refManifest, candManifest, capabilityGaps) {
|
|
229
|
+
const refCaps = new Set(capabilityKeys(refManifest));
|
|
230
|
+
const candCaps = new Set(capabilityKeys(candManifest));
|
|
231
|
+
const missingCaps = [...refCaps].filter((c) => !candCaps.has(c)).sort();
|
|
232
|
+
if (!missingCaps.length) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
capabilityGaps.push({ datasourceKey: key, missingCapabilityKeys: missingCaps });
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {string} key
|
|
241
|
+
* @param {Object} refManifest
|
|
242
|
+
* @param {Object} candManifest
|
|
243
|
+
* @param {Object[]} resourceTypeGaps
|
|
244
|
+
* @returns {boolean}
|
|
245
|
+
*/
|
|
246
|
+
function collectResourceTypeGaps(key, refManifest, candManifest, resourceTypeGaps) {
|
|
247
|
+
const refRt = String(refManifest.resourceType || '').trim();
|
|
248
|
+
const candRt = String(candManifest.resourceType || '').trim();
|
|
249
|
+
if (!(refRt && candRt && refRt !== candRt)) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
resourceTypeGaps.push({ datasourceKey: key, reference: refRt, candidate: candRt });
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {string} key
|
|
258
|
+
* @param {Object} refManifest
|
|
259
|
+
* @param {Object} candManifest
|
|
260
|
+
* @param {Object[]} surfaceGaps
|
|
261
|
+
* @returns {boolean}
|
|
262
|
+
*/
|
|
263
|
+
function collectSurfaceGaps(key, refManifest, candManifest, surfaceGaps) {
|
|
264
|
+
const refFlags = contractSurfaceFlags(refManifest);
|
|
265
|
+
const candFlags = contractSurfaceFlags(candManifest);
|
|
266
|
+
let ok = true;
|
|
267
|
+
for (const [surface, needed] of Object.entries(refFlags)) {
|
|
268
|
+
if (needed && !candFlags[surface]) {
|
|
269
|
+
ok = false;
|
|
270
|
+
surfaceGaps.push({ datasourceKey: key, surface });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return ok;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param {Map<string, Object>} refByKey
|
|
278
|
+
* @param {Map<string, Object>} candByKey
|
|
279
|
+
* @returns {{ capabilityOk: boolean, capabilityGaps: Object[], resourceTypeOk: boolean, resourceTypeGaps: Object[], surfacesOk: boolean, surfaceGaps: Object[] }}
|
|
280
|
+
*/
|
|
281
|
+
function compareSharedDatasources(refByKey, candByKey) {
|
|
282
|
+
const capabilityGaps = [];
|
|
283
|
+
const resourceTypeGaps = [];
|
|
284
|
+
const surfaceGaps = [];
|
|
285
|
+
let capabilityOk = true;
|
|
286
|
+
let resourceTypeOk = true;
|
|
287
|
+
let surfacesOk = true;
|
|
288
|
+
|
|
289
|
+
for (const [key, refRow] of refByKey.entries()) {
|
|
290
|
+
const candRow = candByKey.get(key);
|
|
291
|
+
if (!candRow) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (!collectCapabilityGaps(key, refRow.manifest, candRow.manifest, capabilityGaps)) {
|
|
295
|
+
capabilityOk = false;
|
|
296
|
+
}
|
|
297
|
+
if (!collectResourceTypeGaps(key, refRow.manifest, candRow.manifest, resourceTypeGaps)) {
|
|
298
|
+
resourceTypeOk = false;
|
|
299
|
+
}
|
|
300
|
+
if (!collectSurfaceGaps(key, refRow.manifest, candRow.manifest, surfaceGaps)) {
|
|
301
|
+
surfacesOk = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { capabilityOk, capabilityGaps, resourceTypeOk, resourceTypeGaps, surfacesOk, surfaceGaps };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* @param {ReturnType<typeof compareSharedDatasources>} compared
|
|
310
|
+
* @param {Object[]} facets
|
|
311
|
+
*/
|
|
312
|
+
function pushSharedDatasourceFacets(compared, facets) {
|
|
313
|
+
facets.push(
|
|
314
|
+
facet(
|
|
315
|
+
compared.capabilityOk,
|
|
316
|
+
'capabilityKeys',
|
|
317
|
+
compared.capabilityOk ? 'Capability keys cover reference on shared datasources' : 'Missing capability keys on shared datasources',
|
|
318
|
+
{ gaps: compared.capabilityGaps }
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
facets.push(
|
|
322
|
+
facet(
|
|
323
|
+
compared.resourceTypeOk,
|
|
324
|
+
'resourceTypes',
|
|
325
|
+
compared.resourceTypeOk ? 'resourceType matches on shared datasources' : 'resourceType mismatches on shared datasources',
|
|
326
|
+
{ gaps: compared.resourceTypeGaps }
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
facets.push(
|
|
330
|
+
facet(
|
|
331
|
+
compared.surfacesOk,
|
|
332
|
+
'contractSurfaces',
|
|
333
|
+
compared.surfacesOk ? 'Contract surfaces present where reference declares them' : 'Missing contract surfaces vs reference',
|
|
334
|
+
{ gaps: compared.surfaceGaps }
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @param {Object[]} facets
|
|
341
|
+
* @param {string[]} missingDatasourceKeys
|
|
342
|
+
* @param {string} refAbs
|
|
343
|
+
* @param {string} candAbs
|
|
344
|
+
* @returns {Object}
|
|
345
|
+
*/
|
|
346
|
+
function buildParityReport(facets, missingDatasourceKeys, refAbs, candAbs) {
|
|
347
|
+
const scored = facets.filter((f) => f.id !== 'referencePackagePresent');
|
|
348
|
+
const passed = scored.filter((f) => f.ok).length;
|
|
349
|
+
const score = scored.length === 0 ? 0 : Math.round((passed / scored.length) * 100);
|
|
350
|
+
const identical = facets.every((f) => f.ok) && missingDatasourceKeys.length === 0;
|
|
351
|
+
return {
|
|
352
|
+
score,
|
|
353
|
+
maxScore: 100,
|
|
354
|
+
identical,
|
|
355
|
+
referenceDir: refAbs,
|
|
356
|
+
candidateDir: candAbs,
|
|
357
|
+
goldIsScoreOnly: true,
|
|
358
|
+
facets,
|
|
359
|
+
missingDatasourceKeys,
|
|
360
|
+
summary: identical
|
|
361
|
+
? 'Candidate matches reference facets (score baseline only — not generation input)'
|
|
362
|
+
: `Parity score ${score}/100 — reference is score-only, never generation input`
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Score candidate integration package directory against a reference package directory.
|
|
368
|
+
*
|
|
369
|
+
* @param {string} referenceDir Absolute or relative path to reference package (score baseline only)
|
|
370
|
+
* @param {string} candidateDir Absolute or relative path to candidate package
|
|
371
|
+
* @returns {{
|
|
372
|
+
* score: number,
|
|
373
|
+
* maxScore: number,
|
|
374
|
+
* identical: boolean,
|
|
375
|
+
* referenceDir: string,
|
|
376
|
+
* candidateDir: string,
|
|
377
|
+
* goldIsScoreOnly: true,
|
|
378
|
+
* facets: Object[],
|
|
379
|
+
* missingDatasourceKeys: string[],
|
|
380
|
+
* summary: string
|
|
381
|
+
* }}
|
|
382
|
+
*/
|
|
383
|
+
function scorePackageParity(referenceDir, candidateDir) {
|
|
384
|
+
const refAbs = path.resolve(referenceDir);
|
|
385
|
+
const candAbs = path.resolve(candidateDir);
|
|
386
|
+
const facets = [];
|
|
387
|
+
const { refExists, candExists } = pushPresenceFacets(refAbs, candAbs, facets);
|
|
388
|
+
|
|
389
|
+
if (!refExists || !candExists) {
|
|
390
|
+
return {
|
|
391
|
+
score: 0,
|
|
392
|
+
maxScore: 100,
|
|
393
|
+
identical: false,
|
|
394
|
+
referenceDir: refAbs,
|
|
395
|
+
candidateDir: candAbs,
|
|
396
|
+
goldIsScoreOnly: true,
|
|
397
|
+
facets,
|
|
398
|
+
missingDatasourceKeys: [],
|
|
399
|
+
summary: 'Package directories incomplete — score 0 (no silent 100%)'
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
pushSystemManifestFacet(refAbs, candAbs, facets);
|
|
404
|
+
const refByKey = indexDatasourcesByKey(loadDatasourceManifests(refAbs));
|
|
405
|
+
const candByKey = indexDatasourcesByKey(loadDatasourceManifests(candAbs));
|
|
406
|
+
const missingDatasourceKeys = pushDatasourceCoverageFacet(refByKey, candByKey, facets);
|
|
407
|
+
pushOpenapiFacet(refAbs, candAbs, facets);
|
|
408
|
+
pushSharedDatasourceFacets(compareSharedDatasources(refByKey, candByKey), facets);
|
|
409
|
+
return buildParityReport(facets, missingDatasourceKeys, refAbs, candAbs);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
module.exports = {
|
|
413
|
+
scorePackageParity,
|
|
414
|
+
loadDatasourceManifests,
|
|
415
|
+
openapiBasenames,
|
|
416
|
+
contractSurfaceFlags
|
|
417
|
+
};
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
const CAPABILITIES = Object.freeze([
|
|
13
13
|
{ method: 'GET', route: '/workspaces/me/systems', handlerId: 'listSystems', operationType: 'workspace.read', mutating: false },
|
|
14
14
|
{ method: 'POST', route: '/workspaces/me/systems', handlerId: 'createSystem', operationType: 'workspace.write', mutating: true },
|
|
15
|
+
{ method: 'POST', route: '/systems/:systemKey/openapi/persist-supplied', handlerId: 'persistSuppliedOpenApi', operationType: 'workspace.write', mutating: true },
|
|
15
16
|
{ method: 'GET', route: '/workspaces/me/systems/:systemKey', handlerId: 'getSystemManifest', operationType: 'workspace.read', mutating: false },
|
|
16
17
|
{ method: 'DELETE', route: '/workspaces/me/systems/:systemKey', handlerId: 'deleteSystem', operationType: 'workspace.write', mutating: true },
|
|
17
18
|
{ method: 'POST', route: '/systems/:systemKey/wizard', handlerId: 'runWizard', operationType: 'plan', mutating: true },
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Channel onboarding from your work list
|
|
2
|
+
|
|
3
|
+
Onboard an assistant channel after your external system is published from a Builder workspace.
|
|
4
|
+
|
|
5
|
+
## Work list
|
|
6
|
+
|
|
7
|
+
Call GET workspaces/me/systems to list integration keys in your authenticated workspace. Each entry is a draft or published external system you are building.
|
|
8
|
+
|
|
9
|
+
## Publish before channel
|
|
10
|
+
|
|
11
|
+
Complete validate and upload for the target systemKey so the dataplane exposes MCP tools and connected-system UI. Use repair with bearer-forward auth for facade APIs. Apply platform RBAC when the integration is a platform orchestration API.
|
|
12
|
+
|
|
13
|
+
## Choose a channel target
|
|
14
|
+
|
|
15
|
+
| Target | Use when |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| teams-bot | Microsoft Teams employee assistant |
|
|
18
|
+
| slack | Slack workspace assistant |
|
|
19
|
+
| microsoft-copilot | Microsoft Copilot package (runtime-orchestration MCP) |
|
|
20
|
+
| chatgpt | ChatGPT custom MCP app (runtime-orchestration) |
|
|
21
|
+
| web-embed | Experimental embedded web assistant |
|
|
22
|
+
|
|
23
|
+
Each target requires credentials and deploy approval. Read topic connectedSystemUi after upload for dataplane handoff paths.
|
|
24
|
+
|
|
25
|
+
## Recommended sequence
|
|
26
|
+
|
|
27
|
+
1. GET help topic overview and platformRoles when scoping RBAC.
|
|
28
|
+
2. GET workspaces/me/systems — pick or create systemKey.
|
|
29
|
+
3. Run wizard, repair, validate, upload via Builder API.
|
|
30
|
+
4. Scaffold channel integration with the CLI channel scaffold command for the chosen target.
|
|
31
|
+
5. Register the channel on the deployed application with channel add.
|
|
32
|
+
|
|
33
|
+
## Governance before go-live
|
|
34
|
+
|
|
35
|
+
Confirm resourceType catalog keys match business entities. Add dimensions on record and document datasources before protection. Scope system.subscriptions to business resource types, not generic placeholders.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# CIP execution overview
|
|
2
|
+
|
|
3
|
+
Composable Integration Pipeline is the declarative execution engine for datasource operations. Most external datasources use engine cip under the datasource.execution section.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
Each operation under execution.cip.operations is a named map entry such as list, get, create, update, or delete. Custom operation names are allowed when they match capability keys.
|
|
8
|
+
|
|
9
|
+
Every operation contains an ordered steps array. Each step is a single keyed object: fetch, paginate, map, filter, output, or pythonInline.
|
|
10
|
+
|
|
11
|
+
## Common step patterns
|
|
12
|
+
|
|
13
|
+
Fetch resolves data from OpenAPI references, raw HTTP, or another datasource. Map applies fieldMappings to normalize vendor payloads. Filter applies optional ABAC or JMESPath expressions. Output emits records for sync and storage.
|
|
14
|
+
|
|
15
|
+
Paginate handles cursor, page, or offset strategies for list operations. Python inline runs sandboxed transforms when declarative steps are insufficient.
|
|
16
|
+
|
|
17
|
+
## Quality practices
|
|
18
|
+
|
|
19
|
+
Choose entityType and resourceType before authoring execution steps. Align openapi.operations keys with capabilities and fetch openapiRef values.
|
|
20
|
+
|
|
21
|
+
After changing metadata or capabilities, run repair hooks for fieldMappings, expose, and rbac before validate and test.
|
|
22
|
+
|
|
23
|
+
Use validate responses as the authoritative guide when step properties fail schema checks. Request topic schemaCatalog for property indexes, then topic section for governed fragments.
|
|
24
|
+
|
|
25
|
+
## Related sections
|
|
26
|
+
|
|
27
|
+
datasource.execution, datasource.capabilities, datasource.fieldMappings, datasource.openapiOperations, datasource.metadata
|