datex-studio-cli 0.4.15__tar.gz → 0.4.16__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.
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/PKG-INFO +1 -1
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/pyproject.toml +1 -1
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/__init__.py +1 -1
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/auth.py +59 -24
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/crm.py +34 -128
- datex_studio_cli-0.4.16/src/dxs/commands/crm_delivery.py +1110 -0
- datex_studio_cli-0.4.16/src/dxs/commands/crm_time.py +915 -0
- datex_studio_cli-0.4.16/src/dxs/core/auth/tenants.py +164 -0
- datex_studio_cli-0.4.16/src/dxs/core/dynamics/html.py +33 -0
- datex_studio_cli-0.4.16/src/dxs/core/dynamics/resolvers.py +160 -0
- datex_studio_cli-0.4.16/src/dxs/core/dynamics/time_entry.py +148 -0
- datex_studio_cli-0.4.15/src/dxs/core/auth/tenants.py +0 -71
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/.gitignore +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/README.md +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/report-creator.skill +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/scripts/analyze_corpus.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/scripts/generate_rdlx_schema.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/__main__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/cli.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/api.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/branch.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/cascade.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/config.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/configuration.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/datasource.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/devops.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/document.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/endpoint.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/env.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/explore.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/function.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/init.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/marketplace.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/_common.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/api.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/create.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/data.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/pack.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/ng/preview.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/odata.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/organization.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/proxy.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/reference.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/release_notes.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/repo.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/add_cmds.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/api.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/batch.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/create.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/data.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/dataset.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/edit.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/folder.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/preview.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/report/schema_cmds.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/reset.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/schema.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/servicepack.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/source.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/studio.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/telemetry.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/commands/user.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/context.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/app_config.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/client.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/endpoints.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/metadata_models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/api/reference_graph.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/auth/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/auth/datex_tenant.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/auth/decorators.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/auth/msal_client.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/auth/token_cache.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/cache.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/flow_generator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/flow_models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/generator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/parsers.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/resolver.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/type_def_parser.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/datasource/validator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/designer/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/designer/flow_models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/designer/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/designer/parsers.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/devops/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/devops/client.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/devops/helpers.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/devops/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/dynamics/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/dynamics/client.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/dynamics/metadata_cache.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/footprint/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/footprint/client.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/footprint/edmx_parser.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/footprint/metadata.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/function/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/function/generator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/function/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/function/validator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/graph.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/onboarding.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/csv_fmt.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/formatter.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/json_fmt.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/redaction.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/output/yaml_fmt.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/release_notes.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/responses.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/config.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/identity.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/recorder.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/scrubber.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/core/telemetry/sender.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/models/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/folder.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/manifest.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/mocks.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/preview.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/ng/scaffold.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/py.typed +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/datasource_binding.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/engine.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/field_parser.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/folder.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/manifest.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/models.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/preview.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/preview_arjs.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/schema.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/svg_renderer.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/templates/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/validate_arjs.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/validator.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/report/wrapper.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/click_options.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/clipboard.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/config.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/errors.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/filtering.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/image.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/paths.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/resolvers.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/responses.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/restricted.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/sorting.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/update_check.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/utils/versioning.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/__init__.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/app.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/design.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/proxy_app.py +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/404/index.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/404.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/design/capture/index.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/design/capture/index.txt +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/design/index.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/design/index.txt +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/index.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/index.txt +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/render-cli.js +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/report-preview.html +0 -0
- {datex_studio_cli-0.4.15 → datex_studio_cli-0.4.16}/src/dxs/web/static/report-validate.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datex-studio-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.16
|
|
4
4
|
Summary: CLI for Datex Studio low-code platform, designed for LLM-based AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/datex/datex-studio-cli
|
|
6
6
|
Project-URL: Documentation, https://github.com/datex/datex-studio-cli
|
|
@@ -15,7 +15,7 @@ from dxs.core.auth.decorators import get_access_token
|
|
|
15
15
|
from dxs.core.auth.tenants import org_needs_tenant_login, tenant_authority_for
|
|
16
16
|
from dxs.core.auth.token_cache import CachedIdentity, get_identity_override
|
|
17
17
|
from dxs.core.footprint import get_footprint_token
|
|
18
|
-
from dxs.utils.errors import AuthenticationError, ValidationError
|
|
18
|
+
from dxs.utils.errors import AuthenticationError, DxsError, ValidationError
|
|
19
19
|
from dxs.utils.responses import list_response, single
|
|
20
20
|
from dxs.utils.restricted import restrict_in_restricted_mode
|
|
21
21
|
|
|
@@ -255,8 +255,12 @@ def auth() -> None:
|
|
|
255
255
|
default=None,
|
|
256
256
|
help="Footprint API app registration client secret (for daemon auth)",
|
|
257
257
|
)
|
|
258
|
-
@click.option("--tenant-id", default=None, help="
|
|
259
|
-
@click.option(
|
|
258
|
+
@click.option("--tenant-id", default=None, help="Entra tenant ID (customer self-service)")
|
|
259
|
+
@click.option(
|
|
260
|
+
"--domain",
|
|
261
|
+
default=None,
|
|
262
|
+
help="Entra domain (e.g. csw-wi.com) — resolves the tenant ID for you",
|
|
263
|
+
)
|
|
260
264
|
@pass_context
|
|
261
265
|
def login(
|
|
262
266
|
ctx: DxsContext,
|
|
@@ -275,8 +279,11 @@ def login(
|
|
|
275
279
|
If you are already authenticated with a different identity, this will
|
|
276
280
|
add a new identity and set it as active (use 'dxs auth switch' to change).
|
|
277
281
|
|
|
278
|
-
|
|
279
|
-
|
|
282
|
+
Name an organization to authenticate against its own Entra tenant, looked up
|
|
283
|
+
from the API. On a machine with no credentials that lookup is impossible, so
|
|
284
|
+
pass --domain instead: it is resolved against Entra's public discovery
|
|
285
|
+
document and needs no prior authentication. --tenant-id does the same when
|
|
286
|
+
you already know the GUID.
|
|
280
287
|
|
|
281
288
|
Use --client-id and --client-secret together to configure daemon (client
|
|
282
289
|
credentials) authentication for the Footprint API, needed for cross-tenant
|
|
@@ -284,8 +291,10 @@ def login(
|
|
|
284
291
|
|
|
285
292
|
\b
|
|
286
293
|
Examples:
|
|
287
|
-
dxs auth login
|
|
288
|
-
dxs auth login CAG
|
|
294
|
+
dxs auth login # Login to default tenant
|
|
295
|
+
dxs auth login CAG # Login to CAG's tenant (needs an identity)
|
|
296
|
+
dxs auth login --domain csw-wi.com # Bootstrap: no identity required
|
|
297
|
+
dxs auth login --tenant-id <guid> # Same, when you know the GUID
|
|
289
298
|
dxs auth login CAG --client-id <id> --client-secret <secret>
|
|
290
299
|
"""
|
|
291
300
|
_reject_tenant_override(ctx, "login")
|
|
@@ -304,20 +313,33 @@ def login(
|
|
|
304
313
|
)
|
|
305
314
|
raise SystemExit(1)
|
|
306
315
|
|
|
307
|
-
if
|
|
316
|
+
if org and (tenant_id or domain):
|
|
308
317
|
ctx.output_error(
|
|
309
318
|
ValidationError(
|
|
310
|
-
message="Pass either an organization name or --tenant-id, not both",
|
|
319
|
+
message="Pass either an organization name or --domain/--tenant-id, not both",
|
|
311
320
|
code="DXS-AUTH-017",
|
|
312
321
|
suggestions=[
|
|
313
|
-
"dxs auth login <org>
|
|
314
|
-
"dxs auth login --
|
|
322
|
+
"dxs auth login <org> # Datex staff / known org",
|
|
323
|
+
"dxs auth login --domain <domain> # customer self-service",
|
|
324
|
+
"dxs auth login --tenant-id <guid> # when you know the GUID",
|
|
315
325
|
],
|
|
316
326
|
)
|
|
317
327
|
)
|
|
318
328
|
raise SystemExit(1)
|
|
319
329
|
|
|
320
|
-
if tenant_id:
|
|
330
|
+
if tenant_id or domain:
|
|
331
|
+
# Bootstrap path: no API lookup, so it works with an empty credentials
|
|
332
|
+
# cache. An explicit GUID wins, making the domain lookup free to skip.
|
|
333
|
+
if not tenant_id:
|
|
334
|
+
from dxs.core.auth.tenants import normalize_entra_domain, resolve_tenant_id_from_domain
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
tenant_id = resolve_tenant_id_from_domain(domain) # type: ignore[arg-type]
|
|
338
|
+
except DxsError as e:
|
|
339
|
+
ctx.output_error(e)
|
|
340
|
+
raise SystemExit(1) from None
|
|
341
|
+
# Store/display the bare domain, never the sign-in address it came from.
|
|
342
|
+
domain = normalize_entra_domain(domain) # type: ignore[arg-type]
|
|
321
343
|
|
|
322
344
|
def on_device_code_external(device_code: dict[str, Any]) -> None:
|
|
323
345
|
ctx.output(
|
|
@@ -873,12 +895,12 @@ def switch(
|
|
|
873
895
|
@click.option(
|
|
874
896
|
"--tenant-id",
|
|
875
897
|
default=None,
|
|
876
|
-
help="
|
|
898
|
+
help="Entra tenant ID — self-service; skips the organization lookup",
|
|
877
899
|
)
|
|
878
900
|
@click.option(
|
|
879
901
|
"--domain",
|
|
880
902
|
default=None,
|
|
881
|
-
help="
|
|
903
|
+
help="Entra domain (e.g. csw-wi.com) — resolves the tenant ID for you",
|
|
882
904
|
)
|
|
883
905
|
@click.option("--open/--no-open", default=True, help="Open URL in browser (default: yes)")
|
|
884
906
|
@pass_context
|
|
@@ -889,33 +911,46 @@ def consent(
|
|
|
889
911
|
domain: str | None,
|
|
890
912
|
open: bool,
|
|
891
913
|
) -> None:
|
|
892
|
-
"""Generate admin consent URL
|
|
914
|
+
"""Generate the admin consent URL for an organization's Entra tenant.
|
|
893
915
|
|
|
894
|
-
|
|
895
|
-
|
|
916
|
+
Identify the tenant one of three ways: an organization NAME (Datex staff —
|
|
917
|
+
looked up from the API), a --domain (resolved against Entra's public
|
|
918
|
+
discovery document, no authentication needed), or a --tenant-id directly.
|
|
896
919
|
"""
|
|
897
920
|
import webbrowser
|
|
898
921
|
|
|
922
|
+
from dxs.core.auth.tenants import normalize_entra_domain, resolve_tenant_id_from_domain
|
|
899
923
|
from dxs.utils.config import get_settings
|
|
900
924
|
|
|
901
|
-
if not org and not tenant_id:
|
|
925
|
+
if not org and not tenant_id and not domain:
|
|
902
926
|
ctx.output_error(
|
|
903
927
|
ValidationError(
|
|
904
|
-
message="Provide an organization name or --tenant-id",
|
|
928
|
+
message="Provide an organization name, --domain, or --tenant-id",
|
|
905
929
|
code="DXS-AUTH-016",
|
|
906
930
|
suggestions=[
|
|
907
|
-
"dxs auth consent <org>
|
|
908
|
-
"dxs auth consent --
|
|
931
|
+
"dxs auth consent <org> # Datex staff (API lookup)",
|
|
932
|
+
"dxs auth consent --domain <domain> # resolves the tenant for you",
|
|
933
|
+
"dxs auth consent --tenant-id <id> # when you already know the GUID",
|
|
909
934
|
],
|
|
910
935
|
)
|
|
911
936
|
)
|
|
912
937
|
raise SystemExit(1)
|
|
913
938
|
|
|
914
|
-
if tenant_id:
|
|
915
|
-
# Self-service:
|
|
939
|
+
if tenant_id or domain:
|
|
940
|
+
# Self-service: no API lookup. An explicit GUID wins over the domain, so
|
|
941
|
+
# supplying both never costs a network round trip.
|
|
916
942
|
org_name = org or "your organization"
|
|
917
943
|
org_id: int | None = None
|
|
918
|
-
|
|
944
|
+
if tenant_id:
|
|
945
|
+
resolved_tenant_id = tenant_id
|
|
946
|
+
else:
|
|
947
|
+
try:
|
|
948
|
+
resolved_tenant_id = resolve_tenant_id_from_domain(domain) # type: ignore[arg-type]
|
|
949
|
+
except DxsError as e:
|
|
950
|
+
ctx.output_error(e)
|
|
951
|
+
raise SystemExit(1) from None
|
|
952
|
+
# Display the bare domain, never the sign-in address it came from.
|
|
953
|
+
domain = normalize_entra_domain(domain) # type: ignore[arg-type]
|
|
919
954
|
resolved_domain = domain
|
|
920
955
|
else:
|
|
921
956
|
org_info = _fetch_organization_by_name(ctx, org) # type: ignore[arg-type]
|
|
@@ -9,10 +9,18 @@ import click
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from dxs.cli import DxsContext
|
|
12
|
-
from markdownify import markdownify as md
|
|
13
12
|
|
|
13
|
+
from dxs.commands.crm_delivery import request_group, requirement_group
|
|
14
|
+
from dxs.commands.crm_time import time_group
|
|
14
15
|
from dxs.core.auth import require_auth
|
|
15
16
|
from dxs.core.dynamics.client import DynamicsClient
|
|
17
|
+
from dxs.core.dynamics.html import html_to_markdown as _html_to_markdown
|
|
18
|
+
from dxs.core.dynamics.resolvers import (
|
|
19
|
+
is_guid,
|
|
20
|
+
resolve_account,
|
|
21
|
+
resolve_case_for_write,
|
|
22
|
+
resolve_picklist_value,
|
|
23
|
+
)
|
|
16
24
|
from dxs.core.output.yaml_fmt import LiteralString
|
|
17
25
|
from dxs.utils.responses import count_response, list_response, search_response, single
|
|
18
26
|
from dxs.utils.restricted import check_restricted_mode_for_option, restrict_in_restricted_mode
|
|
@@ -31,10 +39,6 @@ _CASE_STATUS_LABELS = {
|
|
|
31
39
|
2: "Cancelled",
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
_GUID_PATTERN = re.compile(
|
|
35
|
-
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
36
|
-
)
|
|
37
|
-
|
|
38
42
|
|
|
39
43
|
def _extract_attachment_urls(markdown: str | None) -> list[dict[str, str]]:
|
|
40
44
|
"""Extract attachment URLs and UUIDs from markdown content.
|
|
@@ -193,29 +197,6 @@ def _download_attachments(
|
|
|
193
197
|
return downloaded
|
|
194
198
|
|
|
195
199
|
|
|
196
|
-
def _html_to_markdown(html: str | None) -> str | LiteralString | None:
|
|
197
|
-
"""Convert HTML content to Markdown.
|
|
198
|
-
|
|
199
|
-
Args:
|
|
200
|
-
html: HTML string to convert.
|
|
201
|
-
|
|
202
|
-
Returns:
|
|
203
|
-
Markdown string (LiteralString for multiline), or None if input was None/empty.
|
|
204
|
-
"""
|
|
205
|
-
if not html:
|
|
206
|
-
return None
|
|
207
|
-
|
|
208
|
-
# Convert HTML to Markdown, stripping script/style tags
|
|
209
|
-
markdown = md(html, strip=["script", "style"]).strip()
|
|
210
|
-
|
|
211
|
-
# Use LiteralString for multiline content (renders as YAML block scalar)
|
|
212
|
-
# LiteralString automatically handles trailing whitespace cleanup
|
|
213
|
-
if "\n" in markdown:
|
|
214
|
-
return LiteralString(markdown)
|
|
215
|
-
|
|
216
|
-
return markdown
|
|
217
|
-
|
|
218
|
-
|
|
219
200
|
# =============================================================================
|
|
220
201
|
# HTML → plain text stripping (for compact activity output)
|
|
221
202
|
# =============================================================================
|
|
@@ -553,11 +534,6 @@ _COMMENT_STATE_COMPLETED = 1
|
|
|
553
534
|
_COMMENT_STATUS_SENT = 2
|
|
554
535
|
|
|
555
536
|
|
|
556
|
-
def _is_guid(value: str) -> bool:
|
|
557
|
-
"""Whether a value is a bare GUID rather than a name to look up."""
|
|
558
|
-
return bool(_GUID_PATTERN.match(value.strip().strip("{}")))
|
|
559
|
-
|
|
560
|
-
|
|
561
537
|
def _wrap_description_html(text: str) -> str:
|
|
562
538
|
"""Convert plain text to the HTML the case description field expects.
|
|
563
539
|
|
|
@@ -614,47 +590,6 @@ def _read_body(text: str | None, text_file: "Path | None", flag: str) -> str:
|
|
|
614
590
|
return body
|
|
615
591
|
|
|
616
592
|
|
|
617
|
-
def _resolve_case_for_write(client: "DynamicsClient", ticket_number: str) -> dict[str, Any]:
|
|
618
|
-
"""Resolve a ticket number to the one incident a write may land on.
|
|
619
|
-
|
|
620
|
-
``resolve_ticket`` raises when a ticket number matches several cases, which
|
|
621
|
-
is what we want here: appending to an arbitrary one of them would be worse
|
|
622
|
-
than failing.
|
|
623
|
-
"""
|
|
624
|
-
incident = client.resolve_ticket(ticket_number)
|
|
625
|
-
return client.get_incident(incident["incidentid"])
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
def _resolve_account(client: "DynamicsClient", value: str) -> tuple[str, str]:
|
|
629
|
-
"""Resolve an account name or GUID to (account_id, account_name)."""
|
|
630
|
-
from dxs.utils.errors import ValidationError
|
|
631
|
-
|
|
632
|
-
if _is_guid(value):
|
|
633
|
-
account_id = value.strip().strip("{}")
|
|
634
|
-
record = client.get(f"/accounts({account_id})", params={"$select": "accountid,name"})
|
|
635
|
-
return account_id, record.get("name") or account_id
|
|
636
|
-
|
|
637
|
-
matches = client.search_accounts(value).get("value", [])
|
|
638
|
-
if len(matches) == 1:
|
|
639
|
-
return matches[0]["accountid"], matches[0].get("name") or ""
|
|
640
|
-
|
|
641
|
-
if not matches:
|
|
642
|
-
raise ValidationError(
|
|
643
|
-
message=f"No active account matches '{value}'",
|
|
644
|
-
code="DXS-CRM-CASE-ACCOUNT",
|
|
645
|
-
suggestions=[
|
|
646
|
-
f"Search for the account: dxs crm account search '{value}'",
|
|
647
|
-
"Pass the accountid GUID directly if the name is not searchable",
|
|
648
|
-
],
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
raise ValidationError(
|
|
652
|
-
message=f"'{value}' matches {len(matches)} accounts - narrow it or pass the GUID",
|
|
653
|
-
code="DXS-CRM-CASE-ACCOUNT",
|
|
654
|
-
suggestions=[f"{m.get('name')} ({m.get('accountid')})" for m in matches[:10]],
|
|
655
|
-
)
|
|
656
|
-
|
|
657
|
-
|
|
658
593
|
def _resolve_contact(
|
|
659
594
|
client: "DynamicsClient", account_id: str, account_name: str, value: str
|
|
660
595
|
) -> tuple[str, str]:
|
|
@@ -666,7 +601,7 @@ def _resolve_contact(
|
|
|
666
601
|
"""
|
|
667
602
|
from dxs.utils.errors import ValidationError
|
|
668
603
|
|
|
669
|
-
if
|
|
604
|
+
if is_guid(value):
|
|
670
605
|
return value.strip().strip("{}"), value
|
|
671
606
|
|
|
672
607
|
contacts = client.list_contacts_for_account(account_id).get("value", [])
|
|
@@ -720,7 +655,7 @@ def _resolve_entitlement(
|
|
|
720
655
|
"""
|
|
721
656
|
from dxs.utils.errors import ValidationError
|
|
722
657
|
|
|
723
|
-
if value and
|
|
658
|
+
if value and is_guid(value):
|
|
724
659
|
return value.strip().strip("{}"), value
|
|
725
660
|
|
|
726
661
|
all_entitlements = client.list_entitlements_for_account(account_id).get("value", [])
|
|
@@ -761,45 +696,6 @@ def _resolve_entitlement(
|
|
|
761
696
|
)
|
|
762
697
|
|
|
763
698
|
|
|
764
|
-
def _resolve_picklist_value(
|
|
765
|
-
client: "DynamicsClient", entity: str, attribute: str, value: str, flag: str
|
|
766
|
-
) -> tuple[int, str]:
|
|
767
|
-
"""Resolve an option-set label or integer to (value, label).
|
|
768
|
-
|
|
769
|
-
Integers are validated rather than trusted, so a stale option value fails
|
|
770
|
-
here instead of writing a meaningless number onto the record.
|
|
771
|
-
"""
|
|
772
|
-
from dxs.utils.errors import ValidationError
|
|
773
|
-
|
|
774
|
-
options = client.get_picklist_options(entity, attribute)
|
|
775
|
-
needle = value.strip().lower()
|
|
776
|
-
|
|
777
|
-
def _fail(message: str, candidates: list[dict[str, Any]]) -> ValidationError:
|
|
778
|
-
return ValidationError(
|
|
779
|
-
message=message,
|
|
780
|
-
code="DXS-CRM-CASE-PICKLIST",
|
|
781
|
-
suggestions=[f"{o.get('label')} ({o.get('value')})" for o in candidates[:20]],
|
|
782
|
-
)
|
|
783
|
-
|
|
784
|
-
if re.fullmatch(r"-?\d+", needle):
|
|
785
|
-
wanted = int(needle)
|
|
786
|
-
for option in options:
|
|
787
|
-
if option.get("value") == wanted:
|
|
788
|
-
return wanted, str(option.get("label"))
|
|
789
|
-
raise _fail(f"{flag}: {wanted} is not a valid {attribute} value", options)
|
|
790
|
-
|
|
791
|
-
exact = [o for o in options if str(o.get("label", "")).lower() == needle]
|
|
792
|
-
matches = exact or [o for o in options if needle in str(o.get("label", "")).lower()]
|
|
793
|
-
|
|
794
|
-
if len(matches) == 1:
|
|
795
|
-
return int(matches[0]["value"]), str(matches[0].get("label"))
|
|
796
|
-
|
|
797
|
-
if not matches:
|
|
798
|
-
raise _fail(f"{flag}: '{value}' is not a valid {attribute} option", options)
|
|
799
|
-
|
|
800
|
-
raise _fail(f"{flag}: '{value}' matches {len(matches)} options", matches)
|
|
801
|
-
|
|
802
|
-
|
|
803
699
|
def _format_entitlement(entitlement: dict[str, Any]) -> dict[str, Any]:
|
|
804
700
|
"""Format an entitlement for display."""
|
|
805
701
|
return {
|
|
@@ -827,9 +723,9 @@ def _format_contact(contact: dict[str, Any]) -> dict[str, Any]:
|
|
|
827
723
|
def crm() -> None:
|
|
828
724
|
"""Dynamics CRM / Dataverse commands.
|
|
829
725
|
|
|
830
|
-
Query support cases, accounts, and
|
|
831
|
-
any Dataverse entity. Read-only apart from
|
|
832
|
-
'commit-ref'.
|
|
726
|
+
Query support cases, accounts, activities and time entries - or run raw
|
|
727
|
+
OData against any Dataverse entity. Read-only apart from
|
|
728
|
+
'case create/note/comment', 'time create' and 'commit-ref'.
|
|
833
729
|
|
|
834
730
|
\b
|
|
835
731
|
Setup (one-time):
|
|
@@ -841,6 +737,9 @@ def crm() -> None:
|
|
|
841
737
|
case Support cases: search, get, create, note, comment, types
|
|
842
738
|
account Accounts (companies): list, search, contacts, entitlements
|
|
843
739
|
activity Portal comments & emails - by date range or ticket
|
|
740
|
+
time Time entries: list, get, summary, create
|
|
741
|
+
request Project Requests + priced lines (read-only)
|
|
742
|
+
requirement Requirements (project tasks): the unit of delivery
|
|
844
743
|
metadata Discover entities, fields, and relationships
|
|
845
744
|
picklist Resolve option-set (picklist) integer values <-> labels
|
|
846
745
|
odata Raw OData against ANY entity (escape hatch)
|
|
@@ -910,6 +809,13 @@ def case() -> None:
|
|
|
910
809
|
# Register case as subcommand of crm
|
|
911
810
|
crm.add_command(case)
|
|
912
811
|
|
|
812
|
+
# Time entries live in their own module; crm.py is large enough already.
|
|
813
|
+
crm.add_command(time_group)
|
|
814
|
+
|
|
815
|
+
# Delivery: Project Requests (pre-requirement) and Requirements (the deliverable).
|
|
816
|
+
crm.add_command(request_group)
|
|
817
|
+
crm.add_command(requirement_group)
|
|
818
|
+
|
|
913
819
|
|
|
914
820
|
@case.command()
|
|
915
821
|
@click.argument("query", required=False, default=None)
|
|
@@ -1318,7 +1224,7 @@ def case_create(
|
|
|
1318
1224
|
client = DynamicsClient()
|
|
1319
1225
|
|
|
1320
1226
|
ctx.log(f"Resolving account '{account}'...")
|
|
1321
|
-
account_id, account_name =
|
|
1227
|
+
account_id, account_name = resolve_account(client, account)
|
|
1322
1228
|
|
|
1323
1229
|
ctx.log(f"Resolving contact '{contact}' at {account_name}...")
|
|
1324
1230
|
contact_id, contact_name = _resolve_contact(client, account_id, account_name, contact)
|
|
@@ -1328,10 +1234,10 @@ def case_create(
|
|
|
1328
1234
|
client, account_id, account_name, entitlement
|
|
1329
1235
|
)
|
|
1330
1236
|
|
|
1331
|
-
type_value, type_label =
|
|
1237
|
+
type_value, type_label = resolve_picklist_value(
|
|
1332
1238
|
client, "incident", "casetypecode", case_type, "--type"
|
|
1333
1239
|
)
|
|
1334
|
-
priority_value, priority_label =
|
|
1240
|
+
priority_value, priority_label = resolve_picklist_value(
|
|
1335
1241
|
client, "incident", "prioritycode", priority, "--priority"
|
|
1336
1242
|
)
|
|
1337
1243
|
|
|
@@ -1347,14 +1253,14 @@ def case_create(
|
|
|
1347
1253
|
|
|
1348
1254
|
impact_label = None
|
|
1349
1255
|
if business_impact is not None:
|
|
1350
|
-
impact_value, impact_label =
|
|
1256
|
+
impact_value, impact_label = resolve_picklist_value(
|
|
1351
1257
|
client, "incident", "daa_immediatebusinessimpact", business_impact, "--business-impact"
|
|
1352
1258
|
)
|
|
1353
1259
|
payload["daa_immediatebusinessimpact"] = impact_value
|
|
1354
1260
|
|
|
1355
1261
|
origin_label = None
|
|
1356
1262
|
if origin is not None:
|
|
1357
|
-
origin_value, origin_label =
|
|
1263
|
+
origin_value, origin_label = resolve_picklist_value(
|
|
1358
1264
|
client, "incident", "caseorigincode", origin, "--origin"
|
|
1359
1265
|
)
|
|
1360
1266
|
payload["caseorigincode"] = origin_value
|
|
@@ -1468,7 +1374,7 @@ def case_note(
|
|
|
1468
1374
|
client = DynamicsClient()
|
|
1469
1375
|
|
|
1470
1376
|
ctx.log(f"Resolving case {ticket_number}...")
|
|
1471
|
-
incident =
|
|
1377
|
+
incident = resolve_case_for_write(client, ticket_number)
|
|
1472
1378
|
incident_id = incident["incidentid"]
|
|
1473
1379
|
|
|
1474
1380
|
payload: dict[str, Any] = {
|
|
@@ -1559,7 +1465,7 @@ def case_comment(
|
|
|
1559
1465
|
client = DynamicsClient()
|
|
1560
1466
|
|
|
1561
1467
|
ctx.log(f"Resolving case {ticket_number}...")
|
|
1562
|
-
incident =
|
|
1468
|
+
incident = resolve_case_for_write(client, ticket_number)
|
|
1563
1469
|
incident_id = incident["incidentid"]
|
|
1564
1470
|
|
|
1565
1471
|
account_name = incident.get("_customerid_value@OData.Community.Display.V1.FormattedValue") or ""
|
|
@@ -2185,7 +2091,7 @@ def account_contacts(ctx: "DxsContext", account_name: str) -> None:
|
|
|
2185
2091
|
"""
|
|
2186
2092
|
client = DynamicsClient()
|
|
2187
2093
|
|
|
2188
|
-
account_id, resolved_name =
|
|
2094
|
+
account_id, resolved_name = resolve_account(client, account_name)
|
|
2189
2095
|
ctx.log(f"Fetching contacts for {resolved_name}...")
|
|
2190
2096
|
raw = client.list_contacts_for_account(account_id).get("value", [])
|
|
2191
2097
|
|
|
@@ -2226,7 +2132,7 @@ def account_entitlements(ctx: "DxsContext", account_name: str, include_inactive:
|
|
|
2226
2132
|
"""
|
|
2227
2133
|
client = DynamicsClient()
|
|
2228
2134
|
|
|
2229
|
-
account_id, resolved_name =
|
|
2135
|
+
account_id, resolved_name = resolve_account(client, account_name)
|
|
2230
2136
|
ctx.log(f"Fetching entitlements for {resolved_name}...")
|
|
2231
2137
|
raw = client.list_entitlements_for_account(account_id).get("value", [])
|
|
2232
2138
|
|
|
@@ -3759,7 +3665,7 @@ def _validate_task_id(task_id: str) -> str:
|
|
|
3759
3665
|
from dxs.utils.errors import ValidationError
|
|
3760
3666
|
|
|
3761
3667
|
candidate = task_id.strip().strip("{}")
|
|
3762
|
-
if not
|
|
3668
|
+
if not is_guid(candidate):
|
|
3763
3669
|
raise ValidationError(
|
|
3764
3670
|
message=f"'{task_id}' is not a valid msdyn_projecttask GUID",
|
|
3765
3671
|
code="DXS-CRM-COMMIT-001",
|