mammoth-cli 2.2.28__tar.gz → 2.2.30__tar.gz
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.
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/PKG-INFO +1 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/__init__.py +1 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +6 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +2 -2
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/automation.py +46 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/batch.py +159 -6
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/project.py +37 -6
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/view.py +84 -14
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/errors/envelope.py +1 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/input_fields.py +4 -11
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/pyproject.toml +1 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/automation.yaml +3 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/batch.yaml +4 -5
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/project.yaml +1 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/view.yaml +1 -1
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/.gitignore +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/LICENSE +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/README.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/__main__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/app.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-style.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/activity.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/addon.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/agent.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/ai.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/annotation.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/auth.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/billing.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/browse.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/calc.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/capability.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/client_app.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/completion.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/config.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/connector.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/context.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/dashboard.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/data_app.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/dataset.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/doctor.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/external_key.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/file.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/folder.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/job.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/link.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/notification.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/parameter.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/registry.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/report.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/schedule.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/schema.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/skill.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/snippet.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/support.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/template.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/trash.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/upgrade.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/user.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/view_ops.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/view_profile.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/webhook.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/workflow.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/commands/workspace.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/context/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/context/credentials.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/context/endpoint.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/context/profiles.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/context/resolver.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/contracts/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/contracts/auth.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/embed.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/errors/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/manifest/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/manifest/loader.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/messages/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/envelope.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/normalize.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/policy.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/progress.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/output/render.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/py.typed +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/confirm.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/dataset_health.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/dryrun.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/dryrun_targets.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/embedded.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/executor.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/input_loader.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/intent_only.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/invocation.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/new_data.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/options.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/parents.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/runlog.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/session.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/state.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/strict.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/updates.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/validate.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/runtime/verify.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/argspec.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/board_values.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/coerce.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/command_contract.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/conditional_format.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/conditions.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/dashboard_filters.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/dashboard_pages.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/dashboard_review.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/data_profile.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/data_quality.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/dispatch.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/factory.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/listing.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/local_time.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/mapping.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/openapi_types.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/positionals.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/protocol.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/read_queries.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/sdk_service.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/sql_check.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/stored_stats.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/testing.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/text_dates.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/type_system.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/services/write_impact.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/skills/__init__.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/skills/installer.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/skills/steering.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/mammoth_cli/testing.py +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/activity.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/addon.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/agent.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/ai.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/annotation.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/auth.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/billing.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/browse.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/calc.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/capability.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/client-app.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/completion.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/config.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/connector.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/context.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/dashboard.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/data-app.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/dataset.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/doctor.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/external-key.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/file.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/folder.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/job.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/link.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/log.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/notification.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/parameter.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/report.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/schedule.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/schema.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/skill.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/snippet.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/support.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/template.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/trash.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/upgrade.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/user.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/version.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/webhook.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/workflow.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/commands/workspace.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/edits-target.source.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/openapi-operations.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/schema-v1.json +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/sdk-catalog.source.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/manifests/sdk-methods.yaml +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/openapi/metadata.json +0 -0
- {mammoth_cli-2.2.28 → mammoth_cli-2.2.30}/spec/openapi/openapi.json +0 -0
|
@@ -141,7 +141,12 @@ No command:
|
|
|
141
141
|
- **Union or append two views.** There is no union task. Upload a file into
|
|
142
142
|
an existing dataset with `append_to_ds_id`, or send a view's rows into an
|
|
143
143
|
existing dataset with `view export dataset` (`target_ds_id` and
|
|
144
|
-
`save_as_mode` `APPEND_TO_DS`).
|
|
144
|
+
`save_as_mode` `APPEND_TO_DS`). Omit `column_mapping` and every column is
|
|
145
|
+
matched by name; a non-empty `column_mapping` is used as-is, so it must map
|
|
146
|
+
every source column to an existing target column. A partial mapping, or a
|
|
147
|
+
destination the target lacks, is refused with `append_mapping_incomplete`
|
|
148
|
+
before anything is written (the backend would append only the mapped columns
|
|
149
|
+
and add a new column for the unknown name). An append is a standing link: it is stored
|
|
145
150
|
as a pipeline step of the source view, so every later re-run of that view
|
|
146
151
|
appends again into the target dataset, not just the first run. A column
|
|
147
152
|
with the same name but a different type in the source view and the target
|
|
@@ -6,7 +6,7 @@ Every command returns the standard JSON envelope. On nonzero exit, read the erro
|
|
|
6
6
|
|
|
7
7
|
Run: `mammoth batch bulk-delete`. Exact input fields: `mammoth schema get batch.bulk-delete`.
|
|
8
8
|
|
|
9
|
-
Example: `mammoth batch bulk-delete 123`. Illustrative only: append `--yes` after observing an owned target.
|
|
9
|
+
Example: `mammoth batch bulk-delete 123 --input '{"ids": [11, 12]}'`. Illustrative only: append `--yes` after observing an owned target.
|
|
10
10
|
|
|
11
11
|
Result: `BatchBulkDeleteResult`; mutation `destructive`, confirmation `prompt_or_yes`, wait policy `not_async`.
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ Status on release: ran once on CLI 2.0.14 — write sweep 2026-09-18: exit 0 on
|
|
|
16
16
|
|
|
17
17
|
Run: `mammoth batch create`. Exact input fields: `mammoth schema get batch.create`.
|
|
18
18
|
|
|
19
|
-
Example: `mammoth batch create 123 123 --input '{"mapping":
|
|
19
|
+
Example: `mammoth batch create 123 123 --input '{"mapping": {"column_1": "column_1"}}'`. Placeholders are illustrative; resolve IDs and input from observed reads.
|
|
20
20
|
|
|
21
21
|
Result: `BatchCreateResult`; mutation `benign_mutation`, confirmation `none`, wait policy `returns_job`.
|
|
22
22
|
|
|
@@ -24,6 +24,7 @@ from mammoth_cli.errors.envelope import (
|
|
|
24
24
|
)
|
|
25
25
|
from mammoth_cli.manifest.loader import command_by_id
|
|
26
26
|
from mammoth_cli.runtime.confirm import (
|
|
27
|
+
POLICY_CONFIRM_TARGET,
|
|
27
28
|
POLICY_PROMPT_OR_YES,
|
|
28
29
|
POLICY_YES_ALWAYS,
|
|
29
30
|
enforce_confirmation,
|
|
@@ -215,6 +216,50 @@ def automation_delete(invocation: Invocation) -> HandlerResult:
|
|
|
215
216
|
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
216
217
|
|
|
217
218
|
|
|
219
|
+
_RETENTION_TASK = "apply_retention_policy"
|
|
220
|
+
_UNATTENDED_DELETE_TARGET = "unattended-delete"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _guard_retention_tasks(invocation: Invocation, tasks: Any) -> Any:
|
|
224
|
+
"""Default retention tasks to ``require_approval`` and gate an unattended delete.
|
|
225
|
+
|
|
226
|
+
The route defaults ``require_approval`` to false, so a retention task with
|
|
227
|
+
the default ``delete`` action would hard-delete batches on every run with no
|
|
228
|
+
one approving. Here an omitted ``require_approval`` becomes true; an explicit
|
|
229
|
+
false with a delete action needs ``--yes --confirm unattended-delete``.
|
|
230
|
+
"""
|
|
231
|
+
if not isinstance(tasks, list):
|
|
232
|
+
return tasks
|
|
233
|
+
guarded: list[Any] = []
|
|
234
|
+
unattended = False
|
|
235
|
+
for task in tasks:
|
|
236
|
+
details = task.get("details") if isinstance(task, dict) else None
|
|
237
|
+
if not (
|
|
238
|
+
isinstance(task, dict)
|
|
239
|
+
and task.get("task_type") == _RETENTION_TASK
|
|
240
|
+
and isinstance(details, dict)
|
|
241
|
+
):
|
|
242
|
+
guarded.append(task)
|
|
243
|
+
elif "require_approval" not in details:
|
|
244
|
+
guarded.append({**task, "details": {**details, "require_approval": True}})
|
|
245
|
+
else:
|
|
246
|
+
unattended = unattended or (
|
|
247
|
+
details["require_approval"] is False and details.get("action", "delete") == "delete"
|
|
248
|
+
)
|
|
249
|
+
guarded.append(task)
|
|
250
|
+
if unattended:
|
|
251
|
+
enforce_confirmation(
|
|
252
|
+
invocation,
|
|
253
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
254
|
+
action=(
|
|
255
|
+
"create a retention policy that DELETES matching batches on every run without "
|
|
256
|
+
"approval (require_approval is false)"
|
|
257
|
+
),
|
|
258
|
+
target=_UNATTENDED_DELETE_TARGET,
|
|
259
|
+
)
|
|
260
|
+
return guarded
|
|
261
|
+
|
|
262
|
+
|
|
218
263
|
def automation_create(invocation: Invocation) -> HandlerResult:
|
|
219
264
|
"""Create an automation. Name comes from a positional or the ``name`` field.
|
|
220
265
|
|
|
@@ -232,7 +277,7 @@ def automation_create(invocation: Invocation) -> HandlerResult:
|
|
|
232
277
|
hint="Pass the name as a positional argument or a 'name' input field.",
|
|
233
278
|
)
|
|
234
279
|
description = document.get("description", "")
|
|
235
|
-
tasks = _require_field(document, "tasks")
|
|
280
|
+
tasks = _guard_retention_tasks(invocation, _require_field(document, "tasks"))
|
|
236
281
|
kwargs: dict[str, Any] = {"name": name, "description": description, "tasks": tasks}
|
|
237
282
|
_forward_optional(document, kwargs, ("conditions", "condition_mode"))
|
|
238
283
|
enforce_confirmation(invocation, policy=POLICY_YES_ALWAYS, action="create an automation")
|
|
@@ -123,6 +123,144 @@ def batch_get(invocation: Invocation) -> HandlerResult:
|
|
|
123
123
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
_DATASET_GET_SYMBOL = "mammoth.api.datasets.DatasetsAPI.get"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _dataset_columns(service: Any, dataset_id: int, side: str) -> list[dict[str, Any]]:
|
|
130
|
+
"""The dataset's own schema rows (``c_name``, ``c_id``, ``c_type``), or raise.
|
|
131
|
+
|
|
132
|
+
An unreadable schema is never treated as "no columns": the mapping check
|
|
133
|
+
could not run, so the batch is not created.
|
|
134
|
+
"""
|
|
135
|
+
response = service.call(_DATASET_GET_SYMBOL, dataset_id=dataset_id)
|
|
136
|
+
dataset = response.get("dataset") if isinstance(response, dict) else None
|
|
137
|
+
schema = dataset.get("data_schema") if isinstance(dataset, dict) else None
|
|
138
|
+
if not isinstance(schema, list) or not all(
|
|
139
|
+
isinstance(col, dict) and isinstance(col.get("c_name"), str) for col in schema
|
|
140
|
+
):
|
|
141
|
+
raise CliError(
|
|
142
|
+
code="append_schema_unreadable",
|
|
143
|
+
message=f"Could not read the {side} dataset {dataset_id}'s schema before the batch.",
|
|
144
|
+
hint=f"Read dataset {dataset_id} before creating the batch.",
|
|
145
|
+
details={"side": side, "dataset_id": dataset_id},
|
|
146
|
+
)
|
|
147
|
+
return schema
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _mapping_items(mapping: Any) -> list[Any]:
|
|
151
|
+
"""Expand a ``{src: dst}`` mapping into ``ColumnNameMapping`` items, untyped."""
|
|
152
|
+
if isinstance(mapping, dict):
|
|
153
|
+
return [{"source_c_name": src, "destination_c_name": dst} for src, dst in mapping.items()]
|
|
154
|
+
return list(mapping) if isinstance(mapping, list) else []
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _resolve_item(
|
|
158
|
+
item: dict[str, Any], source: list[dict[str, Any]], dest: list[dict[str, Any]]
|
|
159
|
+
) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
|
|
160
|
+
"""The (source, destination) schema rows an item names by name or id, else None."""
|
|
161
|
+
by_id = "source_c_id" in item
|
|
162
|
+
field, src_key, dest_key = (
|
|
163
|
+
("c_id", "source_c_id", "destination_c_id")
|
|
164
|
+
if by_id
|
|
165
|
+
else ("c_name", "source_c_name", "destination_c_name")
|
|
166
|
+
)
|
|
167
|
+
src_col = next((col for col in source if col.get(field) == item.get(src_key)), None)
|
|
168
|
+
dest_col = next((col for col in dest if col.get(field) == item.get(dest_key)), None)
|
|
169
|
+
return src_col, dest_col
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _reject_incomplete_batch_mapping(
|
|
173
|
+
ids: tuple[int, int],
|
|
174
|
+
source_names: set[str],
|
|
175
|
+
mapped_sources: set[str],
|
|
176
|
+
unknown: list[str],
|
|
177
|
+
) -> None:
|
|
178
|
+
"""Refuse a mapping that leaves source columns out or names a missing destination."""
|
|
179
|
+
unmapped = sorted(source_names - mapped_sources)
|
|
180
|
+
if not unmapped and not unknown:
|
|
181
|
+
return
|
|
182
|
+
dataset_id, source_id = ids
|
|
183
|
+
problems = []
|
|
184
|
+
if unknown:
|
|
185
|
+
problems.append(f"destination column(s) not in dataset {dataset_id}: {', '.join(unknown)}")
|
|
186
|
+
if unmapped:
|
|
187
|
+
problems.append(f"source column(s) left unmapped and not appended: {', '.join(unmapped)}")
|
|
188
|
+
raise CliError(
|
|
189
|
+
code="append_mapping_incomplete",
|
|
190
|
+
message=(
|
|
191
|
+
f"mapping for appending dataset {source_id} into dataset {dataset_id} would corrupt "
|
|
192
|
+
f"the append: {'; '.join(problems)}. Unmapped source columns are not appended."
|
|
193
|
+
),
|
|
194
|
+
exit_status=EXIT_USAGE,
|
|
195
|
+
hint=(
|
|
196
|
+
f"Map every source column of dataset {source_id} to an existing column of dataset "
|
|
197
|
+
f"{dataset_id}."
|
|
198
|
+
),
|
|
199
|
+
details={"unknown_destinations": unknown, "unmapped_source_columns": unmapped},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _reject_type_change(item: dict[str, Any], dest_col: dict[str, Any], dataset_id: int) -> None:
|
|
204
|
+
"""Refuse an explicit expected type that differs from the destination's real one."""
|
|
205
|
+
expected, actual = item.get("expected_destination_c_type"), dest_col.get("c_type")
|
|
206
|
+
if expected is None or not actual or str(expected).upper() == str(actual).upper():
|
|
207
|
+
return
|
|
208
|
+
raise CliError(
|
|
209
|
+
code="append_type_mismatch",
|
|
210
|
+
message=(
|
|
211
|
+
f"expected_destination_c_type {expected} for destination column "
|
|
212
|
+
f"{dest_col['c_name']!r} of dataset {dataset_id} would change its type from "
|
|
213
|
+
f"{actual}; the append would convert the whole destination column."
|
|
214
|
+
),
|
|
215
|
+
exit_status=EXIT_USAGE,
|
|
216
|
+
hint=(
|
|
217
|
+
"Omit expected_destination_c_type (the CLI sends the destination's current "
|
|
218
|
+
f"type, {actual})."
|
|
219
|
+
),
|
|
220
|
+
details={"column": dest_col["c_name"], "expected": expected, "actual": actual},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _checked_batch_mapping(
|
|
225
|
+
service: Any, dataset_id: int, source_id: int, mapping: Any
|
|
226
|
+
) -> tuple[list[dict[str, Any]], list[str]]:
|
|
227
|
+
"""Validate a batch mapping against both schemas; return typed items and warnings.
|
|
228
|
+
|
|
229
|
+
Refuses unmapped source columns and unknown destinations (the backend appends
|
|
230
|
+
only the mapped columns) and stamps every item with the destination's actual
|
|
231
|
+
current type, so no destination column is silently re-typed. A mapped pair
|
|
232
|
+
whose source and destination types differ is returned as a warning.
|
|
233
|
+
"""
|
|
234
|
+
source = _dataset_columns(service, source_id, "source")
|
|
235
|
+
dest = _dataset_columns(service, dataset_id, "target")
|
|
236
|
+
items = [dict(item) for item in _mapping_items(mapping) if isinstance(item, dict)]
|
|
237
|
+
typed: list[dict[str, Any]] = []
|
|
238
|
+
mapped_sources: set[str] = set()
|
|
239
|
+
unknown: list[str] = []
|
|
240
|
+
warnings: list[str] = []
|
|
241
|
+
for item in items:
|
|
242
|
+
src_col, dest_col = _resolve_item(item, source, dest)
|
|
243
|
+
if src_col is not None:
|
|
244
|
+
mapped_sources.add(src_col["c_name"])
|
|
245
|
+
if dest_col is None:
|
|
246
|
+
if item.get("action") != "add_column":
|
|
247
|
+
unknown.append(str(item.get("destination_c_name", item.get("destination_c_id"))))
|
|
248
|
+
typed.append(item)
|
|
249
|
+
continue
|
|
250
|
+
_reject_type_change(item, dest_col, dataset_id)
|
|
251
|
+
item.setdefault("expected_destination_c_type", dest_col.get("c_type"))
|
|
252
|
+
typed.append(item)
|
|
253
|
+
if src_col and src_col.get("c_type") and src_col["c_type"] != dest_col.get("c_type"):
|
|
254
|
+
warnings.append(
|
|
255
|
+
f"mapped column {src_col['c_name']!r} is {str(src_col['c_type']).lower()} but "
|
|
256
|
+
f"destination column {dest_col['c_name']!r} is {str(dest_col['c_type']).lower()}"
|
|
257
|
+
)
|
|
258
|
+
_reject_incomplete_batch_mapping(
|
|
259
|
+
(dataset_id, source_id), {col["c_name"] for col in source}, mapped_sources, unknown
|
|
260
|
+
)
|
|
261
|
+
return typed, warnings
|
|
262
|
+
|
|
263
|
+
|
|
126
264
|
def batch_create(invocation: Invocation) -> HandlerResult:
|
|
127
265
|
"""Create a batch for a dataset. ``source_id`` and ``mapping`` are required."""
|
|
128
266
|
project_id = require_project(invocation)
|
|
@@ -158,7 +296,12 @@ def batch_create(invocation: Invocation) -> HandlerResult:
|
|
|
158
296
|
action=f"create a batch on dataset {dataset_id} and DELETE the source dataset",
|
|
159
297
|
)
|
|
160
298
|
with open_service(invocation) as (service, auth):
|
|
299
|
+
kwargs["mapping"], warnings = _checked_batch_mapping(
|
|
300
|
+
service, dataset_id, source_id, mapping
|
|
301
|
+
)
|
|
161
302
|
data = service.call(_symbol(invocation), **kwargs)
|
|
303
|
+
if warnings and isinstance(data, dict):
|
|
304
|
+
data["mapping_check"] = {"warnings": warnings}
|
|
162
305
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
163
306
|
|
|
164
307
|
|
|
@@ -249,20 +392,30 @@ def batch_delete(invocation: Invocation) -> HandlerResult:
|
|
|
249
392
|
|
|
250
393
|
|
|
251
394
|
def batch_bulk_delete(invocation: Invocation) -> HandlerResult:
|
|
252
|
-
"""Bulk-delete batches
|
|
395
|
+
"""Bulk-delete the named batches of a dataset. Prompt or ``--yes`` required.
|
|
253
396
|
|
|
254
|
-
|
|
397
|
+
``ids`` is required: the backend rejects a delete with no batch ids, it
|
|
398
|
+
never deletes "all batches".
|
|
255
399
|
"""
|
|
256
400
|
project_id = require_project(invocation)
|
|
257
401
|
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
258
402
|
document = invocation.load_input() or {}
|
|
403
|
+
ids = _require_field(document, "ids")
|
|
404
|
+
if not ids:
|
|
405
|
+
raise CliError(
|
|
406
|
+
code=CODE_INVALID_ARGUMENT,
|
|
407
|
+
message="The 'ids' input field must name at least one batch id.",
|
|
408
|
+
exit_status=EXIT_USAGE,
|
|
409
|
+
hint=f"List batch ids with `mammoth batch list {dataset_id}`.",
|
|
410
|
+
)
|
|
411
|
+
count = len(ids) if isinstance(ids, list) else len(str(ids).split(","))
|
|
259
412
|
enforce_confirmation(
|
|
260
413
|
invocation,
|
|
261
414
|
policy=POLICY_PROMPT_OR_YES,
|
|
262
|
-
action=f"delete
|
|
415
|
+
action=f"delete {count} batch(es) of dataset {dataset_id}",
|
|
263
416
|
)
|
|
264
|
-
kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
|
|
265
|
-
_forward_optional(document, kwargs, ("ids",))
|
|
266
417
|
with open_service(invocation) as (service, auth):
|
|
267
|
-
data = service.call(
|
|
418
|
+
data = service.call(
|
|
419
|
+
_symbol(invocation), dataset_id=dataset_id, project_id=project_id, ids=ids
|
|
420
|
+
)
|
|
268
421
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
@@ -505,23 +505,54 @@ def project_data_check_list(invocation: Invocation) -> HandlerResult:
|
|
|
505
505
|
return _list_with_filters(invocation)
|
|
506
506
|
|
|
507
507
|
|
|
508
|
+
_PROJECT_MEMBERS_SYMBOL = "mammoth.api.workspace.WorkspaceAPI.list_users"
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def _reject_existing_members(service: Any, project_id: int, user_ids: Any) -> None:
|
|
512
|
+
"""Refuse an add naming a user who is already a member of the project.
|
|
513
|
+
|
|
514
|
+
The backend treats adding an existing member as a role replacement (it
|
|
515
|
+
revokes the current role, then assigns the given one), so an add can
|
|
516
|
+
silently demote a project admin. Role changes belong to ``project user
|
|
517
|
+
update``.
|
|
518
|
+
"""
|
|
519
|
+
members = service.call(_PROJECT_MEMBERS_SYMBOL, project_id=project_id)
|
|
520
|
+
member_ids = {str(m.get("id")) for m in members if isinstance(m, dict)}
|
|
521
|
+
existing = [uid for uid in user_ids if str(uid) in member_ids]
|
|
522
|
+
if existing:
|
|
523
|
+
raise CliError(
|
|
524
|
+
code=CODE_INVALID_ARGUMENT,
|
|
525
|
+
message=(
|
|
526
|
+
f"User(s) {', '.join(str(uid) for uid in existing)} already belong to project "
|
|
527
|
+
f"{project_id}; adding them again would replace their current role."
|
|
528
|
+
),
|
|
529
|
+
exit_status=EXIT_USAGE,
|
|
530
|
+
hint="Use `mammoth project user update` to change a member's role.",
|
|
531
|
+
details={"existing_user_ids": existing, "project_id": project_id},
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
508
535
|
def project_user_add(invocation: Invocation) -> HandlerResult:
|
|
509
|
-
"""Add users to a project. High-impact: ``--yes --confirm PROJECT_ID``.
|
|
536
|
+
"""Add users to a project. High-impact: ``--yes --confirm PROJECT_ID``.
|
|
537
|
+
|
|
538
|
+
``role`` is required and no listed user may already be a member (the
|
|
539
|
+
backend would replace that member's role).
|
|
540
|
+
"""
|
|
510
541
|
project_id = _project_id(invocation)
|
|
511
542
|
document = invocation.load_input()
|
|
512
543
|
user_ids = _require_input_field(document, "user_ids")
|
|
544
|
+
role = _require_input_field(document, "role")
|
|
513
545
|
enforce_confirmation(
|
|
514
546
|
invocation,
|
|
515
547
|
policy=POLICY_CONFIRM_TARGET,
|
|
516
548
|
action=f"add users to project {project_id}",
|
|
517
549
|
target=str(project_id),
|
|
518
550
|
)
|
|
519
|
-
kwargs: dict[str, Any] = {"project_id": project_id, "user_ids": user_ids}
|
|
520
|
-
assert document is not None
|
|
521
|
-
if "role" in document:
|
|
522
|
-
kwargs["role"] = document["role"]
|
|
523
551
|
with open_service(invocation) as (service, auth):
|
|
524
|
-
|
|
552
|
+
_reject_existing_members(service, project_id, user_ids)
|
|
553
|
+
data = service.call(
|
|
554
|
+
_symbol(invocation), project_id=project_id, user_ids=user_ids, role=role
|
|
555
|
+
)
|
|
525
556
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
526
557
|
|
|
527
558
|
|
|
@@ -3545,6 +3545,7 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
|
|
|
3545
3545
|
_reject_export_into_draft(service, dataview_id, dataset_id)
|
|
3546
3546
|
target_view_before = None
|
|
3547
3547
|
target_only_columns: list[str] = []
|
|
3548
|
+
mapped_type_warnings: list[str] = []
|
|
3548
3549
|
if target_ds_id is not None:
|
|
3549
3550
|
# Each `view export dataset` call creates its own PERSISTENT
|
|
3550
3551
|
# export trigger on this view, which re-runs on every pipeline
|
|
@@ -3578,7 +3579,7 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
|
|
|
3578
3579
|
# before the call so a later re-read can never be confused with it.
|
|
3579
3580
|
target_view_before = _dataset_view_info(service, int(target_ds_id), result_project_id)
|
|
3580
3581
|
if save_as_mode == "APPEND_TO_DS":
|
|
3581
|
-
target_only_columns = _reject_append_schema_mismatch(
|
|
3582
|
+
target_only_columns, mapped_type_warnings = _reject_append_schema_mismatch(
|
|
3582
3583
|
service,
|
|
3583
3584
|
dataview_id,
|
|
3584
3585
|
dataset_id,
|
|
@@ -3640,13 +3641,17 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
|
|
|
3640
3641
|
# An append is expected to grow the target; verify.py treats
|
|
3641
3642
|
# a non-increase here as unverified, not just an omitted count.
|
|
3642
3643
|
row_check["expected_row_increase"] = True
|
|
3644
|
+
warnings = list(mapped_type_warnings)
|
|
3643
3645
|
if target_only_columns:
|
|
3644
3646
|
# Allowed -- an append never has to cover every target column
|
|
3645
3647
|
# -- but worth surfacing rather than leaving silent.
|
|
3646
|
-
|
|
3648
|
+
warnings.insert(
|
|
3649
|
+
0,
|
|
3647
3650
|
"target dataset has column(s) the source view does not (kept "
|
|
3648
|
-
"as-is): " + ", ".join(target_only_columns)
|
|
3649
|
-
|
|
3651
|
+
"as-is): " + ", ".join(target_only_columns),
|
|
3652
|
+
)
|
|
3653
|
+
if warnings:
|
|
3654
|
+
row_check["warnings"] = warnings
|
|
3650
3655
|
data["row_check"] = row_check
|
|
3651
3656
|
if pipeline_error is not None:
|
|
3652
3657
|
data["pipeline_error"] = pipeline_error
|
|
@@ -3799,24 +3804,92 @@ def _reject_append_type_mismatch(
|
|
|
3799
3804
|
)
|
|
3800
3805
|
|
|
3801
3806
|
|
|
3807
|
+
def _reject_partial_append_mapping(
|
|
3808
|
+
source_names: set[str],
|
|
3809
|
+
target_names: set[str],
|
|
3810
|
+
mapping: dict[str, str],
|
|
3811
|
+
ids: tuple[int, int],
|
|
3812
|
+
) -> None:
|
|
3813
|
+
"""Refuse a non-empty append ``column_mapping`` that drops or invents columns.
|
|
3814
|
+
|
|
3815
|
+
The backend maps every column by name when the mapping is empty, but uses
|
|
3816
|
+
a non-empty one as-is: unmapped source columns are not appended (their
|
|
3817
|
+
target cells stay NULL) and a destination the target lacks becomes a new
|
|
3818
|
+
column named after the source.
|
|
3819
|
+
"""
|
|
3820
|
+
if not mapping:
|
|
3821
|
+
return
|
|
3822
|
+
unknown = {src: dest for src, dest in sorted(mapping.items()) if dest not in target_names}
|
|
3823
|
+
unmapped = sorted(source_names - set(mapping))
|
|
3824
|
+
if not unknown and not unmapped:
|
|
3825
|
+
return
|
|
3826
|
+
view_id, target_ds_id = ids
|
|
3827
|
+
problems = []
|
|
3828
|
+
if unknown:
|
|
3829
|
+
listed = ", ".join(f"{dest!r} (for {src!r})" for src, dest in unknown.items())
|
|
3830
|
+
problems.append(f"destination column(s) not in target dataset {target_ds_id}: {listed}")
|
|
3831
|
+
if unmapped:
|
|
3832
|
+
problems.append(f"source column(s) left unmapped and not appended: {', '.join(unmapped)}")
|
|
3833
|
+
raise CliError(
|
|
3834
|
+
code="append_mapping_incomplete",
|
|
3835
|
+
message=(
|
|
3836
|
+
f"column_mapping for appending view {view_id} into dataset {target_ds_id} would "
|
|
3837
|
+
f"corrupt the append: {'; '.join(problems)}. A non-empty column_mapping is used "
|
|
3838
|
+
"as-is: unmapped columns are not appended and an unknown destination creates a "
|
|
3839
|
+
"new column."
|
|
3840
|
+
),
|
|
3841
|
+
exit_status=EXIT_USAGE,
|
|
3842
|
+
hint=(
|
|
3843
|
+
"Omit column_mapping to map all columns by name, or map every source column to "
|
|
3844
|
+
f"an existing column of dataset {target_ds_id}."
|
|
3845
|
+
),
|
|
3846
|
+
details={
|
|
3847
|
+
"unknown_destinations": unknown,
|
|
3848
|
+
"unmapped_source_columns": unmapped,
|
|
3849
|
+
"target_columns": sorted(target_names),
|
|
3850
|
+
},
|
|
3851
|
+
)
|
|
3852
|
+
|
|
3853
|
+
|
|
3854
|
+
def _mapped_type_warnings(
|
|
3855
|
+
source_types: Any, target_schema: dict[str, str], mapping: dict[str, str]
|
|
3856
|
+
) -> list[str]:
|
|
3857
|
+
"""Warn where a mapped source column's type differs from its target column's."""
|
|
3858
|
+
if not isinstance(source_types, dict):
|
|
3859
|
+
return []
|
|
3860
|
+
return [
|
|
3861
|
+
f"mapped column {src!r} is {str(source_types[src]).lower()} but target column "
|
|
3862
|
+
f"{dest!r} is {target_schema[dest].lower()}"
|
|
3863
|
+
for src, dest in sorted(mapping.items())
|
|
3864
|
+
if source_types.get(src)
|
|
3865
|
+
and target_schema.get(dest)
|
|
3866
|
+
and str(source_types[src]).lower() != target_schema[dest].lower()
|
|
3867
|
+
]
|
|
3868
|
+
|
|
3869
|
+
|
|
3802
3870
|
def _reject_append_schema_mismatch(
|
|
3803
3871
|
service: Any,
|
|
3804
3872
|
dataview_id: int,
|
|
3805
3873
|
dataset_id: int | None,
|
|
3806
3874
|
target_ds_id: int,
|
|
3807
3875
|
column_mapping: Any,
|
|
3808
|
-
) -> list[str]:
|
|
3876
|
+
) -> tuple[list[str], list[str]]:
|
|
3809
3877
|
"""Refuse an ``APPEND_TO_DS`` export whose source has a column the target
|
|
3810
3878
|
dataset's schema does not, and ``column_mapping`` does not cover.
|
|
3811
3879
|
|
|
3880
|
+
A non-empty ``column_mapping`` is stricter: the backend then copies only
|
|
3881
|
+
the mapped columns and turns an unknown destination into a new column, so
|
|
3882
|
+
every source column must be mapped to a column the target already has
|
|
3883
|
+
(``_reject_partial_append_mapping``).
|
|
3884
|
+
|
|
3812
3885
|
An append writes into rows the target dataset already has; a source
|
|
3813
3886
|
column with no home in the target schema and no explicit mapping would
|
|
3814
3887
|
otherwise reach the backend as a malformed write. Checked here, before
|
|
3815
3888
|
the call, so the refusal costs nothing and names exactly what to fix.
|
|
3816
3889
|
|
|
3817
3890
|
Returns the target-only column names (present in the target schema, not
|
|
3818
|
-
in the source, not a ``column_mapping`` destination)
|
|
3819
|
-
worth a warning
|
|
3891
|
+
in the source, not a ``column_mapping`` destination) and the type
|
|
3892
|
+
warnings for mapped pairs -- both allowed, but worth a warning.
|
|
3820
3893
|
"""
|
|
3821
3894
|
source_columns = service.call_view(dataview_id, "columns", dataset_id=dataset_id)
|
|
3822
3895
|
if not isinstance(source_columns, dict):
|
|
@@ -3833,14 +3906,11 @@ def _reject_append_schema_mismatch(
|
|
|
3833
3906
|
# dict keys are display names, matching what the backend's own schema
|
|
3834
3907
|
# match compares by -- never the internal ids in the dict's values.
|
|
3835
3908
|
source_names = set(source_columns)
|
|
3909
|
+
_reject_partial_append_mapping(source_names, target_names, mapping, (dataview_id, target_ds_id))
|
|
3836
3910
|
source_only = sorted(source_names - target_names - set(mapping))
|
|
3837
3911
|
target_only = sorted(target_names - source_names - set(mapping.values()))
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
target_schema,
|
|
3841
|
-
mapping,
|
|
3842
|
-
(dataview_id, target_ds_id),
|
|
3843
|
-
)
|
|
3912
|
+
source_types = service.call_view(dataview_id, "column_types", dataset_id=dataset_id)
|
|
3913
|
+
_reject_append_type_mismatch(source_types, target_schema, mapping, (dataview_id, target_ds_id))
|
|
3844
3914
|
if source_only:
|
|
3845
3915
|
raise CliError(
|
|
3846
3916
|
code="append_schema_mismatch",
|
|
@@ -3855,7 +3925,7 @@ def _reject_append_schema_mismatch(
|
|
|
3855
3925
|
),
|
|
3856
3926
|
details={"source_only": source_only, "target_only": target_only},
|
|
3857
3927
|
)
|
|
3858
|
-
return target_only
|
|
3928
|
+
return target_only, _mapped_type_warnings(source_types, target_schema, mapping)
|
|
3859
3929
|
|
|
3860
3930
|
|
|
3861
3931
|
def _dataset_view_info(
|
|
@@ -77,6 +77,7 @@ ERROR_SUMMARIES: dict[str, str] = {
|
|
|
77
77
|
"More than one item matched, so the assistant needs to be more specific."
|
|
78
78
|
),
|
|
79
79
|
"api_error": "Mammoth couldn't complete this step.",
|
|
80
|
+
"append_mapping_incomplete": "The column mapping would leave columns out or add new ones.",
|
|
80
81
|
"append_schema_mismatch": "The new data doesn't line up with the existing columns.",
|
|
81
82
|
"append_type_mismatch": "A column has a different type than in the existing data.",
|
|
82
83
|
"append_schema_unreadable": "The columns of the existing data couldn't be read.",
|
|
@@ -236,6 +236,7 @@ _EXAMPLE_INPUT_HINTS: dict[str, dict[str, Any]] = {
|
|
|
236
236
|
},
|
|
237
237
|
"view.conditional-format.delete-all": {"rule_id": "bca0ff33bd6f8ed1"},
|
|
238
238
|
"dataset.bulk-delete": {"dataset_ids": [456, 457]},
|
|
239
|
+
"batch.bulk-delete": {"ids": [11, 12]},
|
|
239
240
|
"folder.move": {"resource_ids": [8024], "target_folder_resource_id": "root"},
|
|
240
241
|
"project.update": {"name": "Renamed project"},
|
|
241
242
|
# ProjectsPatch: role changes across explicitly named projects.
|
|
@@ -267,17 +268,9 @@ _EXAMPLE_INPUT_HINTS: dict[str, dict[str, Any]] = {
|
|
|
267
268
|
"view.export.publish-db-update": {
|
|
268
269
|
"patch": [{"op": "replace", "path": "credentials", "value": {"odbc_type": "postgres"}}],
|
|
269
270
|
},
|
|
270
|
-
# BatchesPostRequest
|
|
271
|
-
#
|
|
272
|
-
"batch.create": {
|
|
273
|
-
"mapping": [
|
|
274
|
-
{
|
|
275
|
-
"source_c_name": "column_1",
|
|
276
|
-
"destination_c_name": "column_1",
|
|
277
|
-
"expected_destination_c_type": "TEXT",
|
|
278
|
-
}
|
|
279
|
-
]
|
|
280
|
-
},
|
|
271
|
+
# BatchesPostRequest items carry the expected destination type; the CLI
|
|
272
|
+
# fills it from the destination's real schema, so the example omits it.
|
|
273
|
+
"batch.create": {"mapping": {"column_1": "column_1"}},
|
|
281
274
|
# The SQL task reads the view as the quoted table "view:<id>" (or its
|
|
282
275
|
# quoted display name); placeholder names such as ``data`` are rejected.
|
|
283
276
|
"view.transform.add-sql": {
|
|
@@ -82,7 +82,9 @@ commands:
|
|
|
82
82
|
task, trigger, option and file format in and out (PDF included). Task models can pull cloud files and send email;
|
|
83
83
|
requires connector/email allowlists and dedicated fixtures. Delete old rows on a schedule with
|
|
84
84
|
task apply_retention_policy (datasource_id, rule_type time_based, count_based or
|
|
85
|
-
condition_based).
|
|
85
|
+
condition_based). The CLI sets require_approval=true on a retention task that omits it (the route
|
|
86
|
+
defaults to false, an unattended hard delete); require_approval=false with the delete action needs
|
|
87
|
+
--yes --confirm unattended-delete. An emailed file is task send_an_alert with attachments.dataview_ids: each
|
|
86
88
|
selected view is emailed as a CSV file. The combined row count of all attached views must stay
|
|
87
89
|
within 100,000 -- over that, the automation is refused (and a run that grows past it fails).'
|
|
88
90
|
reviewed_by: primary
|
|
@@ -30,7 +30,7 @@ commands:
|
|
|
30
30
|
required_fixture_guard: null
|
|
31
31
|
secret_fields: []
|
|
32
32
|
human_example: mammoth batch bulk-delete DATASET_ID --help
|
|
33
|
-
agent_example: mammoth batch bulk-delete 123
|
|
33
|
+
agent_example: 'mammoth batch bulk-delete 123 --input ''{"ids": [11, 12]}'''
|
|
34
34
|
unit_tests:
|
|
35
35
|
- UT-BATCH-BULK-DELETE
|
|
36
36
|
contract_tests:
|
|
@@ -39,7 +39,7 @@ commands:
|
|
|
39
39
|
- CT-BATCH-BULK-DELETE-ERROR
|
|
40
40
|
draft_test: null
|
|
41
41
|
undo_test: null
|
|
42
|
-
known_restrictions:
|
|
42
|
+
known_restrictions: ids is required (the backend rejects a delete with no batch ids; it never deletes all batches). List ids with batch list.
|
|
43
43
|
reviewed_by: primary
|
|
44
44
|
readback:
|
|
45
45
|
command: batch.list
|
|
@@ -82,8 +82,7 @@ commands:
|
|
|
82
82
|
required_fixture_guard: null
|
|
83
83
|
secret_fields: []
|
|
84
84
|
human_example: mammoth batch create DATASET_ID SOURCE_ID --help
|
|
85
|
-
agent_example: 'mammoth batch create 123 123 --input ''{"mapping":
|
|
86
|
-
"column_1", "expected_destination_c_type": "TEXT"}]}'''
|
|
85
|
+
agent_example: 'mammoth batch create 123 123 --input ''{"mapping": {"column_1": "column_1"}}'''
|
|
87
86
|
unit_tests:
|
|
88
87
|
- UT-BATCH-CREATE
|
|
89
88
|
contract_tests:
|
|
@@ -92,7 +91,7 @@ commands:
|
|
|
92
91
|
- CT-BATCH-CREATE-ERROR
|
|
93
92
|
draft_test: null
|
|
94
93
|
undo_test: null
|
|
95
|
-
known_restrictions: SOURCE_ID is a standalone dataset in the same project (4BATC010 INVALID_SOURCE_DATASET for a file id or an append child); to append a file use file upload with append_to_ds_id. delete_source_ds=true changes the mutation class to destructive and requires confirmation.
|
|
94
|
+
known_restrictions: Every source column must be mapped to an existing destination column; the CLI reads both schemas and refuses (append_mapping_incomplete) an unmapped source column or a destination the dataset lacks, because the backend appends only mapped columns. The CLI sends each item's expected_destination_c_type as the destination's current type (never re-types it; an explicit different value is refused with append_type_mismatch) and warns in mapping_check.warnings when source and destination types differ. SOURCE_ID is a standalone dataset in the same project (4BATC010 INVALID_SOURCE_DATASET for a file id or an append child); to append a file use file upload with append_to_ds_id. delete_source_ds=true changes the mutation class to destructive and requires confirmation.
|
|
96
95
|
reviewed_by: primary
|
|
97
96
|
readback:
|
|
98
97
|
command: batch.list
|
|
@@ -933,7 +933,7 @@ commands:
|
|
|
933
933
|
- CT-PROJECT-USER-ADD-ERROR
|
|
934
934
|
draft_test: null
|
|
935
935
|
undo_test: null
|
|
936
|
-
known_restrictions: 'B03 PROJECT_USER_ID_ROLE_UNTYPED. Takes existing workspace users'' ids. To add
|
|
936
|
+
known_restrictions: 'B03 PROJECT_USER_ID_ROLE_UNTYPED. role is required, and a user who is already a project member is refused (the backend would replace that member''s role, demoting an admin); change roles with project user update. Takes existing workspace users'' ids. To add
|
|
937
937
|
someone by email who is not in the workspace yet, use workspace user add with
|
|
938
938
|
{"email_ids": [EMAIL], "projects": [{"project_id": PROJECT_ID, "role": ROLE}]}: it invites them as a workspace member
|
|
939
939
|
with that project role, which is how a project gets a new person.'
|