@aifabrix/builder 2.49.0 → 2.49.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/process/README.md +9 -0
- package/.cursor/skills/README.md +14 -0
- package/lib/api/dev.api.js +1 -0
- package/lib/api/host-ops.api.js +69 -0
- package/lib/cli/infra-guided-auth.js +8 -6
- package/lib/cli/infra-guided.js +11 -2
- package/lib/cli/setup-dev-path-commands.js +24 -0
- package/lib/cli/setup-dev.js +108 -14
- package/lib/commands/dev-cli-handlers.js +64 -3
- package/lib/commands/dev-host-ops.js +239 -0
- package/lib/commands/teardown.js +3 -5
- package/lib/commands/up-common.js +8 -11
- package/lib/commands/up-dataplane.js +81 -13
- package/lib/commands/up-miso.js +6 -2
- package/lib/schema/external-system.schema.json +114 -2
- package/lib/utils/health-check-poll-ui.js +63 -28
- package/lib/utils/health-check.js +50 -21
- package/lib/utils/paths.js +26 -36
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +2 -1
- package/templates/applications/miso-controller/env.template +2 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +0 -1
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +0 -1
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +0 -1
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +0 -1
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +0 -17
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +0 -17
- package/.temp/validation/summary.txt +0 -5
|
@@ -26,36 +26,50 @@ function buildHealthCheckPollText(attemptIndex, maxAttempts) {
|
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
29
|
+
function createSilentHealthCheckPollHandlers() {
|
|
30
|
+
return {
|
|
31
|
+
usesSpinner: false,
|
|
32
|
+
onWaiting: () => {},
|
|
33
|
+
finish: () => {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createDelegatedHealthCheckPollHandlers(maxAttempts, onPollText) {
|
|
38
|
+
return {
|
|
39
|
+
usesSpinner: false,
|
|
40
|
+
onWaiting: (attemptIndex) => {
|
|
41
|
+
onPollText(buildHealthCheckPollText(attemptIndex, maxAttempts));
|
|
42
|
+
},
|
|
43
|
+
finish: () => {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createOraHealthCheckPollHandlers(maxAttempts) {
|
|
48
|
+
const ora = require('ora');
|
|
49
|
+
const spinner = ora({
|
|
50
|
+
text: buildHealthCheckPollText(0, maxAttempts),
|
|
51
|
+
spinner: 'dots'
|
|
52
|
+
});
|
|
53
|
+
let started = false;
|
|
54
|
+
return {
|
|
55
|
+
usesSpinner: true,
|
|
56
|
+
onWaiting: (attemptIndex) => {
|
|
57
|
+
if (!started) {
|
|
58
|
+
spinner.start();
|
|
59
|
+
started = true;
|
|
55
60
|
}
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
spinner.text = buildHealthCheckPollText(attemptIndex, maxAttempts);
|
|
62
|
+
},
|
|
63
|
+
finish: () => {
|
|
64
|
+
if (started) {
|
|
65
|
+
spinner.stop();
|
|
66
|
+
process.stdout.write('\n');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
58
71
|
|
|
72
|
+
function createCarriageReturnHealthCheckPollHandlers(maxAttempts) {
|
|
59
73
|
let lastLine = '';
|
|
60
74
|
return {
|
|
61
75
|
usesSpinner: false,
|
|
@@ -77,6 +91,27 @@ function createHealthCheckPollHandlers(maxAttempts) {
|
|
|
77
91
|
};
|
|
78
92
|
}
|
|
79
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @param {number} maxAttempts
|
|
96
|
+
* @param {{ silent?: boolean, onPollText?: (text: string) => void }} [options]
|
|
97
|
+
* @returns {{ usesSpinner: boolean, onWaiting: (attemptIndex: number) => void, finish: () => void }}
|
|
98
|
+
*/
|
|
99
|
+
function createHealthCheckPollHandlers(maxAttempts, options = {}) {
|
|
100
|
+
const silent = options && options.silent === true;
|
|
101
|
+
const onPollText = options && typeof options.onPollText === 'function' ? options.onPollText : null;
|
|
102
|
+
|
|
103
|
+
if (silent) {
|
|
104
|
+
return createSilentHealthCheckPollHandlers();
|
|
105
|
+
}
|
|
106
|
+
if (onPollText) {
|
|
107
|
+
return createDelegatedHealthCheckPollHandlers(maxAttempts, onPollText);
|
|
108
|
+
}
|
|
109
|
+
if (shouldUseHealthCheckPollSpinner()) {
|
|
110
|
+
return createOraHealthCheckPollHandlers(maxAttempts);
|
|
111
|
+
}
|
|
112
|
+
return createCarriageReturnHealthCheckPollHandlers(maxAttempts);
|
|
113
|
+
}
|
|
114
|
+
|
|
80
115
|
module.exports = {
|
|
81
116
|
buildHealthCheckPollText,
|
|
82
117
|
createHealthCheckPollHandlers,
|
|
@@ -331,29 +331,26 @@ async function performHealthCheckAttemptForUrls(
|
|
|
331
331
|
return { ok: false, resolvedIndex: -1 };
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
healthCheckPort,
|
|
342
|
-
config,
|
|
343
|
-
runOptions,
|
|
344
|
-
debug
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
if (skippedPublicHealthUrl && urlsToTry.length === 1 && debug) {
|
|
348
|
-
logger.log(
|
|
349
|
-
chalk.gray(
|
|
350
|
-
`ℹ Health check: public URL not used (DNS): ${skippedPublicHealthUrl}. ` +
|
|
351
|
-
`Probing ${urlsToTry[0]} only until the app responds.`
|
|
352
|
-
)
|
|
353
|
-
);
|
|
334
|
+
function resolveHealthCheckPollOptions(runOptions = {}) {
|
|
335
|
+
const pollOptions = {};
|
|
336
|
+
if (runOptions.silentPoll === true) {
|
|
337
|
+
pollOptions.silent = true;
|
|
338
|
+
}
|
|
339
|
+
if (typeof runOptions.onPollText === 'function') {
|
|
340
|
+
pollOptions.onPollText = runOptions.onPollText;
|
|
354
341
|
}
|
|
342
|
+
return pollOptions;
|
|
343
|
+
}
|
|
355
344
|
|
|
356
|
-
|
|
345
|
+
async function pollUntilHealthCheckPasses({
|
|
346
|
+
urlsToTry,
|
|
347
|
+
maxAttempts,
|
|
348
|
+
timeout,
|
|
349
|
+
debug,
|
|
350
|
+
skippedPublicHealthUrl,
|
|
351
|
+
pollOptions = {}
|
|
352
|
+
}) {
|
|
353
|
+
const poll = createHealthCheckPollHandlers(maxAttempts, pollOptions);
|
|
357
354
|
const pollAttemptOpts = { suppressSuccessLog: true };
|
|
358
355
|
|
|
359
356
|
for (let attempts = 0; attempts < maxAttempts; attempts++) {
|
|
@@ -389,6 +386,38 @@ async function waitForHealthCheck(appName, timeout = 90, port = null, config = n
|
|
|
389
386
|
throw new Error(`Health check timeout after ${timeout} seconds`);
|
|
390
387
|
}
|
|
391
388
|
|
|
389
|
+
async function waitForHealthCheck(appName, timeout = 90, port = null, config = null, debug = false, runOptions = {}) {
|
|
390
|
+
await waitForDbInit(appName);
|
|
391
|
+
|
|
392
|
+
const healthCheckPort = await determineHealthCheckPort(port, appName, debug);
|
|
393
|
+
const { maxAttempts } = buildHealthCheckConfig(healthCheckPort, config, timeout, debug);
|
|
394
|
+
const { urlsToTry, skippedPublicHealthUrl } = await computePreferredHealthCheckUrls(
|
|
395
|
+
appName,
|
|
396
|
+
healthCheckPort,
|
|
397
|
+
config,
|
|
398
|
+
runOptions,
|
|
399
|
+
debug
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
if (skippedPublicHealthUrl && urlsToTry.length === 1 && debug) {
|
|
403
|
+
logger.log(
|
|
404
|
+
chalk.gray(
|
|
405
|
+
`ℹ Health check: public URL not used (DNS): ${skippedPublicHealthUrl}. ` +
|
|
406
|
+
`Probing ${urlsToTry[0]} only until the app responds.`
|
|
407
|
+
)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
await pollUntilHealthCheckPasses({
|
|
412
|
+
urlsToTry,
|
|
413
|
+
maxAttempts,
|
|
414
|
+
timeout,
|
|
415
|
+
debug,
|
|
416
|
+
skippedPublicHealthUrl,
|
|
417
|
+
pollOptions: resolveHealthCheckPollOptions(runOptions)
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
392
421
|
/**
|
|
393
422
|
* Checks if port is available
|
|
394
423
|
* @param {number} port - Port number to check
|
package/lib/utils/paths.js
CHANGED
|
@@ -459,13 +459,23 @@ function isSystemBuilderAppName(appName) {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
/**
|
|
462
|
-
*
|
|
462
|
+
* Home / work materialization path: `(aifabrix-work | aifabrix-home)/builder/<appName>`.
|
|
463
|
+
* Used by setup, teardown, and template copy — never the product-repo `cwd/builder` tree.
|
|
463
464
|
*
|
|
464
465
|
* @param {string} appName
|
|
465
466
|
* @returns {string}
|
|
466
467
|
*/
|
|
468
|
+
function getHomeBuilderAppPath(appName) {
|
|
469
|
+
return path.join(getAppsMaterializationParent(), 'builder', appName);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* @deprecated Prefer {@link getHomeBuilderAppPath}; same path.
|
|
474
|
+
* @param {string} appName
|
|
475
|
+
* @returns {string}
|
|
476
|
+
*/
|
|
467
477
|
function getProjectBuilderAppPath(appName) {
|
|
468
|
-
return
|
|
478
|
+
return getHomeBuilderAppPath(appName);
|
|
469
479
|
}
|
|
470
480
|
|
|
471
481
|
/**
|
|
@@ -630,38 +640,25 @@ function resolveBuildContext(configDir, buildContext) {
|
|
|
630
640
|
}
|
|
631
641
|
|
|
632
642
|
/**
|
|
633
|
-
* `cwd/builder/<appName>` when
|
|
643
|
+
* `cwd/builder/<appName>` when the running folder has a resolvable application manifest.
|
|
634
644
|
*
|
|
635
645
|
* @param {string} appName
|
|
636
646
|
* @returns {string|null}
|
|
637
647
|
*/
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
try {
|
|
641
|
-
if (!nodeFs().existsSync(cwdApp)) {
|
|
642
|
-
return null;
|
|
643
|
-
}
|
|
644
|
-
const st = nodeFs().statSync(cwdApp);
|
|
645
|
-
if (!st || typeof st.isDirectory !== 'function' || !st.isDirectory()) {
|
|
646
|
-
return null;
|
|
647
|
-
}
|
|
648
|
-
if (isSystemBuilderAppName(appName) && !isProjectBuilderAppDirectory(cwdApp)) {
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
return cwdApp;
|
|
652
|
-
} catch {
|
|
648
|
+
function getCwdBuilderAppPath(appName) {
|
|
649
|
+
if (!appName || typeof appName !== 'string') {
|
|
653
650
|
return null;
|
|
654
651
|
}
|
|
652
|
+
const cwdApp = path.join(path.resolve(process.cwd()), 'builder', appName);
|
|
653
|
+
return isProjectBuilderAppDirectory(cwdApp) ? cwdApp : null;
|
|
655
654
|
}
|
|
656
655
|
|
|
657
656
|
/**
|
|
658
|
-
* Gets the builder folder path:
|
|
659
|
-
* 1.
|
|
660
|
-
* 2. Else **`
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
* 3. Else **`(aifabrix-work or aifabrix-home)/builder/<appName>`** — used by setup / `up-platform` /
|
|
664
|
-
* `up-miso` / `up-dataplane` for template materialization.
|
|
657
|
+
* Gets the builder folder path for run/deploy/register:
|
|
658
|
+
* 1. **`cwd/builder/<app>`** when a resolvable application manifest exists in the running folder.
|
|
659
|
+
* 2. Else **`(aifabrix-work | aifabrix-home)/builder/<app>`** (setup materializes templates there).
|
|
660
|
+
*
|
|
661
|
+
* Teardown and setup `--force` clean use {@link getHomeBuilderAppPath} only (never product-repo trees).
|
|
665
662
|
*
|
|
666
663
|
* @param {string} appName - Application name
|
|
667
664
|
* @returns {string} Absolute path to builder directory
|
|
@@ -670,20 +667,11 @@ function getBuilderPath(appName) {
|
|
|
670
667
|
if (!appName || typeof appName !== 'string') {
|
|
671
668
|
throw new Error('App name is required and must be a string');
|
|
672
669
|
}
|
|
673
|
-
const
|
|
674
|
-
const manifestHit = resolveApplicationManifestPathSync({
|
|
675
|
-
targetKey: appName,
|
|
676
|
-
mode: 'auto',
|
|
677
|
-
cwd: process.cwd()
|
|
678
|
-
});
|
|
679
|
-
if (manifestHit && (manifestHit.tier === 'cwd-builder' || manifestHit.tier === 'cwd-integration')) {
|
|
680
|
-
return manifestHit.absolutePath;
|
|
681
|
-
}
|
|
682
|
-
const cwdHit = tryCwdBuilderPathOrNull(appName);
|
|
670
|
+
const cwdHit = getCwdBuilderAppPath(appName);
|
|
683
671
|
if (cwdHit) {
|
|
684
672
|
return cwdHit;
|
|
685
673
|
}
|
|
686
|
-
return
|
|
674
|
+
return getHomeBuilderAppPath(appName);
|
|
687
675
|
}
|
|
688
676
|
|
|
689
677
|
/**
|
|
@@ -918,6 +906,8 @@ module.exports = {
|
|
|
918
906
|
getIntegrationBuilderBaseDir,
|
|
919
907
|
SYSTEM_BUILDER_APP_KEYS,
|
|
920
908
|
isSystemBuilderAppName,
|
|
909
|
+
getHomeBuilderAppPath,
|
|
910
|
+
getCwdBuilderAppPath,
|
|
921
911
|
getProjectBuilderAppPath,
|
|
922
912
|
getSystemPlatformMaterializationParent,
|
|
923
913
|
getSystemBuilderRoot,
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ app:
|
|
|
5
5
|
description: "AI Fabrix Dataplane is a secure, in-tenant integration and automation layer that supplies governed, normalized, and explainable enterprise data to AI agents. Using CIP as a declarative standard, it enforces RBAC and ABAC, executes integrations, and exposes trusted data via MCP and OpenAPI."
|
|
6
6
|
type: webapp
|
|
7
7
|
language: python # Explicitly specify Python language
|
|
8
|
-
version:
|
|
8
|
+
version: 2.0.0
|
|
9
9
|
|
|
10
10
|
# Image Configuration
|
|
11
11
|
# Set tag to match your build (e.g. aifabrix build dataplane -t 1.0.0 then tag: 1.0.0)
|
|
@@ -26,6 +26,7 @@ LOCAL_MODE=false
|
|
|
26
26
|
# Celery worker process tuning (dataplane-worker container; optional overrides)
|
|
27
27
|
# CELERY_WORKER_QUEUES=external_data,document_sync,document_processing,audit
|
|
28
28
|
# CELERY_WORKER_CONCURRENCY=2
|
|
29
|
+
# CELERY_WORKER_RUNTIME is set automatically by python -m app.core.celery_worker_main (NullPool per task).
|
|
29
30
|
# CELERY_LOG_LEVEL=info
|
|
30
31
|
|
|
31
32
|
# When API_KEY is set, a matching Bearer token bypasses OAuth2 validation
|
|
@@ -42,7 +43,7 @@ API_KEY_BYPASS_ROLES=full-access
|
|
|
42
43
|
|
|
43
44
|
# API Configuration
|
|
44
45
|
API_V1_STR=/api/v1
|
|
45
|
-
VERSION=
|
|
46
|
+
VERSION=2.0.0
|
|
46
47
|
# Minimum supported aifabrix-builder CLI semver (advertised on /health and /api/v1/health; empty = no gate)
|
|
47
48
|
# MIN_BUILDER_CLI_VERSION=2.45.0
|
|
48
49
|
MIN_BUILDER_CLI_VERSION=2.45.5
|
|
@@ -157,6 +157,8 @@ WAIT_FOR_KEYCLOAK=true
|
|
|
157
157
|
# For production: use public domain (e.g., https://miso.example.com)
|
|
158
158
|
# url://public includes front-door path from application.yaml (e.g. /controller).
|
|
159
159
|
MISO_WEB_SERVER_URL=url://public
|
|
160
|
+
# Provider-grant OAuth redirect_uri base (Azure/start-dev also set this explicitly; code falls back to MISO_WEB_SERVER_URL)
|
|
161
|
+
CONTROLLER_BASE_URL=url://public
|
|
160
162
|
MISO_CONTROLLER_URL=url://internal
|
|
161
163
|
MISO_RELATIVE_PATH=url://vdir-public
|
|
162
164
|
ALLOWED_ORIGINS=http://localhost:*,url://host-public,url://host-private,url://dataplane-host-public,url://dataplane-host-private
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"apiVersion":"dataplane.aifabrix.ai/v1","kind":"Protection","metadata":{"key":"hubspot-country-sales","displayName":"HubSpot Country Sales Access"},"spec":{"enabled":true,"datasourceKey":"hubspot-companies","mode":"replaceForSource","rules":[{"key":"sales-country-users","principal":{"type":"group","expression":"Sales {{fk.country.metadata.iso3}} Users"},"grants":[{"dimensionKey":"country","valueExpression":"{{fk.country.metadata.iso2}}"}]}]}}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"kind":"Protection"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"apiVersion":"dataplane.aifabrix.ai/v1","kind":"Protection","metadata":{"key":"hubspot-country-sales","displayName":"HubSpot Country Sales Access"},"spec":{"enabled":true,"datasourceKey":"hubspot-companies","mode":"replaceForSource","rules":[{"key":"sales-country-users","principal":{"type":"group","expression":"Sales {{fk.country.metadata.iso3}} Users"},"grants":[{"dimensionKey":"country","valueExpression":"{{fk.country.metadata.iso2}}"}]}]}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"kind":"Protection"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
apiVersion: dataplane.aifabrix.ai/v1
|
|
2
|
-
kind: Protection
|
|
3
|
-
metadata:
|
|
4
|
-
key: hubspot-country-sales
|
|
5
|
-
displayName: HubSpot Country Sales Access
|
|
6
|
-
spec:
|
|
7
|
-
enabled: true
|
|
8
|
-
datasourceKey: hubspot-companies
|
|
9
|
-
mode: replaceForSource
|
|
10
|
-
rules:
|
|
11
|
-
- key: sales-country-users
|
|
12
|
-
principal:
|
|
13
|
-
type: group
|
|
14
|
-
expression: "Sales {{fk.country.metadata.iso3}} Users"
|
|
15
|
-
grants:
|
|
16
|
-
- dimensionKey: country
|
|
17
|
-
valueExpression: "{{fk.country.metadata.iso2}}"
|