@aifabrix/builder 2.45.0 → 2.46.0
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/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +130 -0
- package/jest.projects.js +69 -121
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/api-response-errors.js +124 -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-scenario-pack.api.js +159 -0
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +88 -5
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +7 -5
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +43 -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 +5 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/deploy.js +34 -19
- package/lib/app/down.js +20 -1
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +91 -69
- package/lib/cli/setup-app.help.js +19 -2
- package/lib/cli/setup-app.js +58 -23
- package/lib/cli/setup-app.test-commands.js +28 -21
- package/lib/cli/setup-auth.js +55 -3
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +62 -6
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +65 -0
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +42 -6
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +41 -258
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +269 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +111 -53
- 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 +118 -0
- package/lib/commands/datasource-unified-test-cli.js +31 -20
- package/lib/commands/datasource-unified-test-cli.options.js +22 -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/dev-show-display.js +8 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- 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/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +56 -17
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -286
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +67 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +61 -14
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-external.js +67 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-external.js +126 -0
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +108 -105
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +5 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +55 -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 +95 -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 +115 -0
- package/lib/datasource/audit-evidence-extract.js +180 -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 +205 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +100 -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 +95 -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 +29 -2
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +47 -18
- package/lib/datasource/validate.js +37 -20
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +9 -7
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +309 -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/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display.js +471 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +51 -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/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2588
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +642 -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/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/dataplane-resolver.js +129 -2
- 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/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +35 -26
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +24 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +98 -0
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +22 -7
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +36 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +116 -11
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Deterministic preset registry for `aifabrix protection create --type`.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const ProtectionPresetRegistry = Object.freeze({
|
|
10
|
+
'country-sales': Object.freeze({
|
|
11
|
+
type: 'country-sales',
|
|
12
|
+
dimensionKey: 'country',
|
|
13
|
+
principalTemplate: 'Sales {{value}} Users',
|
|
14
|
+
valueTemplate: '{{value}}'
|
|
15
|
+
}),
|
|
16
|
+
'department-manager': Object.freeze({
|
|
17
|
+
type: 'department-manager',
|
|
18
|
+
dimensionKey: 'department',
|
|
19
|
+
principalType: 'user',
|
|
20
|
+
principalField: 'metadata.managerEmail',
|
|
21
|
+
valueField: 'department',
|
|
22
|
+
valueTemplate: '{{metadata.department}}',
|
|
23
|
+
requiresField: 'department',
|
|
24
|
+
when: Object.freeze({ groups: Object.freeze({ requireAny: Object.freeze(['Manager']) }) })
|
|
25
|
+
}),
|
|
26
|
+
'customer-team': Object.freeze({
|
|
27
|
+
type: 'customer-team',
|
|
28
|
+
dimensionKey: 'customer',
|
|
29
|
+
principalTemplate: 'Customer {{metadata.customer}} Team',
|
|
30
|
+
valueField: 'customer',
|
|
31
|
+
valueTemplate: '{{metadata.customer}}',
|
|
32
|
+
requiresField: 'customer'
|
|
33
|
+
}),
|
|
34
|
+
'project-team': Object.freeze({
|
|
35
|
+
type: 'project-team',
|
|
36
|
+
dimensionKey: 'project',
|
|
37
|
+
principalTemplate: 'Project {{metadata.project}} Team',
|
|
38
|
+
valueField: 'project',
|
|
39
|
+
valueTemplate: '{{metadata.project}}',
|
|
40
|
+
requiresField: 'project'
|
|
41
|
+
}),
|
|
42
|
+
'static-region': Object.freeze({
|
|
43
|
+
type: 'static-region',
|
|
44
|
+
dimensionKey: 'region',
|
|
45
|
+
principalTemplate: '{{metadata.regionDisplayName}} Users',
|
|
46
|
+
valueField: 'region',
|
|
47
|
+
valueTemplate: '{{metadata.region}}',
|
|
48
|
+
requiresField: 'region',
|
|
49
|
+
staticOnly: true
|
|
50
|
+
}),
|
|
51
|
+
'owner-direct': Object.freeze({
|
|
52
|
+
type: 'owner-direct',
|
|
53
|
+
dimensionKey: 'owner',
|
|
54
|
+
principalType: 'user',
|
|
55
|
+
principalField: 'metadata.ownerEmail',
|
|
56
|
+
valueField: 'owner',
|
|
57
|
+
valueTemplate: '{{metadata.owner}}',
|
|
58
|
+
requiresField: 'owner'
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @returns {string[]}
|
|
64
|
+
*/
|
|
65
|
+
function listProtectionPresetTypes() {
|
|
66
|
+
return Object.keys(ProtectionPresetRegistry);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {string|undefined} type
|
|
71
|
+
* @returns {Object|null}
|
|
72
|
+
*/
|
|
73
|
+
function getProtectionPreset(type) {
|
|
74
|
+
const key = String(type || '').trim();
|
|
75
|
+
return key ? ProtectionPresetRegistry[key] || null : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string|undefined} type
|
|
80
|
+
* @returns {Object|null}
|
|
81
|
+
*/
|
|
82
|
+
function requireProtectionPreset(type) {
|
|
83
|
+
const preset = getProtectionPreset(type);
|
|
84
|
+
if (preset || !String(type || '').trim()) {
|
|
85
|
+
return preset;
|
|
86
|
+
}
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Unknown protection type "${type}". Supported types: ${listProtectionPresetTypes().join(', ')}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
ProtectionPresetRegistry,
|
|
94
|
+
getProtectionPreset,
|
|
95
|
+
listProtectionPresetTypes,
|
|
96
|
+
requireProtectionPreset
|
|
97
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Exit codes for protection validate reports.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} report
|
|
11
|
+
* @returns {Object[]}
|
|
12
|
+
*/
|
|
13
|
+
function reportTaskRows(report) {
|
|
14
|
+
if (!report || typeof report !== 'object') {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(report.results)) {
|
|
18
|
+
return report.results;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(report.tasks)) {
|
|
21
|
+
return report.tasks;
|
|
22
|
+
}
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Object} report
|
|
28
|
+
* @param {Object} [opts]
|
|
29
|
+
* @param {boolean} [opts.warningsAsErrors]
|
|
30
|
+
* @returns {number} 0 ok, 1 fail/warn-as-error, 3 invalid input
|
|
31
|
+
*/
|
|
32
|
+
function exitCodeFromProtectionReport(report, opts = {}) {
|
|
33
|
+
if (!report || typeof report !== 'object') {
|
|
34
|
+
return 3;
|
|
35
|
+
}
|
|
36
|
+
const summary = report.summary || {};
|
|
37
|
+
const failCount = Number(summary.fail || 0);
|
|
38
|
+
const warnCount = Number(summary.warn || 0);
|
|
39
|
+
if (failCount > 0) {
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
if (opts.warningsAsErrors === true && warnCount > 0) {
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
const rows = reportTaskRows(report);
|
|
46
|
+
const hasFail = rows.some((r) => String(r?.status || '').toUpperCase() === 'FAIL');
|
|
47
|
+
if (hasFail) {
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
if (opts.warningsAsErrors === true) {
|
|
51
|
+
const hasWarn = rows.some((r) => String(r?.status || '').toUpperCase() === 'WARN');
|
|
52
|
+
if (hasWarn) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
reportTaskRows,
|
|
61
|
+
exitCodeFromProtectionReport
|
|
62
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resolve protection manifests by datasource key under `integration/.protection/`.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { getProtectionRoot } = require('./paths');
|
|
12
|
+
const { loadProtectionManifest } = require('./load');
|
|
13
|
+
|
|
14
|
+
const PROTECTION_FILENAME_RE = /^([a-z0-9][a-z0-9-]*)-protection-([a-z0-9][a-z0-9-]*)$/i;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} root
|
|
18
|
+
* @param {string} candidate
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
function isPathInsideRoot(root, candidate) {
|
|
22
|
+
const resolvedRoot = path.resolve(root);
|
|
23
|
+
const resolved = path.resolve(candidate);
|
|
24
|
+
return resolved === resolvedRoot || resolved.startsWith(`${resolvedRoot}${path.sep}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} stem
|
|
29
|
+
* @returns {string|null}
|
|
30
|
+
*/
|
|
31
|
+
function datasourceKeyFromProtectionFilename(stem) {
|
|
32
|
+
const m = PROTECTION_FILENAME_RE.exec(stem);
|
|
33
|
+
if (!m) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return `${m[1]}-${m[2]}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} root
|
|
41
|
+
* @returns {string[]}
|
|
42
|
+
*/
|
|
43
|
+
function listProtectionManifestPaths(root) {
|
|
44
|
+
if (!fs.existsSync(root)) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return fs
|
|
48
|
+
.readdirSync(root)
|
|
49
|
+
.filter((name) => /\.(yaml|yml|json)$/i.test(name))
|
|
50
|
+
.map((name) => path.join(root, name))
|
|
51
|
+
.sort((a, b) => a.localeCompare(b));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} root
|
|
56
|
+
* @throws {Error}
|
|
57
|
+
*/
|
|
58
|
+
function assertNoDuplicateDatasourceKeys(root) {
|
|
59
|
+
const seen = new Map();
|
|
60
|
+
for (const filePath of listProtectionManifestPaths(root)) {
|
|
61
|
+
let manifest;
|
|
62
|
+
try {
|
|
63
|
+
manifest = loadProtectionManifest(filePath);
|
|
64
|
+
} catch {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const ds = String(manifest?.spec?.datasourceKey || '').trim();
|
|
68
|
+
if (!ds) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (seen.has(ds)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Duplicate spec.datasourceKey "${ds}" in .protection/ (${seen.get(ds)} and ${filePath})`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
seen.set(ds, filePath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {Object} manifest
|
|
82
|
+
* @param {string} key
|
|
83
|
+
* @param {string} location
|
|
84
|
+
*/
|
|
85
|
+
function assertManifestDatasourceKey(manifest, key, location) {
|
|
86
|
+
const specKey = String(manifest?.spec?.datasourceKey || '').trim();
|
|
87
|
+
if (specKey && specKey !== key) {
|
|
88
|
+
throw new Error(`spec.datasourceKey "${specKey}" does not match CLI argument "${key}"${location}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} key
|
|
94
|
+
* @param {string} manifestPath
|
|
95
|
+
* @param {Object} manifest
|
|
96
|
+
*/
|
|
97
|
+
function protectionManifestResult(key, manifestPath, manifest) {
|
|
98
|
+
assertManifestDatasourceKey(manifest, key, ` in ${manifestPath}`);
|
|
99
|
+
return { datasourceKey: key, manifestPath, manifest };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} key
|
|
104
|
+
* @param {string} root
|
|
105
|
+
* @returns {string[]}
|
|
106
|
+
*/
|
|
107
|
+
function collectProtectionManifestMatches(key, root) {
|
|
108
|
+
const matches = [];
|
|
109
|
+
for (const filePath of listProtectionManifestPaths(root)) {
|
|
110
|
+
const stem = path.basename(filePath, path.extname(filePath));
|
|
111
|
+
if (stem === key || datasourceKeyFromProtectionFilename(stem) === key) {
|
|
112
|
+
matches.push(filePath);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const manifest = loadProtectionManifest(filePath);
|
|
117
|
+
if (String(manifest?.spec?.datasourceKey || '').trim() === key) {
|
|
118
|
+
matches.push(filePath);
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
/* skip unreadable */
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return matches;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} datasourceKey
|
|
129
|
+
* @param {string} [explicitPath]
|
|
130
|
+
* @returns {{ datasourceKey: string, manifestPath: string, manifest: Object }}
|
|
131
|
+
*/
|
|
132
|
+
function resolveProtectionManifest(datasourceKey, explicitPath) {
|
|
133
|
+
const key = String(datasourceKey || '').trim();
|
|
134
|
+
if (!key) throw new Error('Datasource key is required');
|
|
135
|
+
|
|
136
|
+
const root = getProtectionRoot();
|
|
137
|
+
assertNoDuplicateDatasourceKeys(root);
|
|
138
|
+
|
|
139
|
+
if (explicitPath) {
|
|
140
|
+
const manifestPath = path.resolve(explicitPath);
|
|
141
|
+
if (!isPathInsideRoot(root, manifestPath)) {
|
|
142
|
+
throw new Error(`Protection path must be under ${root}`);
|
|
143
|
+
}
|
|
144
|
+
return protectionManifestResult(key, manifestPath, loadProtectionManifest(manifestPath));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const ext of ['.yaml', '.yml', '.json']) {
|
|
148
|
+
const preferred = path.join(root, `${key}${ext}`);
|
|
149
|
+
if (fs.existsSync(preferred)) {
|
|
150
|
+
return protectionManifestResult(key, preferred, loadProtectionManifest(preferred));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const matches = collectProtectionManifestMatches(key, root);
|
|
155
|
+
if (matches.length === 0) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`No protection manifest for datasource "${key}" under ${root}. Expected ${key}.yaml or ${key}.json`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (matches.length > 1) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Ambiguous protection manifest for "${key}": ${matches.map((p) => path.basename(p)).join(', ')}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const manifestPath = matches[0];
|
|
167
|
+
return protectionManifestResult(key, manifestPath, loadProtectionManifest(manifestPath));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {string} arg - CLI positional (datasource key or file path)
|
|
172
|
+
* @returns {{ datasourceKey: string, manifestPath: string, manifest: Object }}
|
|
173
|
+
*/
|
|
174
|
+
function resolveProtectionArgument(arg) {
|
|
175
|
+
const raw = String(arg || '').trim();
|
|
176
|
+
if (!raw) {
|
|
177
|
+
throw new Error('Datasource key is required');
|
|
178
|
+
}
|
|
179
|
+
const root = getProtectionRoot();
|
|
180
|
+
const asPath = path.resolve(raw);
|
|
181
|
+
if (fs.existsSync(asPath) && fs.statSync(asPath).isFile()) {
|
|
182
|
+
if (!isPathInsideRoot(root, asPath)) {
|
|
183
|
+
throw new Error(`Protection file must be under ${root}`);
|
|
184
|
+
}
|
|
185
|
+
const manifest = loadProtectionManifest(asPath);
|
|
186
|
+
const ds = String(manifest?.spec?.datasourceKey || '').trim();
|
|
187
|
+
if (!ds) {
|
|
188
|
+
throw new Error('Manifest spec.datasourceKey is required');
|
|
189
|
+
}
|
|
190
|
+
return resolveProtectionManifest(ds, asPath);
|
|
191
|
+
}
|
|
192
|
+
return resolveProtectionManifest(raw);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = {
|
|
196
|
+
resolveProtectionManifest,
|
|
197
|
+
resolveProtectionArgument,
|
|
198
|
+
listProtectionManifestPaths,
|
|
199
|
+
datasourceKeyFromProtectionFilename,
|
|
200
|
+
isPathInsideRoot,
|
|
201
|
+
assertNoDuplicateDatasourceKeys
|
|
202
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dataplane validation path for protection validate command.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const { validateProtectionManifestLocal } = require('./validate-local');
|
|
11
|
+
const { resolveProtectionDataplaneContext } = require('./auth-context');
|
|
12
|
+
const { validateProtection, simulateProtection } = require('../api/protection.api');
|
|
13
|
+
const { exitCodeFromProtectionReport } = require('./report-exit');
|
|
14
|
+
const {
|
|
15
|
+
printProtectionValidateReport,
|
|
16
|
+
formatProgress,
|
|
17
|
+
formatSuccessLine,
|
|
18
|
+
formatBlockingError,
|
|
19
|
+
metadata,
|
|
20
|
+
headerKeyValue,
|
|
21
|
+
sectionTitle,
|
|
22
|
+
SEP
|
|
23
|
+
} = require('./protection-display');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {Object} logger
|
|
27
|
+
* @param {Object} resolved
|
|
28
|
+
* @param {Object} ctx
|
|
29
|
+
*/
|
|
30
|
+
function printProtectionTargetHeader(logger, resolved, ctx) {
|
|
31
|
+
logger.log(sectionTitle('Target'));
|
|
32
|
+
logger.log(SEP);
|
|
33
|
+
logger.log(headerKeyValue('Environment:', ctx.environment));
|
|
34
|
+
logger.log(headerKeyValue('Dataplane:', ctx.dataplaneUrl));
|
|
35
|
+
logger.log('');
|
|
36
|
+
logger.log(metadata(`Protection: work — ${resolved.manifestPath}`));
|
|
37
|
+
logger.log(headerKeyValue('Datasource:', resolved.datasourceKey));
|
|
38
|
+
logger.log('');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {Object} manifest
|
|
43
|
+
* @param {Object} opts
|
|
44
|
+
* @param {Object} logger
|
|
45
|
+
* @returns {number}
|
|
46
|
+
*/
|
|
47
|
+
function runLocalSchemaGate(manifest, opts, logger) {
|
|
48
|
+
const local = validateProtectionManifestLocal(manifest);
|
|
49
|
+
if (!local.valid) {
|
|
50
|
+
if (opts.json) {
|
|
51
|
+
process.stdout.write(JSON.stringify({ valid: false, errors: local.errors }, null, 2));
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
logger.error(formatBlockingError('Local schema check failed:'));
|
|
55
|
+
local.errors.forEach((e) => logger.error(chalk.gray(` ${e}`)));
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
if (opts.json) {
|
|
59
|
+
logger.log(formatProgress('Local schema valid'));
|
|
60
|
+
} else {
|
|
61
|
+
logger.log(formatProgress('Local schema check...'));
|
|
62
|
+
logger.log(formatSuccessLine('Local schema valid'));
|
|
63
|
+
}
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Object} ctx
|
|
69
|
+
* @param {Object} manifest
|
|
70
|
+
* @param {Object} opts
|
|
71
|
+
* @param {Object} logger
|
|
72
|
+
* @param {string} datasourceKey
|
|
73
|
+
* @param {string} manifestPath
|
|
74
|
+
* @returns {Promise<number>}
|
|
75
|
+
*/
|
|
76
|
+
async function runDataplaneProtectionValidate(ctx, manifest, opts, logger, datasourceKey, manifestPath) {
|
|
77
|
+
printProtectionTargetHeader(logger, { datasourceKey, manifestPath }, ctx);
|
|
78
|
+
logger.log(formatProgress('Dataplane validation...'));
|
|
79
|
+
const report = await validateProtection(ctx.dataplaneUrl, ctx.authConfig, manifest, {
|
|
80
|
+
strict: opts.warningsAsErrors === true
|
|
81
|
+
});
|
|
82
|
+
let simulationBlock = null;
|
|
83
|
+
if (opts.simulate && exitCodeFromProtectionReport(report, opts) === 0) {
|
|
84
|
+
const sim = await simulateProtection(ctx.dataplaneUrl, ctx.authConfig, manifest, {
|
|
85
|
+
strict: opts.warningsAsErrors === true
|
|
86
|
+
});
|
|
87
|
+
const meta = sim?.metadata || {};
|
|
88
|
+
simulationBlock = {
|
|
89
|
+
recordsSampled: meta.recordsSampled,
|
|
90
|
+
grantsProjected: meta.grantsProjected,
|
|
91
|
+
unresolvedPrincipals: meta.unresolvedPrincipals
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
printProtectionValidateReport(report, {
|
|
95
|
+
json: opts.json,
|
|
96
|
+
verbose: opts.verbose,
|
|
97
|
+
warningsAsErrors: opts.warningsAsErrors,
|
|
98
|
+
simulationBlock,
|
|
99
|
+
logger
|
|
100
|
+
});
|
|
101
|
+
return exitCodeFromProtectionReport(report, opts);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} datasourceKey
|
|
106
|
+
* @param {Object} manifest
|
|
107
|
+
* @param {Object} opts
|
|
108
|
+
* @param {Object} logger
|
|
109
|
+
* @returns {Promise<number>}
|
|
110
|
+
*/
|
|
111
|
+
async function runProtectionValidate(datasourceKey, manifest, opts, logger) {
|
|
112
|
+
const localCode = runLocalSchemaGate(manifest, opts, logger);
|
|
113
|
+
if (localCode !== 0) {
|
|
114
|
+
return localCode;
|
|
115
|
+
}
|
|
116
|
+
const ctx = await resolveProtectionDataplaneContext(opts);
|
|
117
|
+
return runDataplaneProtectionValidate(
|
|
118
|
+
ctx,
|
|
119
|
+
manifest,
|
|
120
|
+
opts,
|
|
121
|
+
logger,
|
|
122
|
+
datasourceKey,
|
|
123
|
+
opts.manifestPath
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
printProtectionTargetHeader,
|
|
129
|
+
runProtectionValidate
|
|
130
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Single-manifest protection command implementations.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const inquirer = require('inquirer');
|
|
11
|
+
const { resolveProtectionArgument } = require('./resolve');
|
|
12
|
+
const { runProtectionValidate } = require('./run-commands-validate');
|
|
13
|
+
const { resolveProtectionDataplaneContext } = require('./auth-context');
|
|
14
|
+
const {
|
|
15
|
+
uploadProtection,
|
|
16
|
+
getProtection,
|
|
17
|
+
getProtectionStatus,
|
|
18
|
+
deleteProtection,
|
|
19
|
+
findProtectionKeyByDatasource
|
|
20
|
+
} = require('../api/protection.api');
|
|
21
|
+
const { preflightDatasourceReady } = require('./preflight-datasource-ready');
|
|
22
|
+
const { syncDatasourceAfterProtectionUpload } = require('./sync-after-upload');
|
|
23
|
+
const {
|
|
24
|
+
formatProtectionListTTY,
|
|
25
|
+
formatProtectionShowTTY,
|
|
26
|
+
formatProtectionDeleteSummaryTTY,
|
|
27
|
+
formatProgress,
|
|
28
|
+
formatSuccessLine,
|
|
29
|
+
formatWarningLine
|
|
30
|
+
} = require('./protection-display');
|
|
31
|
+
const { listProtectionManifests } = require('../api/protection.api');
|
|
32
|
+
const { getProtectionRoot } = require('./paths');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} datasourceKey
|
|
36
|
+
* @param {Object} manifest
|
|
37
|
+
* @param {Object} opts
|
|
38
|
+
* @param {Object} logger
|
|
39
|
+
* @returns {Promise<number>}
|
|
40
|
+
*/
|
|
41
|
+
async function runProtectionUpload(datasourceKey, manifest, opts, logger) {
|
|
42
|
+
const code = await runProtectionValidate(datasourceKey, manifest, {
|
|
43
|
+
...opts,
|
|
44
|
+
simulate: false,
|
|
45
|
+
json: false
|
|
46
|
+
}, logger);
|
|
47
|
+
if (code !== 0) {
|
|
48
|
+
return code;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ctx = await resolveProtectionDataplaneContext(opts);
|
|
52
|
+
await preflightDatasourceReady(ctx.dataplaneUrl, ctx.authConfig, manifest);
|
|
53
|
+
|
|
54
|
+
if (opts.dryRun) {
|
|
55
|
+
logger.log(formatWarningLine('Dry run: would upload protection (no mutation).'));
|
|
56
|
+
logger.log(
|
|
57
|
+
chalk.gray(
|
|
58
|
+
` key=${manifest?.metadata?.key || '—'} datasource=${datasourceKey} rules=${(manifest?.spec?.rules || []).length}`
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.log(formatProgress('Uploading protection manifest...'));
|
|
65
|
+
const uploaded = await uploadProtection(ctx.dataplaneUrl, ctx.authConfig, manifest);
|
|
66
|
+
logger.log(
|
|
67
|
+
formatSuccessLine(
|
|
68
|
+
`Protection for '${datasourceKey}' uploaded (deploymentId ${uploaded?.deploymentId || '—'}, revision ${uploaded?.revision ?? '—'})`
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!opts.noSync) {
|
|
73
|
+
logger.log(formatProgress(`Syncing datasource ${datasourceKey}...`));
|
|
74
|
+
const sync = await syncDatasourceAfterProtectionUpload(
|
|
75
|
+
ctx.dataplaneUrl,
|
|
76
|
+
ctx.authConfig,
|
|
77
|
+
datasourceKey
|
|
78
|
+
);
|
|
79
|
+
if (sync.syncJobId) {
|
|
80
|
+
logger.log(formatSuccessLine(`Sync started (syncJobId: ${sync.syncJobId})`));
|
|
81
|
+
} else if (sync.warning) {
|
|
82
|
+
logger.log(formatWarningLine(`Sync warning: ${sync.warning}`));
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
logger.log(
|
|
86
|
+
chalk.gray(
|
|
87
|
+
'Sync skipped (--no-sync). Protection active on dataplane; projections update on next datasource sync.'
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} datasourceKey
|
|
96
|
+
* @param {Object} opts
|
|
97
|
+
* @param {Object} logger
|
|
98
|
+
* @returns {Promise<string|null>}
|
|
99
|
+
*/
|
|
100
|
+
async function resolveProtectionKeyForDatasource(datasourceKey, ctx) {
|
|
101
|
+
try {
|
|
102
|
+
const resolved = resolveProtectionArgument(datasourceKey);
|
|
103
|
+
return String(resolved.manifest?.metadata?.key || '').trim() || null;
|
|
104
|
+
} catch {
|
|
105
|
+
return findProtectionKeyByDatasource(ctx.dataplaneUrl, ctx.authConfig, datasourceKey);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {Object} opts
|
|
111
|
+
* @param {Object} logger
|
|
112
|
+
* @returns {Promise<number>}
|
|
113
|
+
*/
|
|
114
|
+
async function runProtectionList(opts, logger) {
|
|
115
|
+
const ctx = await resolveProtectionDataplaneContext(opts);
|
|
116
|
+
const params = {};
|
|
117
|
+
if (opts.page !== undefined && opts.page !== null && !Number.isNaN(Number(opts.page))) {
|
|
118
|
+
params.page = Number(opts.page);
|
|
119
|
+
}
|
|
120
|
+
if (
|
|
121
|
+
opts.pageSize !== undefined &&
|
|
122
|
+
opts.pageSize !== null &&
|
|
123
|
+
!Number.isNaN(Number(opts.pageSize))
|
|
124
|
+
) {
|
|
125
|
+
params.pageSize = Number(opts.pageSize);
|
|
126
|
+
}
|
|
127
|
+
if (opts.filter && String(opts.filter).trim()) {
|
|
128
|
+
params.filter = String(opts.filter).trim();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const listed = await listProtectionManifests(ctx.dataplaneUrl, ctx.authConfig, params);
|
|
132
|
+
if (opts.json) {
|
|
133
|
+
process.stdout.write(
|
|
134
|
+
`${JSON.stringify(listed.raw ?? { data: listed.items, meta: listed.meta }, null, 2)}\n`
|
|
135
|
+
);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
logger.log(
|
|
140
|
+
formatProtectionListTTY(
|
|
141
|
+
{
|
|
142
|
+
items: listed.items,
|
|
143
|
+
meta: listed.meta,
|
|
144
|
+
environment: ctx.environment,
|
|
145
|
+
dataplaneUrl: ctx.dataplaneUrl
|
|
146
|
+
},
|
|
147
|
+
{ verbose: opts.verbose === true }
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @param {string} datasourceKey
|
|
155
|
+
* @param {Object} opts
|
|
156
|
+
* @param {Object} logger
|
|
157
|
+
* @returns {Promise<number>}
|
|
158
|
+
*/
|
|
159
|
+
async function runProtectionShow(datasourceKey, opts, logger) {
|
|
160
|
+
const ctx = await resolveProtectionDataplaneContext(opts);
|
|
161
|
+
const protectionKey = await resolveProtectionKeyForDatasource(datasourceKey, ctx);
|
|
162
|
+
if (!protectionKey) {
|
|
163
|
+
throw new Error(`No deployed protection found for datasource "${datasourceKey}"`);
|
|
164
|
+
}
|
|
165
|
+
const manifest = await getProtection(ctx.dataplaneUrl, ctx.authConfig, protectionKey);
|
|
166
|
+
const status = await getProtectionStatus(ctx.dataplaneUrl, ctx.authConfig, protectionKey);
|
|
167
|
+
const payload = { manifest, status, environment: ctx.environment, dataplaneUrl: ctx.dataplaneUrl };
|
|
168
|
+
if (opts.json) {
|
|
169
|
+
process.stdout.write(JSON.stringify(payload, null, 2));
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
logger.log(formatProtectionShowTTY(payload, { verbose: opts.verbose }));
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {string} datasourceKey
|
|
178
|
+
* @param {Object} opts
|
|
179
|
+
* @param {Object} logger
|
|
180
|
+
* @returns {Promise<number>}
|
|
181
|
+
*/
|
|
182
|
+
async function runProtectionDelete(datasourceKey, opts, logger) {
|
|
183
|
+
const ctx = await resolveProtectionDataplaneContext(opts);
|
|
184
|
+
const protectionKey = await resolveProtectionKeyForDatasource(datasourceKey, ctx);
|
|
185
|
+
if (!protectionKey) {
|
|
186
|
+
throw new Error(`No deployed protection found for datasource "${datasourceKey}"`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!opts.yes) {
|
|
190
|
+
const { confirmed } = await inquirer.prompt([
|
|
191
|
+
{
|
|
192
|
+
type: 'confirm',
|
|
193
|
+
name: 'confirmed',
|
|
194
|
+
message: `Delete protection for '${datasourceKey}' (${protectionKey})?`,
|
|
195
|
+
default: false
|
|
196
|
+
}
|
|
197
|
+
]);
|
|
198
|
+
if (!confirmed) {
|
|
199
|
+
logger.log(chalk.gray('Delete cancelled.'));
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const res = await deleteProtection(ctx.dataplaneUrl, ctx.authConfig, protectionKey);
|
|
205
|
+
logger.log(formatProtectionDeleteSummaryTTY(res?.data || res, datasourceKey));
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = {
|
|
210
|
+
getProtectionRoot,
|
|
211
|
+
runProtectionValidate,
|
|
212
|
+
runProtectionUpload,
|
|
213
|
+
runProtectionList,
|
|
214
|
+
runProtectionShow,
|
|
215
|
+
runProtectionDelete
|
|
216
|
+
};
|