datex-studio-cli 0.2.1__tar.gz → 0.3.0__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.2.1 → datex_studio_cli-0.3.0}/.gitignore +1 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/PKG-INFO +14 -11
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/README.md +12 -10
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/pyproject.toml +6 -1
- datex_studio_cli-0.3.0/report-creator.skill +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/__init__.py +1 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/cli.py +5 -122
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/auth.py +234 -33
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/branch.py +3 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/crm.py +40 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/datasource.py +314 -89
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/env.py +3 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/odata.py +148 -22
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/proxy.py +4 -9
- datex_studio_cli-0.3.0/src/dxs/commands/release_notes.py +376 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/report.py +319 -54
- datex_studio_cli-0.3.0/src/dxs/commands/schema.py +5050 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/servicepack.py +3 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/source.py +246 -162
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/studio.py +1 -3
- datex_studio_cli-0.3.0/src/dxs/context.py +133 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/api/client.py +123 -43
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/api/endpoints.py +124 -34
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/api/metadata_models.py +9 -3
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/auth/token_cache.py +72 -2
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/datasource/generator.py +134 -34
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/datasource/models.py +5 -4
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/datasource/validator.py +67 -13
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/dynamics/client.py +34 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/footprint/client.py +39 -12
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/footprint/edmx_parser.py +2 -1
- datex_studio_cli-0.3.0/src/dxs/core/release_notes.py +324 -0
- datex_studio_cli-0.3.0/src/dxs/report/datasource_binding.py +108 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/engine.py +6 -2
- {datex_studio_cli-0.2.1/src/dxs/core/report_data → datex_studio_cli-0.3.0/src/dxs/report}/field_parser.py +3 -1
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/preview.py +97 -43
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/schema.py +2 -5
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/validator.py +111 -80
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/config.py +5 -3
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/resolvers.py +25 -9
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/app.py +36 -12
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/proxy_app.py +49 -28
- datex_studio_cli-0.2.1/code-review-process.md +0 -206
- datex_studio_cli-0.2.1/release-notes-process.md +0 -273
- datex_studio_cli-0.2.1/release_notes/2026-01-15_summary.md +0 -107
- datex_studio_cli-0.2.1/src/dxs/commands/report_data.py +0 -704
- datex_studio_cli-0.2.1/src/dxs/commands/schema.py +0 -3199
- datex_studio_cli-0.2.1/src/dxs/core/report_data/__init__.py +0 -5
- datex_studio_cli-0.2.1/src/dxs/core/report_data/generator.py +0 -802
- datex_studio_cli-0.2.1/walmart-mixed-pallet-v2.rdlx-json +0 -123
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/__main__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/api.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/config.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/devops.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/document.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/explore.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/marketplace.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/organization.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/repo.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/commands/user.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/api/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/api/models.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/auth/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/auth/decorators.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/auth/msal_client.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/cache.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/datasource/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/devops/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/devops/client.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/devops/models.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/dynamics/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/footprint/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/footprint/metadata.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/graph.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/output/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/output/csv_fmt.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/output/formatter.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/output/json_fmt.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/output/yaml_fmt.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/core/responses.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/models/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/models.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/templates/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/report/wrapper.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/click_options.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/errors.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/filtering.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/paths.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/responses.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/restricted.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/utils/sorting.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/__init__.py +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/404.html +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/RIG3nV-yzLdq0OlVxUM6i/_buildManifest.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/RIG3nV-yzLdq0OlVxUM6i/_ssgManifest.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/255-67e8754147461423.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/841-8775e8c75ff8c949.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/871-370fd7261c4ea9bc.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/app/layout-fb92e6e3144d6d3a.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/app/page-b5e545d0d4880f6f.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/main-1a0dcce460eb61ce.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/_next/static/css/686bbface7277f83.css +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/index.html +0 -0
- {datex_studio_cli-0.2.1 → datex_studio_cli-0.3.0}/src/dxs/web/static/index.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datex-studio-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: click>=8.1.0
|
|
23
|
+
Requires-Dist: defusedxml>=0.7.0
|
|
23
24
|
Requires-Dist: httpx>=0.27.0
|
|
24
25
|
Requires-Dist: markdownify>=0.13.0
|
|
25
26
|
Requires-Dist: msal>=1.28.0
|
|
@@ -487,7 +488,7 @@ dxs crm metadata incident --relationships
|
|
|
487
488
|
| Command | Description | Performance |
|
|
488
489
|
|---------|-------------|-------------|
|
|
489
490
|
| `search <keyword>` | Search entities by name (server-side filtered) | ~200 tokens, <1s |
|
|
490
|
-
| `
|
|
491
|
+
| `entities` | List all entities in the model | ~5000 tokens, <1s |
|
|
491
492
|
| `enums` | List enumeration types | ~500 tokens, <1s |
|
|
492
493
|
| `actions` | List server-side actions (POST operations) | ~300 tokens, <1s |
|
|
493
494
|
| `functions` | List server-side functions (GET operations) | ~300 tokens, <1s |
|
|
@@ -498,8 +499,10 @@ dxs crm metadata incident --relationships
|
|
|
498
499
|
|
|
499
500
|
| Command | Description | Performance |
|
|
500
501
|
|---------|-------------|-------------|
|
|
501
|
-
| `describe <entity>` | Show full entity schema (properties, types, navigation) | 1,000-50,000 tokens, <2s |
|
|
502
|
-
| `relationships <entity>` | Show navigation properties and relationships | ~1000 tokens, <2s |
|
|
502
|
+
| `describe-entity <entity>` | Show full entity schema (properties, types, navigation) | 1,000-50,000 tokens, <2s |
|
|
503
|
+
| `describe-relationships <entity>` | Show navigation properties and relationships | ~1000 tokens, <2s |
|
|
504
|
+
| `describe-properties <type> <prop>` | Show details for a specific property (by composite key) | ~100 tokens, <1s |
|
|
505
|
+
| `describe-navigation-properties <type> <prop>` | Show details for a specific navigation property (by composite key) | ~100 tokens, <1s |
|
|
503
506
|
| `describe-enum <enum>` | Show enum members and values | ~200 tokens, <1s |
|
|
504
507
|
| `describe-action <action>` | Show action parameters and return type | ~300 tokens, <1s |
|
|
505
508
|
| `describe-function <func>` | Show function parameters and return type | ~300 tokens, <1s |
|
|
@@ -519,10 +522,10 @@ dxs crm metadata incident --relationships
|
|
|
519
522
|
dxs schema search "invoice" -c 1
|
|
520
523
|
|
|
521
524
|
# 2. DESCRIBE structure (understand properties and keys)
|
|
522
|
-
dxs schema describe Invoices -c 1 | grep -A 50 "navigation_properties:"
|
|
525
|
+
dxs schema describe-entity Invoices -c 1 | grep -A 50 "navigation_properties:"
|
|
523
526
|
|
|
524
527
|
# 3. EXPLORE relationships (find what can be expanded)
|
|
525
|
-
dxs schema relationships Invoices -c 1
|
|
528
|
+
dxs schema describe-relationships Invoices -c 1
|
|
526
529
|
|
|
527
530
|
# 4. TEST queries incrementally (avoid 400 errors)
|
|
528
531
|
dxs odata execute -c 1 -q "Invoices(123)?\$expand=InvoiceLines"
|
|
@@ -550,20 +553,20 @@ This nested expansion returns:
|
|
|
550
553
|
|
|
551
554
|
```bash
|
|
552
555
|
# DON'T: List all entities (returns thousands)
|
|
553
|
-
dxs schema
|
|
556
|
+
dxs schema entities -c 1 # ~5000 tokens
|
|
554
557
|
|
|
555
558
|
# DO: Search for specific entities (returns ~20 matches)
|
|
556
559
|
dxs schema search "order" -c 1 # ~200 tokens
|
|
557
560
|
|
|
558
561
|
# Use --count flag for count-only queries (minimal tokens)
|
|
559
|
-
dxs schema
|
|
562
|
+
dxs schema entities --count -c 1 # Returns: count: 150
|
|
560
563
|
dxs schema search "invoice" --count -c 1 # Returns: count: 10
|
|
561
564
|
|
|
562
565
|
# Filter large describe output with grep
|
|
563
|
-
dxs schema describe EntityName -c 1 | grep -A 50 "navigation_properties:"
|
|
566
|
+
dxs schema describe-entity EntityName -c 1 | grep -A 50 "navigation_properties:"
|
|
564
567
|
|
|
565
568
|
# Save large schemas to file for analysis
|
|
566
|
-
dxs schema describe EntityName -c 1 > schema.yaml 2>&1
|
|
569
|
+
dxs schema describe-entity EntityName -c 1 > schema.yaml 2>&1
|
|
567
570
|
```
|
|
568
571
|
|
|
569
572
|
**Query Building Best Practices:**
|
|
@@ -572,7 +575,7 @@ dxs schema describe EntityName -c 1 > schema.yaml 2>&1
|
|
|
572
575
|
✅ Test `$expand` incrementally: single → multiple → nested
|
|
573
576
|
✅ Use `$select` to limit fields: `Entity?$select=Id,Name,Date`
|
|
574
577
|
✅ Use bulk queries: `Entity?$filter=Id in (1,2,3)` vs one-by-one
|
|
575
|
-
✅ Start with `schema search` (not `
|
|
578
|
+
✅ Start with `schema search` (not `entities`) when looking for entities
|
|
576
579
|
|
|
577
580
|
See [Schema Discovery Guide](docs/schema-discovery-guide.md) for command reference and [Schema Discovery Examples](docs/schema-discovery-examples.md) for complete real-world walkthroughs.
|
|
578
581
|
|
|
@@ -443,7 +443,7 @@ dxs crm metadata incident --relationships
|
|
|
443
443
|
| Command | Description | Performance |
|
|
444
444
|
|---------|-------------|-------------|
|
|
445
445
|
| `search <keyword>` | Search entities by name (server-side filtered) | ~200 tokens, <1s |
|
|
446
|
-
| `
|
|
446
|
+
| `entities` | List all entities in the model | ~5000 tokens, <1s |
|
|
447
447
|
| `enums` | List enumeration types | ~500 tokens, <1s |
|
|
448
448
|
| `actions` | List server-side actions (POST operations) | ~300 tokens, <1s |
|
|
449
449
|
| `functions` | List server-side functions (GET operations) | ~300 tokens, <1s |
|
|
@@ -454,8 +454,10 @@ dxs crm metadata incident --relationships
|
|
|
454
454
|
|
|
455
455
|
| Command | Description | Performance |
|
|
456
456
|
|---------|-------------|-------------|
|
|
457
|
-
| `describe <entity>` | Show full entity schema (properties, types, navigation) | 1,000-50,000 tokens, <2s |
|
|
458
|
-
| `relationships <entity>` | Show navigation properties and relationships | ~1000 tokens, <2s |
|
|
457
|
+
| `describe-entity <entity>` | Show full entity schema (properties, types, navigation) | 1,000-50,000 tokens, <2s |
|
|
458
|
+
| `describe-relationships <entity>` | Show navigation properties and relationships | ~1000 tokens, <2s |
|
|
459
|
+
| `describe-properties <type> <prop>` | Show details for a specific property (by composite key) | ~100 tokens, <1s |
|
|
460
|
+
| `describe-navigation-properties <type> <prop>` | Show details for a specific navigation property (by composite key) | ~100 tokens, <1s |
|
|
459
461
|
| `describe-enum <enum>` | Show enum members and values | ~200 tokens, <1s |
|
|
460
462
|
| `describe-action <action>` | Show action parameters and return type | ~300 tokens, <1s |
|
|
461
463
|
| `describe-function <func>` | Show function parameters and return type | ~300 tokens, <1s |
|
|
@@ -475,10 +477,10 @@ dxs crm metadata incident --relationships
|
|
|
475
477
|
dxs schema search "invoice" -c 1
|
|
476
478
|
|
|
477
479
|
# 2. DESCRIBE structure (understand properties and keys)
|
|
478
|
-
dxs schema describe Invoices -c 1 | grep -A 50 "navigation_properties:"
|
|
480
|
+
dxs schema describe-entity Invoices -c 1 | grep -A 50 "navigation_properties:"
|
|
479
481
|
|
|
480
482
|
# 3. EXPLORE relationships (find what can be expanded)
|
|
481
|
-
dxs schema relationships Invoices -c 1
|
|
483
|
+
dxs schema describe-relationships Invoices -c 1
|
|
482
484
|
|
|
483
485
|
# 4. TEST queries incrementally (avoid 400 errors)
|
|
484
486
|
dxs odata execute -c 1 -q "Invoices(123)?\$expand=InvoiceLines"
|
|
@@ -506,20 +508,20 @@ This nested expansion returns:
|
|
|
506
508
|
|
|
507
509
|
```bash
|
|
508
510
|
# DON'T: List all entities (returns thousands)
|
|
509
|
-
dxs schema
|
|
511
|
+
dxs schema entities -c 1 # ~5000 tokens
|
|
510
512
|
|
|
511
513
|
# DO: Search for specific entities (returns ~20 matches)
|
|
512
514
|
dxs schema search "order" -c 1 # ~200 tokens
|
|
513
515
|
|
|
514
516
|
# Use --count flag for count-only queries (minimal tokens)
|
|
515
|
-
dxs schema
|
|
517
|
+
dxs schema entities --count -c 1 # Returns: count: 150
|
|
516
518
|
dxs schema search "invoice" --count -c 1 # Returns: count: 10
|
|
517
519
|
|
|
518
520
|
# Filter large describe output with grep
|
|
519
|
-
dxs schema describe EntityName -c 1 | grep -A 50 "navigation_properties:"
|
|
521
|
+
dxs schema describe-entity EntityName -c 1 | grep -A 50 "navigation_properties:"
|
|
520
522
|
|
|
521
523
|
# Save large schemas to file for analysis
|
|
522
|
-
dxs schema describe EntityName -c 1 > schema.yaml 2>&1
|
|
524
|
+
dxs schema describe-entity EntityName -c 1 > schema.yaml 2>&1
|
|
523
525
|
```
|
|
524
526
|
|
|
525
527
|
**Query Building Best Practices:**
|
|
@@ -528,7 +530,7 @@ dxs schema describe EntityName -c 1 > schema.yaml 2>&1
|
|
|
528
530
|
✅ Test `$expand` incrementally: single → multiple → nested
|
|
529
531
|
✅ Use `$select` to limit fields: `Entity?$select=Id,Name,Date`
|
|
530
532
|
✅ Use bulk queries: `Entity?$filter=Id in (1,2,3)` vs one-by-one
|
|
531
|
-
✅ Start with `schema search` (not `
|
|
533
|
+
✅ Start with `schema search` (not `entities`) when looking for entities
|
|
532
534
|
|
|
533
535
|
See [Schema Discovery Guide](docs/schema-discovery-guide.md) for command reference and [Schema Discovery Examples](docs/schema-discovery-examples.md) for complete real-world walkthroughs.
|
|
534
536
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "datex-studio-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
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"
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"pydantic>=2.5.0",
|
|
34
34
|
"pydantic-settings>=2.0.0",
|
|
35
35
|
"pyyaml>=6.0",
|
|
36
|
+
"defusedxml>=0.7.0",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.optional-dependencies]
|
|
@@ -125,6 +126,10 @@ plugins = ["pydantic.mypy"]
|
|
|
125
126
|
module = ["msal.*"]
|
|
126
127
|
ignore_missing_imports = true
|
|
127
128
|
|
|
129
|
+
[[tool.mypy.overrides]]
|
|
130
|
+
module = ["defusedxml.*"]
|
|
131
|
+
ignore_missing_imports = true
|
|
132
|
+
|
|
128
133
|
[[tool.mypy.overrides]]
|
|
129
134
|
module = ["fastapi.*", "uvicorn.*"]
|
|
130
135
|
ignore_missing_imports = true
|
|
Binary file
|
|
@@ -7,10 +7,14 @@ from typing import Any, TypeVar, cast
|
|
|
7
7
|
import click
|
|
8
8
|
|
|
9
9
|
from dxs import __app_name__, __version__
|
|
10
|
-
from dxs.
|
|
10
|
+
from dxs.context import DxsContext, pass_context # re-exported for backward compat
|
|
11
|
+
from dxs.core.output import OutputFormat, format_error
|
|
11
12
|
from dxs.utils.config import get_settings
|
|
12
13
|
from dxs.utils.errors import DxsError
|
|
13
14
|
|
|
15
|
+
# Re-export so that existing `from dxs.cli import DxsContext, pass_context` still works
|
|
16
|
+
__all__ = ["DxsContext", "pass_context"]
|
|
17
|
+
|
|
14
18
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
15
19
|
|
|
16
20
|
# Fields to strip from author objects in concise mode
|
|
@@ -45,125 +49,6 @@ def _strip_concise(data: Any, parent_key: str | None = None) -> Any:
|
|
|
45
49
|
return data
|
|
46
50
|
|
|
47
51
|
|
|
48
|
-
class DxsContext:
|
|
49
|
-
"""Shared context passed to all commands."""
|
|
50
|
-
|
|
51
|
-
def __init__(self) -> None:
|
|
52
|
-
self.output_format: OutputFormat = OutputFormat.YAML
|
|
53
|
-
self.verbose: bool = False
|
|
54
|
-
self.quiet: bool = False
|
|
55
|
-
self.concise: bool = True # Concise output is default
|
|
56
|
-
self.org: str | None = None
|
|
57
|
-
self.env: str | None = None
|
|
58
|
-
self.branch: int | None = None
|
|
59
|
-
self.repo: int | None = None
|
|
60
|
-
self.save_path: str | None = None
|
|
61
|
-
self.force_overwrite: bool = False
|
|
62
|
-
self.explicit_output_format: bool = False
|
|
63
|
-
|
|
64
|
-
def output(self, data: Any, include_metadata: bool = True) -> None:
|
|
65
|
-
"""Output data in the configured format.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
data: Data to output.
|
|
69
|
-
include_metadata: Include timestamp and version metadata.
|
|
70
|
-
"""
|
|
71
|
-
from dxs.core.output.formatter import detect_format_from_extension
|
|
72
|
-
|
|
73
|
-
# Determine format: --output flag > file extension > default
|
|
74
|
-
if self.save_path and not self.explicit_output_format:
|
|
75
|
-
output_format = detect_format_from_extension(self.save_path, self.output_format)
|
|
76
|
-
else:
|
|
77
|
-
output_format = self.output_format
|
|
78
|
-
|
|
79
|
-
formatted = format_output(data, output_format, include_metadata, self.concise)
|
|
80
|
-
|
|
81
|
-
# Save to file if requested (suppresses stdout)
|
|
82
|
-
if self.save_path:
|
|
83
|
-
self._save_to_file(formatted)
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
click.echo(formatted)
|
|
87
|
-
|
|
88
|
-
def _save_to_file(self, content: str) -> None:
|
|
89
|
-
"""Save content to file with overwrite protection.
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
content: Formatted content to save.
|
|
93
|
-
|
|
94
|
-
Raises:
|
|
95
|
-
ValidationError: If file exists and force_overwrite is False.
|
|
96
|
-
DxsError: If file write fails.
|
|
97
|
-
RestrictedModeError: If restricted mode is enabled.
|
|
98
|
-
"""
|
|
99
|
-
from pathlib import Path
|
|
100
|
-
|
|
101
|
-
from dxs.utils.config import is_restricted_mode
|
|
102
|
-
from dxs.utils.errors import RestrictedModeError, ValidationError
|
|
103
|
-
|
|
104
|
-
# Block file saving in restricted mode
|
|
105
|
-
if is_restricted_mode():
|
|
106
|
-
raise RestrictedModeError(
|
|
107
|
-
command_name="--save option",
|
|
108
|
-
reason="writes files to the filesystem",
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
assert self.save_path is not None # Caller checks this
|
|
112
|
-
path = Path(self.save_path)
|
|
113
|
-
|
|
114
|
-
if path.exists() and not self.force_overwrite:
|
|
115
|
-
raise ValidationError(
|
|
116
|
-
message=f"File '{self.save_path}' already exists",
|
|
117
|
-
code="DXS-FILE-001",
|
|
118
|
-
suggestions=[
|
|
119
|
-
"Use --force flag to overwrite the file",
|
|
120
|
-
"Specify a different filename with --save",
|
|
121
|
-
],
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
-
|
|
126
|
-
try:
|
|
127
|
-
path.write_text(content, encoding="utf-8")
|
|
128
|
-
self.debug(f"Output saved to: {self.save_path}")
|
|
129
|
-
except OSError as e:
|
|
130
|
-
raise DxsError(
|
|
131
|
-
code="DXS-FILE-002",
|
|
132
|
-
message=f"Failed to write file '{self.save_path}': {e}",
|
|
133
|
-
) from e
|
|
134
|
-
|
|
135
|
-
def output_error(self, error: DxsError | Exception) -> None:
|
|
136
|
-
"""Output an error in the configured format.
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
error: Error to output.
|
|
140
|
-
"""
|
|
141
|
-
formatted = format_error(error, self.output_format)
|
|
142
|
-
click.echo(formatted, err=True)
|
|
143
|
-
|
|
144
|
-
def log(self, message: str) -> None:
|
|
145
|
-
"""Log a message (respects quiet flag).
|
|
146
|
-
|
|
147
|
-
Args:
|
|
148
|
-
message: Message to log.
|
|
149
|
-
"""
|
|
150
|
-
if not self.quiet:
|
|
151
|
-
click.echo(message, err=True)
|
|
152
|
-
|
|
153
|
-
def debug(self, message: str) -> None:
|
|
154
|
-
"""Log a debug message (only in verbose mode).
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
message: Debug message to log.
|
|
158
|
-
"""
|
|
159
|
-
if self.verbose:
|
|
160
|
-
click.echo(f"[DEBUG] {message}", err=True)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
# Create a pass decorator for the context
|
|
164
|
-
pass_context = click.make_pass_decorator(DxsContext, ensure=True)
|
|
165
|
-
|
|
166
|
-
|
|
167
52
|
# Global options shared across all commands
|
|
168
53
|
CONTEXT_SETTINGS = {
|
|
169
54
|
"help_option_names": ["-h", "--help"],
|
|
@@ -459,7 +344,6 @@ def register_commands() -> None:
|
|
|
459
344
|
organization,
|
|
460
345
|
proxy,
|
|
461
346
|
report,
|
|
462
|
-
report_data,
|
|
463
347
|
source,
|
|
464
348
|
studio,
|
|
465
349
|
user,
|
|
@@ -477,7 +361,6 @@ def register_commands() -> None:
|
|
|
477
361
|
cli.add_command(organization.organization)
|
|
478
362
|
cli.add_command(proxy.proxy)
|
|
479
363
|
cli.add_command(report.report)
|
|
480
|
-
cli.add_command(report_data.report_data)
|
|
481
364
|
cli.add_command(source.source)
|
|
482
365
|
cli.add_command(studio.studio)
|
|
483
366
|
cli.add_command(user.user)
|