datex-studio-cli 0.4.2__tar.gz → 0.4.3__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.2 → datex_studio_cli-0.4.3}/PKG-INFO +1 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/pyproject.toml +1 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/scripts/analyze_corpus.py +12 -12
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/scripts/generate_rdlx_schema.py +42 -18
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/__init__.py +1 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/cli.py +62 -2
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/branch.py +2 -5
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/configuration.py +3 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/crm.py +92 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/datasource.py +41 -23
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/devops.py +15 -13
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/document.py +4 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/explore.py +25 -27
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/function.py +1 -2
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/odata.py +26 -5
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/organization.py +3 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/release_notes.py +3 -8
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/__init__.py +6 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/add_cmds.py +30 -22
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/api.py +8 -15
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/create.py +3 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/data.py +11 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/dataset.py +3 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/folder.py +62 -20
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/schema.py +57 -27
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/source.py +33 -44
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/studio.py +0 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/telemetry.py +1 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/context.py +10 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/__init__.py +9 -2
- datex_studio_cli-0.4.3/src/dxs/core/api/app_config.py +76 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/endpoints.py +11 -17
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_generator.py +1 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_models.py +1 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/models.py +3 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/parsers.py +2 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/models.py +12 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/client.py +117 -2
- datex_studio_cli-0.4.3/src/dxs/core/dynamics/metadata_cache.py +133 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/csv_fmt.py +2 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/formatter.py +13 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/redaction.py +61 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/release_notes.py +2 -7
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/config.py +1 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/recorder.py +8 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/engine.py +11 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/folder.py +9 -20
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/preview_arjs.py +4 -2
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/schema.py +1 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/svg_renderer.py +8 -6
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/validator.py +10 -25
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/paths.py +11 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/resolvers.py +27 -11
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/update_check.py +31 -14
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/app.py +1 -1
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/design.py +15 -15
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/proxy_app.py +1 -3
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/.gitignore +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/README.md +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/report-creator.skill +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/__main__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/api.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/auth.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/config.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/endpoint.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/env.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/marketplace.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/proxy.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/repo.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/batch.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/edit.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/preview.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/schema_cmds.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/servicepack.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/user.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/client.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/metadata_models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/decorators.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/msal_client.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/token_cache.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/cache.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/generator.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/parsers.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/resolver.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/type_def_parser.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/validator.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/flow_models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/client.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/helpers.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/client.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/edmx_parser.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/metadata.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/generator.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/validator.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/graph.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/json_fmt.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/yaml_fmt.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/responses.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/identity.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/scrubber.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/sender.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/models/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/datasource_binding.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/field_parser.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/manifest.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/models.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/preview.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/validate_arjs.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/wrapper.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/click_options.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/config.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/errors.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/filtering.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/image.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/responses.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/restricted.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/sorting.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/__init__.py +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/404/index.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/404.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.txt +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.txt +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.txt +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/render-cli.js +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-preview.html +0 -0
- {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-validate.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datex-studio-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
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
|
|
@@ -226,39 +226,39 @@ def main() -> None:
|
|
|
226
226
|
for cat, cnt in category_counts.most_common():
|
|
227
227
|
print(f" {cat:30s} {cnt:4d}")
|
|
228
228
|
|
|
229
|
-
print(
|
|
229
|
+
print("\n## Page Size Distribution")
|
|
230
230
|
for size, cnt in page_sizes.most_common():
|
|
231
231
|
pct = cnt / total * 100
|
|
232
232
|
print(f" {size:30s} {cnt:4d} ({pct:.0f}%)")
|
|
233
233
|
|
|
234
|
-
print(
|
|
234
|
+
print("\n## Margin Distribution")
|
|
235
235
|
for margin, cnt in margin_counter.most_common(15):
|
|
236
236
|
pct = cnt / total * 100
|
|
237
237
|
print(f" {margin:55s} {cnt:4d} ({pct:.0f}%)")
|
|
238
238
|
|
|
239
|
-
print(
|
|
239
|
+
print("\n## Item Type Frequency")
|
|
240
240
|
for typ, cnt in item_type_counter.most_common():
|
|
241
241
|
print(f" {typ:25s} {cnt:5d}")
|
|
242
242
|
|
|
243
|
-
print(
|
|
243
|
+
print("\n## Barcode Symbology Frequency")
|
|
244
244
|
for sym, cnt in barcode_symbology_counter.most_common():
|
|
245
245
|
print(f" {sym:25s} {cnt:4d}")
|
|
246
246
|
|
|
247
|
-
print(
|
|
247
|
+
print("\n## Font Usage Frequency (top 20)")
|
|
248
248
|
for fnt, cnt in font_counter.most_common(20):
|
|
249
249
|
print(f" {fnt:30s} {cnt:5d}")
|
|
250
250
|
|
|
251
|
-
print(
|
|
251
|
+
print("\n## Expression Pattern Frequency")
|
|
252
252
|
for pat, cnt in expression_pattern_counter.most_common():
|
|
253
253
|
print(f" {pat:30s} {cnt:5d}")
|
|
254
254
|
|
|
255
|
-
print(
|
|
255
|
+
print("\n## Dataset Count per Report")
|
|
256
256
|
if dataset_counts:
|
|
257
257
|
print(f" Mean: {mean(dataset_counts):.1f}")
|
|
258
258
|
print(f" Median: {median(dataset_counts):.1f}")
|
|
259
259
|
print(f" Range: {min(dataset_counts)} - {max(dataset_counts)}")
|
|
260
260
|
|
|
261
|
-
print(
|
|
261
|
+
print("\n## Element Count per Report by Category (body-level items)")
|
|
262
262
|
for cat in sorted(element_counts_by_category):
|
|
263
263
|
counts = element_counts_by_category[cat]
|
|
264
264
|
if counts:
|
|
@@ -269,7 +269,7 @@ def main() -> None:
|
|
|
269
269
|
f"n={len(counts)}"
|
|
270
270
|
)
|
|
271
271
|
|
|
272
|
-
print(
|
|
272
|
+
print("\n## Common Field Names by Category (top 10)")
|
|
273
273
|
for cat in sorted(field_names_by_category):
|
|
274
274
|
top = field_names_by_category[cat].most_common(10)
|
|
275
275
|
if top:
|
|
@@ -277,9 +277,9 @@ def main() -> None:
|
|
|
277
277
|
for name, cnt in top:
|
|
278
278
|
print(f" {name:30s} {cnt:4d}")
|
|
279
279
|
|
|
280
|
-
print(
|
|
281
|
-
print(f" PageHeader: {page_header_count}/{total} ({page_header_count/total*100:.0f}%)")
|
|
282
|
-
print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count/total*100:.0f}%)")
|
|
280
|
+
print("\n## PageHeader / PageFooter Usage")
|
|
281
|
+
print(f" PageHeader: {page_header_count}/{total} ({page_header_count / total * 100:.0f}%)")
|
|
282
|
+
print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count / total * 100:.0f}%)")
|
|
283
283
|
|
|
284
284
|
|
|
285
285
|
if __name__ == "__main__":
|
|
@@ -241,9 +241,22 @@ def compare_schema(item_types: dict, items: list[dict]) -> dict:
|
|
|
241
241
|
all_schema_props = schema_props | schema_style_props
|
|
242
242
|
corpus_props = set(item_types[type_name]["properties"].keys())
|
|
243
243
|
# Exclude common layout props that aren't type-specific
|
|
244
|
-
common_layout = {
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
common_layout = {
|
|
245
|
+
"Type",
|
|
246
|
+
"Name",
|
|
247
|
+
"Left",
|
|
248
|
+
"Top",
|
|
249
|
+
"Width",
|
|
250
|
+
"Height",
|
|
251
|
+
"ZIndex",
|
|
252
|
+
"Visibility",
|
|
253
|
+
"Style",
|
|
254
|
+
"LayerName",
|
|
255
|
+
"OverflowName",
|
|
256
|
+
"Bookmark",
|
|
257
|
+
"Label",
|
|
258
|
+
"CustomProperties",
|
|
259
|
+
}
|
|
247
260
|
missing = sorted((corpus_props - all_schema_props) - common_layout)
|
|
248
261
|
if missing:
|
|
249
262
|
props_not_in_schema[type_name] = missing
|
|
@@ -296,13 +309,13 @@ def print_summary(schema: dict) -> None:
|
|
|
296
309
|
# Common properties
|
|
297
310
|
common = schema.get("common_properties", {})
|
|
298
311
|
if common:
|
|
299
|
-
print(
|
|
312
|
+
print("\n## Common Properties (shared across >=80% of item types)")
|
|
300
313
|
for prop, info in sorted(common.items()):
|
|
301
314
|
print(f" {prop:25s} {info['present_in']}")
|
|
302
315
|
|
|
303
316
|
# Top properties per type (top 5 by count, excluding common)
|
|
304
317
|
common_names = set(common.keys()) if common else set()
|
|
305
|
-
print(
|
|
318
|
+
print("\n## Type-Specific Properties (top 10, excluding common)")
|
|
306
319
|
for type_name, type_info in sorted(item_types.items(), key=lambda x: -x[1]["count"]):
|
|
307
320
|
props = type_info["properties"]
|
|
308
321
|
specific = [(k, v) for k, v in props.items() if k not in common_names]
|
|
@@ -322,41 +335,41 @@ def print_summary(schema: dict) -> None:
|
|
|
322
335
|
# Comparison
|
|
323
336
|
comp = schema.get("comparison", {})
|
|
324
337
|
if comp and "error" not in comp:
|
|
325
|
-
print(
|
|
338
|
+
print("\n## Schema Comparison (corpus vs schema.py)")
|
|
326
339
|
|
|
327
340
|
types_missing = comp.get("item_types_in_corpus_not_in_schema", [])
|
|
328
341
|
if types_missing:
|
|
329
|
-
print(
|
|
342
|
+
print(" Item types in corpus but NOT in schema.py:")
|
|
330
343
|
for t in types_missing:
|
|
331
344
|
count = item_types.get(t, {}).get("count", "?")
|
|
332
345
|
print(f" - {t} ({count} instances)")
|
|
333
346
|
|
|
334
347
|
types_extra = comp.get("item_types_in_schema_not_in_corpus", [])
|
|
335
348
|
if types_extra:
|
|
336
|
-
print(
|
|
349
|
+
print(" Item types in schema.py but NOT in corpus:")
|
|
337
350
|
for t in types_extra:
|
|
338
351
|
print(f" - {t}")
|
|
339
352
|
|
|
340
353
|
props_missing = comp.get("properties_in_corpus_not_in_schema", {})
|
|
341
354
|
if props_missing:
|
|
342
|
-
print(
|
|
355
|
+
print(" Properties in corpus but NOT in schema.py:")
|
|
343
356
|
for type_name, props in sorted(props_missing.items()):
|
|
344
357
|
print(f" {type_name}: {', '.join(props)}")
|
|
345
358
|
|
|
346
359
|
sym_missing = comp.get("symbologies_in_corpus_not_in_schema", [])
|
|
347
360
|
if sym_missing:
|
|
348
|
-
print(
|
|
361
|
+
print(" Barcode symbologies in corpus but NOT in schema.py:")
|
|
349
362
|
for s in sym_missing:
|
|
350
363
|
print(f" - {s}")
|
|
351
364
|
|
|
352
365
|
sym_extra = comp.get("symbologies_in_schema_not_in_corpus", [])
|
|
353
366
|
if sym_extra:
|
|
354
|
-
print(
|
|
367
|
+
print(" Barcode symbologies in schema.py but NOT in corpus:")
|
|
355
368
|
for s in sym_extra:
|
|
356
369
|
print(f" - {s}")
|
|
357
370
|
|
|
358
371
|
if not comp or all(not v for v in comp.values() if isinstance(v, (list, dict))):
|
|
359
|
-
print(
|
|
372
|
+
print("\n Schema comparison: no gaps found!")
|
|
360
373
|
|
|
361
374
|
|
|
362
375
|
# ── Main ─────────────────────────────────────────────────────────────────
|
|
@@ -364,12 +377,23 @@ def print_summary(schema: dict) -> None:
|
|
|
364
377
|
|
|
365
378
|
def main() -> None:
|
|
366
379
|
parser = argparse.ArgumentParser(description="Generate RDLX-JSON schema from report corpus")
|
|
367
|
-
parser.add_argument(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
380
|
+
parser.add_argument(
|
|
381
|
+
"directory",
|
|
382
|
+
nargs="?",
|
|
383
|
+
default="example-reports",
|
|
384
|
+
help="Directory containing .rdlx-json files (default: example-reports/)",
|
|
385
|
+
)
|
|
386
|
+
parser.add_argument(
|
|
387
|
+
"-o",
|
|
388
|
+
"--output",
|
|
389
|
+
default="corpus-schema.json",
|
|
390
|
+
help="Output JSON file path (default: corpus-schema.json)",
|
|
391
|
+
)
|
|
392
|
+
parser.add_argument(
|
|
393
|
+
"--summary-only",
|
|
394
|
+
action="store_true",
|
|
395
|
+
help="Print human-readable summary only, skip JSON output",
|
|
396
|
+
)
|
|
373
397
|
args = parser.parse_args()
|
|
374
398
|
|
|
375
399
|
root = Path(args.directory)
|
|
@@ -38,6 +38,37 @@ def set_last_output_format(fmt: OutputFormat) -> None:
|
|
|
38
38
|
_last_output_format = fmt
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _format_set_in_env_or_config() -> bool:
|
|
42
|
+
"""Whether ``default_output_format`` was set explicitly outside CLI flags.
|
|
43
|
+
|
|
44
|
+
Used to decide if TTY-auto-script mode should override the default format.
|
|
45
|
+
Returns True when either ``DXS_DEFAULT_OUTPUT_FORMAT`` is in the environment
|
|
46
|
+
or the config file contains a ``default_output_format`` entry.
|
|
47
|
+
"""
|
|
48
|
+
import os
|
|
49
|
+
|
|
50
|
+
from dxs.utils.config import load_config_file
|
|
51
|
+
|
|
52
|
+
if os.environ.get("DXS_DEFAULT_OUTPUT_FORMAT"):
|
|
53
|
+
return True
|
|
54
|
+
try:
|
|
55
|
+
return bool(load_config_file().get("default_output_format"))
|
|
56
|
+
except Exception: # noqa: BLE001 - config read should never block startup
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _stdout_is_pipe() -> bool:
|
|
61
|
+
"""Whether stdout looks like a non-interactive consumer (pipe/redirect).
|
|
62
|
+
|
|
63
|
+
Pulled into a helper so tests can patch it independently of Click's
|
|
64
|
+
CliRunner, which swaps ``sys.stdout`` with a non-TTY StringIO.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
return not sys.stdout.isatty()
|
|
68
|
+
except (AttributeError, ValueError):
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
41
72
|
# Global options shared across all commands
|
|
42
73
|
CONTEXT_SETTINGS = {
|
|
43
74
|
"help_option_names": ["-h", "--help"],
|
|
@@ -140,6 +171,13 @@ class DxsGroup(click.Group):
|
|
|
140
171
|
default=False,
|
|
141
172
|
help="Show full output including null values and verbose metadata",
|
|
142
173
|
)
|
|
174
|
+
@click.option(
|
|
175
|
+
"--flat-fv/--no-flat-fv",
|
|
176
|
+
"flat_fv",
|
|
177
|
+
default=None,
|
|
178
|
+
help="Flatten Dynamics @OData FormattedValue siblings into clean keys. "
|
|
179
|
+
"Defaults to on with --concise (the default mode); pass --no-flat-fv to opt out.",
|
|
180
|
+
)
|
|
143
181
|
@click.option(
|
|
144
182
|
"-s",
|
|
145
183
|
"--save",
|
|
@@ -181,6 +219,7 @@ def cli(
|
|
|
181
219
|
verbose: bool,
|
|
182
220
|
quiet: bool,
|
|
183
221
|
full: bool,
|
|
222
|
+
flat_fv: bool | None,
|
|
184
223
|
save_path: str | None,
|
|
185
224
|
force_overwrite: bool,
|
|
186
225
|
diagnose_auth: bool,
|
|
@@ -229,19 +268,40 @@ def cli(
|
|
|
229
268
|
# Load settings
|
|
230
269
|
settings = get_settings()
|
|
231
270
|
|
|
232
|
-
#
|
|
271
|
+
# When stdout is not a TTY (piped/redirected), assume a script consumer:
|
|
272
|
+
# default to JSON output and suppress progress banners unless the user
|
|
273
|
+
# has explicitly chosen otherwise via CLI flag, env var, or config file.
|
|
274
|
+
piped = _stdout_is_pipe()
|
|
275
|
+
click_ctx = click.get_current_context()
|
|
276
|
+
quiet_src = click_ctx.get_parameter_source("quiet")
|
|
277
|
+
fmt_explicit_in_env_or_config = _format_set_in_env_or_config()
|
|
278
|
+
|
|
279
|
+
# Set output format (CLI flag > env var > config > TTY auto > default)
|
|
233
280
|
if output_format:
|
|
234
281
|
dxs_ctx.output_format = OutputFormat(output_format.lower())
|
|
235
282
|
dxs_ctx.explicit_output_format = True
|
|
283
|
+
elif fmt_explicit_in_env_or_config and settings.default_output_format:
|
|
284
|
+
dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
|
|
285
|
+
elif piped:
|
|
286
|
+
dxs_ctx.output_format = OutputFormat.JSON
|
|
236
287
|
elif settings.default_output_format:
|
|
237
288
|
dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
|
|
238
289
|
set_last_output_format(dxs_ctx.output_format)
|
|
239
290
|
|
|
240
291
|
# Set verbosity (--verbose enables debug output, --quiet suppresses progress)
|
|
241
292
|
dxs_ctx.verbose = verbose
|
|
242
|
-
|
|
293
|
+
if quiet_src == click.core.ParameterSource.COMMANDLINE:
|
|
294
|
+
dxs_ctx.quiet = quiet
|
|
295
|
+
else:
|
|
296
|
+
dxs_ctx.quiet = quiet or piped
|
|
243
297
|
# Concise output is default; --full disables it
|
|
244
298
|
dxs_ctx.concise = not full
|
|
299
|
+
# Flatten Dynamics FormattedValue siblings: follow --concise unless
|
|
300
|
+
# --flat-fv / --no-flat-fv was passed explicitly.
|
|
301
|
+
if flat_fv is None:
|
|
302
|
+
dxs_ctx.flat_fv = dxs_ctx.concise
|
|
303
|
+
else:
|
|
304
|
+
dxs_ctx.flat_fv = flat_fv
|
|
245
305
|
|
|
246
306
|
# Set save options
|
|
247
307
|
dxs_ctx.save_path = save_path
|
|
@@ -13,6 +13,7 @@ from dxs.core.api import (
|
|
|
13
13
|
BranchEndpoints,
|
|
14
14
|
RepoEndpoints,
|
|
15
15
|
SourceControlEndpoints,
|
|
16
|
+
fetch_settings,
|
|
16
17
|
)
|
|
17
18
|
from dxs.core.api.models import (
|
|
18
19
|
APPLICATION_DEFINITION_TYPE_NAMES,
|
|
@@ -1073,11 +1074,7 @@ def settings(ctx: DxsContext, branch_id: int) -> None:
|
|
|
1073
1074
|
client = ApiClient()
|
|
1074
1075
|
ctx.log(f"Fetching settings for branch {branch_id}...")
|
|
1075
1076
|
|
|
1076
|
-
settings_data = client
|
|
1077
|
-
|
|
1078
|
-
# Normalize to list if not already
|
|
1079
|
-
if not isinstance(settings_data, list):
|
|
1080
|
-
settings_data = [settings_data] if settings_data else []
|
|
1077
|
+
settings_data = fetch_settings(client, branch_id)
|
|
1081
1078
|
|
|
1082
1079
|
ctx.output(list_response(items=settings_data, semantic_key="settings", branch_id=branch_id))
|
|
1083
1080
|
|
|
@@ -70,7 +70,9 @@ def configuration() -> None:
|
|
|
70
70
|
def configuration_types(ctx: DxsContext) -> None:
|
|
71
71
|
"""List the known platform configuration type names accepted by these commands."""
|
|
72
72
|
types = sorted(ConfigurationEndpoints.KNOWN_TYPES)
|
|
73
|
-
ctx.output(
|
|
73
|
+
ctx.output(
|
|
74
|
+
single(item={"types": types, "count": len(types)}, semantic_key="configuration_types")
|
|
75
|
+
)
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
@configuration.command("get")
|
|
@@ -1318,7 +1318,12 @@ def _list_entities(ctx: "DxsContext") -> None:
|
|
|
1318
1318
|
ctx.output(list_response(items=formatted_entities, semantic_key="entities"))
|
|
1319
1319
|
|
|
1320
1320
|
|
|
1321
|
-
def _get_entity_metadata(
|
|
1321
|
+
def _get_entity_metadata(
|
|
1322
|
+
ctx: "DxsContext",
|
|
1323
|
+
entity_name: str,
|
|
1324
|
+
relationships: bool,
|
|
1325
|
+
search: str | None = None,
|
|
1326
|
+
) -> None:
|
|
1322
1327
|
"""Get metadata for a specific entity."""
|
|
1323
1328
|
client = DynamicsClient()
|
|
1324
1329
|
|
|
@@ -1342,6 +1347,16 @@ def _get_entity_metadata(ctx: "DxsContext", entity_name: str, relationships: boo
|
|
|
1342
1347
|
}
|
|
1343
1348
|
)
|
|
1344
1349
|
|
|
1350
|
+
if search:
|
|
1351
|
+
needle = search.lower()
|
|
1352
|
+
formatted_fields = [
|
|
1353
|
+
f
|
|
1354
|
+
for f in formatted_fields
|
|
1355
|
+
if needle in (f.get("name") or "").lower()
|
|
1356
|
+
or needle in (f.get("display_name") or "").lower()
|
|
1357
|
+
or needle in (f.get("description") or "").lower()
|
|
1358
|
+
]
|
|
1359
|
+
|
|
1345
1360
|
# Output fields
|
|
1346
1361
|
ctx.output(list_response(items=formatted_fields, semantic_key="fields"))
|
|
1347
1362
|
|
|
@@ -1375,6 +1390,23 @@ def _get_entity_metadata(ctx: "DxsContext", entity_name: str, relationships: boo
|
|
|
1375
1390
|
}
|
|
1376
1391
|
)
|
|
1377
1392
|
|
|
1393
|
+
if search:
|
|
1394
|
+
needle = search.lower()
|
|
1395
|
+
formatted_relationships = [
|
|
1396
|
+
r
|
|
1397
|
+
for r in formatted_relationships
|
|
1398
|
+
if any(
|
|
1399
|
+
needle in (r.get(k) or "").lower()
|
|
1400
|
+
for k in (
|
|
1401
|
+
"name",
|
|
1402
|
+
"schema_name",
|
|
1403
|
+
"target_entity",
|
|
1404
|
+
"referencing_entity",
|
|
1405
|
+
"referencing_attribute",
|
|
1406
|
+
)
|
|
1407
|
+
)
|
|
1408
|
+
]
|
|
1409
|
+
|
|
1378
1410
|
ctx.output(list_response(items=formatted_relationships, semantic_key="relationships"))
|
|
1379
1411
|
|
|
1380
1412
|
|
|
@@ -1387,9 +1419,22 @@ def _get_entity_metadata(ctx: "DxsContext", entity_name: str, relationships: boo
|
|
|
1387
1419
|
default=False,
|
|
1388
1420
|
help="Include navigation properties (relationships to other entities)",
|
|
1389
1421
|
)
|
|
1422
|
+
@click.option(
|
|
1423
|
+
"--search",
|
|
1424
|
+
"-S",
|
|
1425
|
+
type=str,
|
|
1426
|
+
default=None,
|
|
1427
|
+
help="Filter fields (and relationships) by case-insensitive substring "
|
|
1428
|
+
"match on name, display_name, or description.",
|
|
1429
|
+
)
|
|
1390
1430
|
@click.pass_obj
|
|
1391
1431
|
@require_auth
|
|
1392
|
-
def metadata(
|
|
1432
|
+
def metadata(
|
|
1433
|
+
ctx: "DxsContext",
|
|
1434
|
+
entity_name: str | None,
|
|
1435
|
+
relationships: bool,
|
|
1436
|
+
search: str | None,
|
|
1437
|
+
) -> None:
|
|
1393
1438
|
"""Entity metadata exploration.
|
|
1394
1439
|
|
|
1395
1440
|
Explore Dynamics CRM entity definitions, fields, and relationships.
|
|
@@ -1409,6 +1454,7 @@ def metadata(ctx: "DxsContext", entity_name: str | None, relationships: bool) ->
|
|
|
1409
1454
|
dxs crm metadata account # Fields for accounts
|
|
1410
1455
|
dxs crm metadata incident --relationships # Include navigation properties
|
|
1411
1456
|
dxs crm metadata incident -r # Short form
|
|
1457
|
+
dxs crm metadata incident --search priority # Filter fields by keyword
|
|
1412
1458
|
"""
|
|
1413
1459
|
if entity_name is None:
|
|
1414
1460
|
# Show help if no argument provided
|
|
@@ -1420,13 +1466,56 @@ def metadata(ctx: "DxsContext", entity_name: str | None, relationships: bool) ->
|
|
|
1420
1466
|
_list_entities(ctx)
|
|
1421
1467
|
else:
|
|
1422
1468
|
# Show fields for the specified entity
|
|
1423
|
-
_get_entity_metadata(ctx, entity_name, relationships)
|
|
1469
|
+
_get_entity_metadata(ctx, entity_name, relationships, search=search)
|
|
1424
1470
|
|
|
1425
1471
|
|
|
1426
1472
|
# Register metadata as subcommand of crm
|
|
1427
1473
|
crm.add_command(metadata)
|
|
1428
1474
|
|
|
1429
1475
|
|
|
1476
|
+
@click.command("picklist")
|
|
1477
|
+
@click.argument("entity_name")
|
|
1478
|
+
@click.argument("attribute_name")
|
|
1479
|
+
@click.pass_obj
|
|
1480
|
+
@require_auth
|
|
1481
|
+
def picklist(ctx: "DxsContext", entity_name: str, attribute_name: str) -> None:
|
|
1482
|
+
"""Show option set (picklist) values for an entity attribute.
|
|
1483
|
+
|
|
1484
|
+
Lists the integer values and labels for a picklist/state/status field.
|
|
1485
|
+
Useful for building OData filters that need numeric picklist values.
|
|
1486
|
+
|
|
1487
|
+
\b
|
|
1488
|
+
Examples:
|
|
1489
|
+
dxs crm picklist incident statuscode
|
|
1490
|
+
dxs crm picklist salesorder statuscode
|
|
1491
|
+
dxs crm picklist incident prioritycode
|
|
1492
|
+
"""
|
|
1493
|
+
from dxs.utils.errors import ApiError
|
|
1494
|
+
|
|
1495
|
+
client = DynamicsClient()
|
|
1496
|
+
ctx.log(f"Fetching picklist options for {entity_name}.{attribute_name}...")
|
|
1497
|
+
try:
|
|
1498
|
+
options = client.get_picklist_options(entity_name, attribute_name)
|
|
1499
|
+
except (KeyError, TypeError) as exc:
|
|
1500
|
+
raise ApiError(
|
|
1501
|
+
message=(
|
|
1502
|
+
f"'{attribute_name}' on '{entity_name}' is not a picklist attribute "
|
|
1503
|
+
"(no OptionSet present in the response)."
|
|
1504
|
+
),
|
|
1505
|
+
code="DXS-DYNAMICS-NOT-PICKLIST",
|
|
1506
|
+
suggestions=[
|
|
1507
|
+
f"Run 'dxs crm metadata {entity_name} --search {attribute_name}' "
|
|
1508
|
+
"to inspect the field's actual type.",
|
|
1509
|
+
"Picklist fields are typically suffixed 'code' (e.g., statuscode, casetypecode).",
|
|
1510
|
+
],
|
|
1511
|
+
) from exc
|
|
1512
|
+
ctx.output(list_response(items=options, semantic_key=f"{attribute_name}_options"))
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
# Register picklist as subcommand of crm
|
|
1516
|
+
crm.add_command(picklist)
|
|
1517
|
+
|
|
1518
|
+
|
|
1430
1519
|
# =============================================================================
|
|
1431
1520
|
# Activity Commands (portal comments, emails)
|
|
1432
1521
|
# =============================================================================
|
|
@@ -37,8 +37,9 @@ from dxs.core.datasource.parsers import (
|
|
|
37
37
|
)
|
|
38
38
|
from dxs.core.datasource.resolver import apply_linked_params, resolve_linked_ds_metadata
|
|
39
39
|
from dxs.core.datasource.validator import DatasourceValidator
|
|
40
|
-
from dxs.utils.click_options import pagination_options
|
|
41
|
-
from dxs.utils.
|
|
40
|
+
from dxs.utils.click_options import pagination_options
|
|
41
|
+
from dxs.utils.click_options import resolve_branch as _resolve_branch
|
|
42
|
+
from dxs.utils.errors import DxsError, ValidationError
|
|
42
43
|
from dxs.utils.responses import list_response, single
|
|
43
44
|
|
|
44
45
|
_MAX_FLOW_FILE_BYTES = 512 * 1024 # 512 KB
|
|
@@ -85,7 +86,10 @@ def _load_config_json(file: str) -> dict[str, Any]:
|
|
|
85
86
|
raise ValidationError(
|
|
86
87
|
message=f"Invalid JSON in {file}: {e}",
|
|
87
88
|
code="DXS-DS-003",
|
|
88
|
-
suggestions=[
|
|
89
|
+
suggestions=[
|
|
90
|
+
"Check JSON syntax (mismatched brackets, trailing commas)",
|
|
91
|
+
"Use a JSON validator",
|
|
92
|
+
],
|
|
89
93
|
) from None
|
|
90
94
|
|
|
91
95
|
|
|
@@ -207,9 +211,7 @@ def _generate_config(
|
|
|
207
211
|
"The linked reference will be stored but may cause build errors."
|
|
208
212
|
)
|
|
209
213
|
except DxsError as exc:
|
|
210
|
-
ctx.log(
|
|
211
|
-
f"Warning: Could not verify target datasource '{target_ref}': {exc}"
|
|
212
|
-
)
|
|
214
|
+
ctx.log(f"Warning: Could not verify target datasource '{target_ref}': {exc}")
|
|
213
215
|
|
|
214
216
|
DatasourceValidator().validate_and_raise(config.model_dump())
|
|
215
217
|
|
|
@@ -493,7 +495,9 @@ def generate(
|
|
|
493
495
|
raise ValidationError(
|
|
494
496
|
message="Use either --query/-q or --query-file/-Q, not both.",
|
|
495
497
|
code="DXS-DS-010",
|
|
496
|
-
suggestions=[
|
|
498
|
+
suggestions=[
|
|
499
|
+
"Use --query/-q for inline queries or --query-file/-Q for file-based queries"
|
|
500
|
+
],
|
|
497
501
|
)
|
|
498
502
|
if query_file:
|
|
499
503
|
query = _read_file(query_file, "--query-file").strip()
|
|
@@ -501,7 +505,10 @@ def generate(
|
|
|
501
505
|
raise ValidationError(
|
|
502
506
|
message="Provide either --query/-q or --query-file/-Q.",
|
|
503
507
|
code="DXS-DS-011",
|
|
504
|
-
suggestions=[
|
|
508
|
+
suggestions=[
|
|
509
|
+
"Provide an OData query with --query/-q or --query-file/-Q",
|
|
510
|
+
"See examples: dxs datasource generate --help",
|
|
511
|
+
],
|
|
505
512
|
)
|
|
506
513
|
|
|
507
514
|
ctx.log(f"Generating datasource configuration for: {query[:80]}...")
|
|
@@ -539,9 +546,7 @@ def generate(
|
|
|
539
546
|
|
|
540
547
|
config_dict = config.model_dump(by_alias=True, mode="python", exclude_none=False)
|
|
541
548
|
output_path = Path(output_file)
|
|
542
|
-
output_path.write_text(
|
|
543
|
-
json_lib.dumps(config_dict, indent=2, default=str), encoding="utf-8"
|
|
544
|
-
)
|
|
549
|
+
output_path.write_text(json_lib.dumps(config_dict, indent=2, default=str), encoding="utf-8")
|
|
545
550
|
|
|
546
551
|
output_item: dict[str, Any] = {
|
|
547
552
|
"referenceName": reference_name,
|
|
@@ -580,7 +585,10 @@ def upsert(ctx: DxsContext, file: str, branch: int | None) -> None:
|
|
|
580
585
|
raise ValidationError(
|
|
581
586
|
message=f"File {file} is missing 'referenceName'. Use 'dxs datasource generate' to create valid config files.",
|
|
582
587
|
code="DXS-DS-012",
|
|
583
|
-
suggestions=[
|
|
588
|
+
suggestions=[
|
|
589
|
+
"Use 'dxs datasource generate' to create a valid config file",
|
|
590
|
+
"Manually add 'referenceName' to the JSON",
|
|
591
|
+
],
|
|
584
592
|
)
|
|
585
593
|
|
|
586
594
|
api_client = ApiClient()
|
|
@@ -864,7 +872,9 @@ def generate_flow(
|
|
|
864
872
|
raise ValidationError(
|
|
865
873
|
message="--collection and --single are mutually exclusive",
|
|
866
874
|
code="DXS-DS-013",
|
|
867
|
-
suggestions=[
|
|
875
|
+
suggestions=[
|
|
876
|
+
"Use --collection for collection result or --single for single entity, not both"
|
|
877
|
+
],
|
|
868
878
|
)
|
|
869
879
|
|
|
870
880
|
# Determine result_is_collection (None = auto-detect in generator)
|
|
@@ -900,7 +910,10 @@ def generate_flow(
|
|
|
900
910
|
raise ValidationError(
|
|
901
911
|
message=f"--type-def must contain a YAML/JSON list of type definitions, got {type(type_def_raw).__name__}",
|
|
902
912
|
code="DXS-DS-005",
|
|
903
|
-
suggestions=[
|
|
913
|
+
suggestions=[
|
|
914
|
+
"Ensure --type-def file contains a top-level list/array",
|
|
915
|
+
"Example: [{id: Id, type: number}, ...]",
|
|
916
|
+
],
|
|
904
917
|
)
|
|
905
918
|
try:
|
|
906
919
|
type_def_parsed = parse_type_def(type_def_raw)
|
|
@@ -908,7 +921,10 @@ def generate_flow(
|
|
|
908
921
|
raise ValidationError(
|
|
909
922
|
message=str(e),
|
|
910
923
|
code="DXS-DS-006",
|
|
911
|
-
suggestions=[
|
|
924
|
+
suggestions=[
|
|
925
|
+
"Check type definition format",
|
|
926
|
+
"See: dxs datasource generate-flow --help",
|
|
927
|
+
],
|
|
912
928
|
) from None
|
|
913
929
|
|
|
914
930
|
# 4. Parse --in-param
|
|
@@ -926,7 +942,10 @@ def generate_flow(
|
|
|
926
942
|
raise ValidationError(
|
|
927
943
|
message=f"--key must be 'name:type', got: {k!r}",
|
|
928
944
|
code="DXS-DS-014",
|
|
929
|
-
suggestions=[
|
|
945
|
+
suggestions=[
|
|
946
|
+
"Format: name:type (e.g. Id:number)",
|
|
947
|
+
"Use --key Id:number --key Code:string",
|
|
948
|
+
],
|
|
930
949
|
)
|
|
931
950
|
parsed_keys.append((parts[0], parts[1]))
|
|
932
951
|
|
|
@@ -990,9 +1009,7 @@ def generate_flow(
|
|
|
990
1009
|
"The linked reference will be stored but may cause build errors."
|
|
991
1010
|
)
|
|
992
1011
|
except DxsError as exc:
|
|
993
|
-
ctx.log(
|
|
994
|
-
f"Warning: Could not verify target datasource '{target_ref}': {exc}"
|
|
995
|
-
)
|
|
1012
|
+
ctx.log(f"Warning: Could not verify target datasource '{target_ref}': {exc}")
|
|
996
1013
|
|
|
997
1014
|
# 11. Validate the final configuration
|
|
998
1015
|
DatasourceValidator().validate_and_raise(config.model_dump())
|
|
@@ -1000,9 +1017,7 @@ def generate_flow(
|
|
|
1000
1017
|
# Serialize to JSON
|
|
1001
1018
|
config_dict = config.model_dump(by_alias=True, mode="python", exclude_none=False)
|
|
1002
1019
|
output_path = Path(output_file)
|
|
1003
|
-
output_path.write_text(
|
|
1004
|
-
json_lib.dumps(config_dict, indent=2, default=str), encoding="utf-8"
|
|
1005
|
-
)
|
|
1020
|
+
output_path.write_text(json_lib.dumps(config_dict, indent=2, default=str), encoding="utf-8")
|
|
1006
1021
|
|
|
1007
1022
|
output_item: dict[str, Any] = {
|
|
1008
1023
|
"referenceName": reference_name,
|
|
@@ -1226,7 +1241,10 @@ def delete(
|
|
|
1226
1241
|
raise ValidationError(
|
|
1227
1242
|
message="Provide a reference name or --id.",
|
|
1228
1243
|
code="DXS-DS-015",
|
|
1229
|
-
suggestions=[
|
|
1244
|
+
suggestions=[
|
|
1245
|
+
"Provide reference name: dxs datasource delete ds_name -b 123",
|
|
1246
|
+
"Or use --id: dxs datasource delete --id 456 -b 123",
|
|
1247
|
+
],
|
|
1230
1248
|
)
|
|
1231
1249
|
|
|
1232
1250
|
api_client = ApiClient()
|