@aifabrix/builder 2.45.0 → 2.45.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +44 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +22 -3
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +33 -24
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +30 -3
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +2 -2
- package/lib/cli/setup-utility.js +99 -81
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/upload.js +34 -13
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/manifest-source-emit.js +22 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/external-system/README.md.hbs +56 -8
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Online probes for `protection create` (dataplane datasource + Controller dimension).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { getDatasource } = require('../api/datasources-core.api');
|
|
10
|
+
const { getDimension } = require('../api/dimensions.api');
|
|
11
|
+
const { unwrapApiData } = require('../utils/external-system-readiness-core');
|
|
12
|
+
const {
|
|
13
|
+
startDatasourceProbeLines,
|
|
14
|
+
appendDatasourceSuccessDetail,
|
|
15
|
+
startDimensionProbeLines,
|
|
16
|
+
appendDimensionSuccessDetail
|
|
17
|
+
} = require('./protection-create-probe-lines');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {*} response
|
|
21
|
+
* @returns {*}
|
|
22
|
+
*/
|
|
23
|
+
function unwrapControllerDimension(response) {
|
|
24
|
+
const d = response?.data?.data ?? response?.data ?? response;
|
|
25
|
+
return d;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string[]} lines
|
|
30
|
+
* @param {string} msg
|
|
31
|
+
* @returns {never}
|
|
32
|
+
*/
|
|
33
|
+
function throwProbe(lines, msg) {
|
|
34
|
+
const err = new Error(msg);
|
|
35
|
+
err.probeLines = lines;
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} dataplaneUrl
|
|
41
|
+
* @param {Object} authConfig
|
|
42
|
+
* @param {string} datasourceKey
|
|
43
|
+
* @param {Object} opts
|
|
44
|
+
* @returns {Promise<Object>}
|
|
45
|
+
*/
|
|
46
|
+
async function probeDatasourceForProtectionCreate(dataplaneUrl, authConfig, datasourceKey, opts = {}) {
|
|
47
|
+
const key = String(datasourceKey || '').trim();
|
|
48
|
+
const lines = startDatasourceProbeLines(key);
|
|
49
|
+
const res = await getDatasource(dataplaneUrl, key, authConfig);
|
|
50
|
+
if (res && res.success === false) {
|
|
51
|
+
const msg = res.formattedError || res.error || `Datasource "${key}" request failed`;
|
|
52
|
+
throwProbe(lines, typeof msg === 'string' ? msg : 'Datasource probe failed');
|
|
53
|
+
}
|
|
54
|
+
const row = unwrapApiData(res);
|
|
55
|
+
if (!row) {
|
|
56
|
+
throwProbe(
|
|
57
|
+
lines,
|
|
58
|
+
`Datasource "${key}" not found or not readable on the dataplane. Deploy the integration first.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
appendDatasourceSuccessDetail(lines, row, key, dataplaneUrl, opts);
|
|
62
|
+
return { row, lines };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} controllerUrl
|
|
67
|
+
* @param {Object} authConfig
|
|
68
|
+
* @param {string} dimensionKey
|
|
69
|
+
* @param {Object} opts
|
|
70
|
+
* @returns {Promise<Object>}
|
|
71
|
+
*/
|
|
72
|
+
async function probeDimensionForProtectionCreate(controllerUrl, authConfig, dimensionKey, opts = {}) {
|
|
73
|
+
const key = String(dimensionKey || '').trim();
|
|
74
|
+
const lines = startDimensionProbeLines(key);
|
|
75
|
+
const res = await getDimension(controllerUrl, authConfig, key, { includeValues: opts.verbose === true });
|
|
76
|
+
if (res && res.success === false) {
|
|
77
|
+
const msg = res.formattedError || res.error || `Dimension "${key}" request failed`;
|
|
78
|
+
throwProbe(lines, typeof msg === 'string' ? msg : 'Dimension probe failed');
|
|
79
|
+
}
|
|
80
|
+
const row = unwrapControllerDimension(res);
|
|
81
|
+
if (!row || !row.key) {
|
|
82
|
+
throwProbe(
|
|
83
|
+
lines,
|
|
84
|
+
`Dimension "${key}" not found in the Controller catalog. Create it with: aifabrix dimension create …`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
appendDimensionSuccessDetail(lines, row, controllerUrl, opts);
|
|
88
|
+
return { row, lines };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
probeDatasourceForProtectionCreate,
|
|
93
|
+
probeDimensionForProtectionCreate,
|
|
94
|
+
unwrapControllerDimension
|
|
95
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build protection manifest objects for `protection create`.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { requireProtectionPreset } = require('./protection-preset-registry');
|
|
10
|
+
|
|
11
|
+
const PROTECTION_KEY_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
12
|
+
const DIMENSION_KEY_RE = /^[a-zA-Z][a-zA-Z0-9-_]*$/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} raw
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function requireDatasourceKey(raw) {
|
|
19
|
+
const k = String(raw || '').trim();
|
|
20
|
+
if (!k) {
|
|
21
|
+
throw new Error('Datasource key is required');
|
|
22
|
+
}
|
|
23
|
+
if (!PROTECTION_KEY_RE.test(k)) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Datasource key must match ${PROTECTION_KEY_RE}: got "${k}"`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return k;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} raw
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function requireDimensionKey(raw) {
|
|
36
|
+
const k = String(raw || '').trim();
|
|
37
|
+
if (!k) {
|
|
38
|
+
throw new Error('--dimension-key is required for protection create');
|
|
39
|
+
}
|
|
40
|
+
if (!DIMENSION_KEY_RE.test(k)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'Dimension key must start with a letter and contain letters, numbers, hyphens, underscores only.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return k;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} raw
|
|
50
|
+
* @param {string} fallback
|
|
51
|
+
* @returns {string}
|
|
52
|
+
*/
|
|
53
|
+
function protectionMetadataKey(raw, fallback) {
|
|
54
|
+
const k = String(raw || '').trim();
|
|
55
|
+
if (k) {
|
|
56
|
+
if (!PROTECTION_KEY_RE.test(k)) {
|
|
57
|
+
throw new Error(`--protection-key must match ${PROTECTION_KEY_RE}: got "${k}"`);
|
|
58
|
+
}
|
|
59
|
+
return k;
|
|
60
|
+
}
|
|
61
|
+
return fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {Object} vars
|
|
66
|
+
* @param {string} vars.datasourceKey
|
|
67
|
+
* @param {string} vars.dimensionKey
|
|
68
|
+
* @param {string} [vars.protectionKey]
|
|
69
|
+
* @param {string} [vars.displayName]
|
|
70
|
+
* @param {string} [vars.ruleKey]
|
|
71
|
+
* @param {Object} vars.principal
|
|
72
|
+
* @param {string} vars.valueExpression
|
|
73
|
+
* @param {Object} [vars.when]
|
|
74
|
+
* @returns {Object}
|
|
75
|
+
*/
|
|
76
|
+
function buildProtectionManifest(vars) {
|
|
77
|
+
const datasourceKey = requireDatasourceKey(vars.datasourceKey);
|
|
78
|
+
const dimensionKey = requireDimensionKey(vars.dimensionKey);
|
|
79
|
+
const presetPart = vars.type ? `-${vars.type}` : '';
|
|
80
|
+
const protectionKey = protectionMetadataKey(
|
|
81
|
+
vars.protectionKey,
|
|
82
|
+
`${datasourceKey}-${dimensionKey}${presetPart}-access`
|
|
83
|
+
);
|
|
84
|
+
const ruleKey = String(vars.ruleKey || '').trim() || `${dimensionKey}${presetPart || '-access'}`;
|
|
85
|
+
const rule = {
|
|
86
|
+
key: ruleKey,
|
|
87
|
+
principal: vars.principal,
|
|
88
|
+
grants: [{ dimensionKey, valueExpression: vars.valueExpression }]
|
|
89
|
+
};
|
|
90
|
+
if (vars.when) {
|
|
91
|
+
rule.when = JSON.parse(JSON.stringify(vars.when));
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
apiVersion: 'dataplane.aifabrix.ai/v1',
|
|
95
|
+
kind: 'Protection',
|
|
96
|
+
metadata: {
|
|
97
|
+
key: protectionKey,
|
|
98
|
+
displayName: String(vars.displayName || '').trim() || `${datasourceKey} — ${dimensionKey}`
|
|
99
|
+
},
|
|
100
|
+
spec: {
|
|
101
|
+
enabled: vars.enabled !== false,
|
|
102
|
+
datasourceKey,
|
|
103
|
+
mode: 'replaceForSource',
|
|
104
|
+
rules: [rule]
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {Object} datasource
|
|
111
|
+
* @param {string} field
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
function hasMetadataField(datasource, field) {
|
|
115
|
+
const props = datasource?.metadataSchema?.properties;
|
|
116
|
+
if (!props || typeof props !== 'object' || Array.isArray(props)) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return Object.prototype.hasOwnProperty.call(props, field);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {Object} datasource
|
|
124
|
+
* @param {string} name
|
|
125
|
+
* @returns {Object|null}
|
|
126
|
+
*/
|
|
127
|
+
function findForeignKey(datasource, name) {
|
|
128
|
+
const fks = Array.isArray(datasource?.foreignKeys) ? datasource.foreignKeys : [];
|
|
129
|
+
return fks.find((fk) => fk && fk.name === name) || null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {Object} datasource
|
|
134
|
+
* @param {string} field
|
|
135
|
+
*/
|
|
136
|
+
function assertMetadataField(datasource, field) {
|
|
137
|
+
if (!hasMetadataField(datasource, field)) {
|
|
138
|
+
throw new Error(`Preset field not found in datasource metadataSchema.properties: ${field}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {Object} preset
|
|
144
|
+
* @param {Object} dimension
|
|
145
|
+
*/
|
|
146
|
+
function assertDimensionValueType(preset, dimension) {
|
|
147
|
+
const valueType = String(dimension?.valueType || '').trim();
|
|
148
|
+
if (preset.staticOnly && valueType === 'dynamic') {
|
|
149
|
+
throw new Error(`Protection type "${preset.type}" requires a static or both dimension.`);
|
|
150
|
+
}
|
|
151
|
+
if (preset.dynamicOnly && valueType === 'static') {
|
|
152
|
+
throw new Error(`Protection type "${preset.type}" requires a dynamic or both dimension.`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @param {Object} vars
|
|
158
|
+
* @param {Object} preset
|
|
159
|
+
* @returns {{ principal: Object, valueExpression: string, when: Object|undefined }}
|
|
160
|
+
*/
|
|
161
|
+
function resolveStandardPresetSpec(vars, preset) {
|
|
162
|
+
const field = String(vars.field || preset.valueField || preset.dimensionKey).trim();
|
|
163
|
+
assertMetadataField(vars.datasource, field);
|
|
164
|
+
const principal = vars.principalExpression
|
|
165
|
+
? { type: 'group', expression: String(vars.principalExpression).trim() }
|
|
166
|
+
: principalFromPreset(vars, preset);
|
|
167
|
+
const valueExpression =
|
|
168
|
+
String(vars.valueExpression || '').trim() || preset.valueTemplate || `{{metadata.${field}}}`;
|
|
169
|
+
return { principal, valueExpression, when: preset.when };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {Object} vars
|
|
174
|
+
* @param {Object} preset
|
|
175
|
+
* @returns {Object}
|
|
176
|
+
*/
|
|
177
|
+
function principalFromPreset(vars, preset) {
|
|
178
|
+
if (preset.type === 'static-region') {
|
|
179
|
+
const displayField = hasMetadataField(vars.datasource, 'regionDisplayName') ? 'regionDisplayName' : 'region';
|
|
180
|
+
return { type: 'group', expression: `{{metadata.${displayField}}} Users` };
|
|
181
|
+
}
|
|
182
|
+
if (preset.principalType === 'user') {
|
|
183
|
+
assertMetadataField(vars.datasource, String(preset.principalField).replace(/^metadata\./, ''));
|
|
184
|
+
return { type: 'user', field: preset.principalField };
|
|
185
|
+
}
|
|
186
|
+
return { type: 'group', expression: preset.principalTemplate };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param {Object} vars
|
|
191
|
+
* @param {Object} preset
|
|
192
|
+
* @returns {{ principal: Object, valueExpression: string }}
|
|
193
|
+
*/
|
|
194
|
+
function resolveCountrySalesSpec(vars, preset) {
|
|
195
|
+
const dimensionKey = vars.dimensionKey;
|
|
196
|
+
const fkName = String(vars.fkName || dimensionKey).trim();
|
|
197
|
+
const explicitField = String(vars.field || '').trim();
|
|
198
|
+
const fk = explicitField ? null : findForeignKey(vars.datasource, fkName);
|
|
199
|
+
if (vars.fkName && !fk) {
|
|
200
|
+
throw new Error(`Preset FK not found in datasource foreignKeys[].name: ${fkName}`);
|
|
201
|
+
}
|
|
202
|
+
if (fk) {
|
|
203
|
+
return {
|
|
204
|
+
principal: { type: 'group', expression: `Sales {{fk.${fkName}.metadata.iso3}} Users` },
|
|
205
|
+
valueExpression: String(vars.valueExpression || '').trim() || `{{fk.${fkName}.metadata.iso2}}`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const field = explicitField || preset.dimensionKey;
|
|
209
|
+
assertMetadataField(vars.datasource, field);
|
|
210
|
+
return {
|
|
211
|
+
principal: { type: 'group', expression: `Sales {{metadata.${field}}} Users` },
|
|
212
|
+
valueExpression: String(vars.valueExpression || '').trim() || `{{metadata.${field}}}`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {Object} vars
|
|
218
|
+
* @returns {Object}
|
|
219
|
+
*/
|
|
220
|
+
function buildPresetProtectionManifest(vars) {
|
|
221
|
+
const preset = requireProtectionPreset(vars.type);
|
|
222
|
+
if (!preset) {
|
|
223
|
+
return buildDynamicFkProtectionManifest(vars);
|
|
224
|
+
}
|
|
225
|
+
const dimensionKey = requireDimensionKey(vars.dimensionKey || preset.dimensionKey);
|
|
226
|
+
assertDimensionValueType(preset, vars.dimension);
|
|
227
|
+
const resolved = preset.type === 'country-sales'
|
|
228
|
+
? resolveCountrySalesSpec({ ...vars, dimensionKey }, preset)
|
|
229
|
+
: resolveStandardPresetSpec({ ...vars, dimensionKey }, preset);
|
|
230
|
+
return buildProtectionManifest({
|
|
231
|
+
...vars,
|
|
232
|
+
type: preset.type,
|
|
233
|
+
dimensionKey,
|
|
234
|
+
principal: resolved.principal,
|
|
235
|
+
valueExpression: resolved.valueExpression,
|
|
236
|
+
when: resolved.when
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Build v1 "country-from-FK" style manifest (dynamic dimension from one-hop FK metadata).
|
|
242
|
+
*
|
|
243
|
+
* @param {Object} vars
|
|
244
|
+
* @param {string} vars.datasourceKey
|
|
245
|
+
* @param {string} vars.dimensionKey
|
|
246
|
+
* @param {string} [vars.fkName] - defaults to dimensionKey
|
|
247
|
+
* @param {string} [vars.protectionKey]
|
|
248
|
+
* @param {string} [vars.displayName]
|
|
249
|
+
* @param {string} [vars.ruleKey]
|
|
250
|
+
* @param {string} [vars.principalExpression]
|
|
251
|
+
* @param {string} [vars.valueExpression]
|
|
252
|
+
* @returns {Object}
|
|
253
|
+
*/
|
|
254
|
+
function buildDynamicFkProtectionManifest(vars) {
|
|
255
|
+
const datasourceKey = requireDatasourceKey(vars.datasourceKey);
|
|
256
|
+
const dimensionKey = requireDimensionKey(vars.dimensionKey);
|
|
257
|
+
const fkName = String(vars.fkName || dimensionKey).trim() || dimensionKey;
|
|
258
|
+
const principalExpression =
|
|
259
|
+
String(vars.principalExpression || '').trim() ||
|
|
260
|
+
`Sales {{fk.${fkName}.metadata.iso3}} Users`;
|
|
261
|
+
const valueExpression =
|
|
262
|
+
String(vars.valueExpression || '').trim() ||
|
|
263
|
+
`{{fk.${fkName}.metadata.iso2}}`;
|
|
264
|
+
return buildProtectionManifest({
|
|
265
|
+
...vars,
|
|
266
|
+
datasourceKey,
|
|
267
|
+
dimensionKey,
|
|
268
|
+
ruleKey: vars.ruleKey || `${dimensionKey}-from-fk-users`,
|
|
269
|
+
displayName: vars.displayName || `${datasourceKey} — ${dimensionKey} (FK projection)`,
|
|
270
|
+
principal: { type: 'group', expression: principalExpression },
|
|
271
|
+
valueExpression
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = {
|
|
276
|
+
buildPresetProtectionManifest,
|
|
277
|
+
buildDynamicFkProtectionManifest,
|
|
278
|
+
buildProtectionManifest,
|
|
279
|
+
requireDatasourceKey,
|
|
280
|
+
requireDimensionKey
|
|
281
|
+
};
|