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.
Files changed (171) hide show
  1. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/PKG-INFO +1 -1
  2. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/pyproject.toml +1 -1
  3. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/scripts/analyze_corpus.py +12 -12
  4. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/scripts/generate_rdlx_schema.py +42 -18
  5. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/__init__.py +1 -1
  6. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/cli.py +62 -2
  7. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/branch.py +2 -5
  8. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/configuration.py +3 -1
  9. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/crm.py +92 -3
  10. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/datasource.py +41 -23
  11. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/devops.py +15 -13
  12. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/document.py +4 -6
  13. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/explore.py +25 -27
  14. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/function.py +1 -2
  15. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/odata.py +26 -5
  16. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/organization.py +3 -1
  17. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/release_notes.py +3 -8
  18. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/__init__.py +6 -1
  19. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/add_cmds.py +30 -22
  20. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/api.py +8 -15
  21. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/create.py +3 -1
  22. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/data.py +11 -1
  23. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/dataset.py +3 -1
  24. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/folder.py +62 -20
  25. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/schema.py +57 -27
  26. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/source.py +33 -44
  27. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/studio.py +0 -1
  28. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/telemetry.py +1 -3
  29. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/context.py +10 -1
  30. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/__init__.py +9 -2
  31. datex_studio_cli-0.4.3/src/dxs/core/api/app_config.py +76 -0
  32. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/endpoints.py +11 -17
  33. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_generator.py +1 -3
  34. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_models.py +1 -3
  35. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/models.py +3 -1
  36. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/parsers.py +2 -6
  37. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/models.py +12 -6
  38. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/client.py +117 -2
  39. datex_studio_cli-0.4.3/src/dxs/core/dynamics/metadata_cache.py +133 -0
  40. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/csv_fmt.py +2 -1
  41. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/formatter.py +13 -1
  42. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/redaction.py +61 -0
  43. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/release_notes.py +2 -7
  44. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/config.py +1 -3
  45. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/recorder.py +8 -6
  46. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/engine.py +11 -6
  47. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/folder.py +9 -20
  48. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/preview_arjs.py +4 -2
  49. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/schema.py +1 -1
  50. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/svg_renderer.py +8 -6
  51. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/validator.py +10 -25
  52. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/paths.py +11 -0
  53. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/resolvers.py +27 -11
  54. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/update_check.py +31 -14
  55. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/app.py +1 -1
  56. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/design.py +15 -15
  57. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/proxy_app.py +1 -3
  58. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/.gitignore +0 -0
  59. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/README.md +0 -0
  60. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/report-creator.skill +0 -0
  61. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/__main__.py +0 -0
  62. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/__init__.py +0 -0
  63. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/api.py +0 -0
  64. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/auth.py +0 -0
  65. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/config.py +0 -0
  66. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/endpoint.py +0 -0
  67. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/env.py +0 -0
  68. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/marketplace.py +0 -0
  69. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/proxy.py +0 -0
  70. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/repo.py +0 -0
  71. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/batch.py +0 -0
  72. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/edit.py +0 -0
  73. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/preview.py +0 -0
  74. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/report/schema_cmds.py +0 -0
  75. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/servicepack.py +0 -0
  76. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/commands/user.py +0 -0
  77. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/__init__.py +0 -0
  78. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/client.py +0 -0
  79. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/metadata_models.py +0 -0
  80. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/api/models.py +0 -0
  81. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/__init__.py +0 -0
  82. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/decorators.py +0 -0
  83. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/msal_client.py +0 -0
  84. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/auth/token_cache.py +0 -0
  85. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/cache.py +0 -0
  86. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/__init__.py +0 -0
  87. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/generator.py +0 -0
  88. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/parsers.py +0 -0
  89. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/resolver.py +0 -0
  90. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/type_def_parser.py +0 -0
  91. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/validator.py +0 -0
  92. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/__init__.py +0 -0
  93. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/flow_models.py +0 -0
  94. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/designer/models.py +0 -0
  95. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/__init__.py +0 -0
  96. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/client.py +0 -0
  97. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/devops/helpers.py +0 -0
  98. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/__init__.py +0 -0
  99. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/__init__.py +0 -0
  100. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/client.py +0 -0
  101. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/edmx_parser.py +0 -0
  102. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/metadata.py +0 -0
  103. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/__init__.py +0 -0
  104. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/generator.py +0 -0
  105. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/models.py +0 -0
  106. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/function/validator.py +0 -0
  107. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/graph.py +0 -0
  108. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/__init__.py +0 -0
  109. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/json_fmt.py +0 -0
  110. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/output/yaml_fmt.py +0 -0
  111. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/responses.py +0 -0
  112. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/__init__.py +0 -0
  113. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/identity.py +0 -0
  114. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/scrubber.py +0 -0
  115. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/core/telemetry/sender.py +0 -0
  116. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/models/__init__.py +0 -0
  117. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/__init__.py +0 -0
  118. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/datasource_binding.py +0 -0
  119. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/field_parser.py +0 -0
  120. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/manifest.py +0 -0
  121. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/models.py +0 -0
  122. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/preview.py +0 -0
  123. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/__init__.py +0 -0
  124. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
  125. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
  126. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/validate_arjs.py +0 -0
  127. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/report/wrapper.py +0 -0
  128. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/__init__.py +0 -0
  129. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/click_options.py +0 -0
  130. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/config.py +0 -0
  131. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/errors.py +0 -0
  132. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/filtering.py +0 -0
  133. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/image.py +0 -0
  134. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/responses.py +0 -0
  135. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/restricted.py +0 -0
  136. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/utils/sorting.py +0 -0
  137. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/__init__.py +0 -0
  138. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/404/index.html +0 -0
  139. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/404.html +0 -0
  140. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
  141. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
  142. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
  143. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
  144. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
  145. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
  146. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
  147. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
  148. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
  149. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
  150. {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
  151. {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
  152. {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
  153. {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
  154. {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
  155. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
  156. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
  157. {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
  158. {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
  159. {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
  160. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
  161. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
  162. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
  163. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.html +0 -0
  164. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.txt +0 -0
  165. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.html +0 -0
  166. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.txt +0 -0
  167. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.html +0 -0
  168. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.txt +0 -0
  169. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/render-cli.js +0 -0
  170. {datex_studio_cli-0.4.2 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-preview.html +0 -0
  171. {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.2
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datex-studio-cli"
7
- version = "0.4.2"
7
+ version = "0.4.3"
8
8
  description = "CLI for Datex Studio low-code platform, designed for LLM-based AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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(f"\n## Page Size Distribution")
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(f"\n## Margin Distribution")
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(f"\n## Item Type Frequency")
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(f"\n## Barcode Symbology Frequency")
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(f"\n## Font Usage Frequency (top 20)")
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(f"\n## Expression Pattern Frequency")
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(f"\n## Dataset Count per Report")
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(f"\n## Element Count per Report by Category (body-level items)")
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(f"\n## Common Field Names by Category (top 10)")
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(f"\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}%)")
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 = {"Type", "Name", "Left", "Top", "Width", "Height", "ZIndex",
245
- "Visibility", "Style", "LayerName", "OverflowName", "Bookmark",
246
- "Label", "CustomProperties"}
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(f"\n## Common Properties (shared across >=80% of item types)")
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(f"\n## Type-Specific Properties (top 10, excluding common)")
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(f"\n## Schema Comparison (corpus vs schema.py)")
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(f" Item types in corpus but NOT in schema.py:")
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(f" Item types in schema.py but NOT in corpus:")
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(f" Properties in corpus but NOT in schema.py:")
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(f" Barcode symbologies in corpus but NOT in schema.py:")
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(f" Barcode symbologies in schema.py but NOT in corpus:")
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(f"\n Schema comparison: no gaps found!")
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("directory", nargs="?", default="example-reports",
368
- help="Directory containing .rdlx-json files (default: example-reports/)")
369
- parser.add_argument("-o", "--output", default="corpus-schema.json",
370
- help="Output JSON file path (default: corpus-schema.json)")
371
- parser.add_argument("--summary-only", action="store_true",
372
- help="Print human-readable summary only, skip JSON output")
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)
@@ -1,4 +1,4 @@
1
1
  """Datex Studio CLI - Command-line interface for Datex Studio platform."""
2
2
 
3
- __version__ = "0.4.2"
3
+ __version__ = "0.4.3"
4
4
  __app_name__ = "dxs"
@@ -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
- # Set output format (CLI flag > env var > config > default)
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
- dxs_ctx.quiet = quiet
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.get(BranchEndpoints.settings(branch_id))
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(single(item={"types": types, "count": len(types)}, semantic_key="configuration_types"))
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(ctx: "DxsContext", entity_name: str, relationships: bool) -> None:
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(ctx: "DxsContext", entity_name: str | None, relationships: bool) -> None:
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, resolve_branch as _resolve_branch
41
- from dxs.utils.errors import ApiError, DxsError, ValidationError
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=["Check JSON syntax (mismatched brackets, trailing commas)", "Use a JSON validator"],
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=["Use --query/-q for inline queries or --query-file/-Q for file-based queries"],
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=["Provide an OData query with --query/-q or --query-file/-Q", "See examples: dxs datasource generate --help"],
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=["Use 'dxs datasource generate' to create a valid config file", "Manually add 'referenceName' to the JSON"],
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=["Use --collection for collection result or --single for single entity, not both"],
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=["Ensure --type-def file contains a top-level list/array", "Example: [{id: Id, type: number}, ...]"],
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=["Check type definition format", "See: dxs datasource generate-flow --help"],
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=["Format: name:type (e.g. Id:number)", "Use --key Id:number --key Code:string"],
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=["Provide reference name: dxs datasource delete ds_name -b 123", "Or use --id: dxs datasource delete --id 456 -b 123"],
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()