You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .claude/skills/dataverse-sdk-dev/SKILL.md
+59-1Lines changed: 59 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,13 +13,39 @@ This skill provides guidance for developers working on the PowerPlatform Dataver
13
13
14
14
### API Design
15
15
16
-
1.**Public methods in operation namespaces** - New public methods go in the appropriate namespace module under `src/PowerPlatform/Dataverse/operations/` (`records.py`, `query.py`, `tables.py`). The `client.py` file exposes these via namespace properties (`client.records`, `client.query`, `client.tables`). Public types and constants live in their own modules (e.g., `models/metadata.py`, `common/constants.py`)
16
+
1.**Public methods in operation namespaces** - New public methods go in the appropriate namespace module under `src/PowerPlatform/Dataverse/operations/` (`records.py`, `query.py`, `tables.py`, `batch.py`). The `client.py` file exposes these via namespace properties (`client.records`, `client.query`, `client.tables`, `client.batch`). Public types and constants live in their own modules (e.g., `models/metadata.py`, `models/batch.py`, `common/constants.py`)
17
17
2.**Every public method needs README example** - Public API methods must have examples in README.md
18
18
3.**Reuse existing APIs** - Always check if an existing method can be used before making direct Web API calls
19
19
4.**Update documentation** when adding features - Keep README and SKILL files (both copies) in sync
6.**Internal vs public naming** - Modules, files, and functions not meant to be part of the public API must use a `_` prefix (e.g., `_odata.py`, `_relationships.py`). Files without the prefix (e.g., `constants.py`, `metadata.py`) are public and importable by SDK consumers
22
22
23
+
### Dataverse Property Naming Rules
24
+
25
+
Dataverse uses two different naming conventions for properties. Getting this wrong causes 400 errors that are hard to debug.
26
+
27
+
| Property type | Name convention | Example | When used |
Navigation property names are case-sensitive and must match the entity's `$metadata`. Using the logical name instead of the navigation property name results in 400 Bad Request errors.
33
+
34
+
**Critical rule:** The OData parser validates `@odata.bind` property names **case-sensitively** against declared navigation properties. Lowercasing `new_CustomerId@odata.bind` to `new_customerid@odata.bind` causes: `ODataException: An undeclared property 'new_customerid' which only has property annotations...`
35
+
36
+
**SDK implementation:**
37
+
38
+
-`_lowercase_keys()` lowercases all keys EXCEPT those containing `@odata.` (preserves navigation property casing in `@odata.bind` keys)
39
+
-`_lowercase_list()` lowercases `$select` and `$orderby` params (structural properties)
40
+
-`$expand` params are passed as-is (navigation properties, PascalCase)
41
+
-`_convert_labels_to_ints()` skips `@odata.` keys entirely (they are annotations, not attributes)
42
+
43
+
**When adding new code that processes record dicts or builds query parameters:**
44
+
45
+
- Always use `_lowercase_keys()` for record payloads. Never manually call `.lower()` on all keys
46
+
- Never lowercase `$expand` values or `@odata.bind` key prefixes
47
+
- If iterating record keys, skip keys containing `@odata.` when doing attribute-level operations
48
+
23
49
### Code Style
24
50
25
51
6.**No emojis** - Do not use emoji in code, comments, or output
@@ -28,3 +54,35 @@ This skill provides guidance for developers working on the PowerPlatform Dataver
28
54
9.**Document public APIs** - Add Sphinx-style docstrings with examples for public methods
29
55
10.**Define __all__ in module files** - Each module declares its own exports via `__all__` (e.g., `errors.py` defines `__all__ = ["HttpError", ...]`). Package `__init__.py` files should not re-export or redefine another module's `__all__`; they use `__all__ = []` to indicate no star-import exports.
30
56
11.**Run black before committing** - Always run `python -m black <changed files>` before committing. CI will reject unformatted code. Config is in `pyproject.toml` under `[tool.black]`.
57
+
58
+
### Docstring Type Annotations (Microsoft Learn Compatibility)
59
+
60
+
This SDK's API reference is published on Microsoft Learn. The Learn doc pipeline parses `:type:` and `:rtype:` directives differently from standard Sphinx -- every word between `:class:` references is treated as a separate cross-reference (`<xref:word>`). Using Sphinx-style `:class:\`list\` of :class:\`str\`` produces broken `<xref:of>` links on Learn.
61
+
62
+
**Rules for `:type:` and `:rtype:` directives:**
63
+
64
+
- Use Python bracket notation for generic types: `list[str]`, `dict[str, typing.Any]`, `list[dict]`
65
+
- Use `or` (without `:class:`) for union types: `str or None`, `dict or list[dict]`
66
+
- Use bracket nesting for complex types: `collections.abc.Iterable[list[dict]]`
67
+
- Use `~` prefix for SDK types to show short name: `list[~PowerPlatform.Dataverse.models.record.Record]`
68
+
-`:class:` is fine for single standalone types: `:class:\`str\``, `:class:\`bool\``
69
+
70
+
**Never** use `:class:\`X\` of :class:\`Y\`` or `:class:\`X\` mapping :class:\`Y\` to :class:\`Z\`` -- the words `of`, `mapping`, `to` become broken `<xref:>` links.
Copy file name to clipboardExpand all lines: .claude/skills/dataverse-sdk-use/SKILL.md
+102-2Lines changed: 102 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,7 @@ Use the PowerPlatform Dataverse Client Python SDK to interact with Microsoft Dat
22
22
-`client.query` -- query and search operations
23
23
-`client.tables` -- table metadata, columns, and relationships
24
24
-`client.files` -- file upload operations
25
+
-`client.batch` -- batch multiple operations into a single HTTP request
25
26
26
27
### Bulk Operations
27
28
The SDK supports Dataverse's native bulk operations: Pass lists to `create()`, `update()` for automatic bulk processing, for `delete()`, set `use_bulk_delete` when passing lists to use bulk operation
@@ -30,6 +31,9 @@ The SDK supports Dataverse's native bulk operations: Pass lists to `create()`, `
30
31
- Control page size with `page_size` parameter
31
32
- Use `top` parameter to limit total records returned
32
33
34
+
### DataFrame Support
35
+
- DataFrame operations are accessed via the `client.dataframe` namespace: `client.dataframe.get()`, `client.dataframe.create()`, `client.dataframe.update()`, `client.dataframe.delete()`
36
+
33
37
## Common Operations
34
38
35
39
### Import
@@ -105,6 +109,20 @@ for page in client.records.get(
Creates or updates records identified by alternate keys. Single item → PATCH; multiple items →`UpsertMultiple` bulk action.
136
+
Creates or updates records identified by alternate keys. Single item -> PATCH; multiple items ->`UpsertMultiple` bulk action.
119
137
> **Prerequisite**: The table must have an alternate key configured in Dataverse for the columns used in `alternate_key`. Without it, Dataverse will reject the request with a 400 error.
120
138
```python
121
139
from PowerPlatform.Dataverse.models.upsert import UpsertItem
SQL queries are **read-only** and support limited SQL syntax. A single SELECT statement with optional WHERE, TOP (integer literal), ORDER BY (column names only), and a simple table alias after FROM is supported. But JOIN and subqueries may not be. Refer to the Dataverse documentation for the current feature set.
Types on the same line map to the same exact format under the hood
198
252
-`"string"` or `"text"` - Single line of text
253
+
-`"memo"` or `"multiline"` - Multiple lines of text (4000 character default)
199
254
-`"int"` or `"integer"` - Whole number
200
255
-`"decimal"` or `"money"` - Decimal number
201
256
-`"float"` or `"double"` - Floating point number
@@ -316,6 +371,50 @@ client.files.upload(
316
371
)
317
372
```
318
373
374
+
### Batch Operations
375
+
376
+
Use `client.batch` to send multiple operations in one HTTP request. All batch methods return `None`; results arrive via `BatchResult` after `execute()`.
-`result.responses` -- list of `BatchItemResponse` in submission order
408
+
-`result.succeeded` -- responses with 2xx status codes
409
+
-`result.failed` -- responses with non-2xx status codes
410
+
-`result.has_errors` -- True if any response failed
411
+
-`result.entity_ids` -- GUIDs from OData-EntityId headers (creates and updates)
412
+
413
+
**Batch limitations:**
414
+
- Maximum 1000 operations per batch
415
+
- Paginated `records.get()` (without `record_id`) is not supported in batch
416
+
-`flush_cache()` is not supported in batch
417
+
319
418
## Error Handling
320
419
321
420
The SDK provides structured exceptions with detailed error information:
@@ -359,6 +458,7 @@ except ValidationError as e:
359
458
- Check filter/expand parameters use correct case
360
459
- Verify column names exist and are spelled correctly
361
460
- Ensure custom columns include customization prefix
461
+
- For `@odata.bind` errors ("undeclared property"): the navigation property name before `@odata.bind` is case-sensitive and must match the entity's `$metadata` exactly (e.g., `new_CustomerId@odata.bind` for custom lookups, `parentaccountid@odata.bind` for system lookups). The SDK preserves `@odata.bind` key casing.
362
462
363
463
## Best Practices
364
464
@@ -371,7 +471,7 @@ except ValidationError as e:
371
471
5.**Use production credentials** - ClientSecretCredential or CertificateCredential for unattended operations
372
472
6.**Error handling** - Implement retry logic for transient errors (`e.is_transient`)
373
473
7.**Always include customization prefix** for custom tables/columns
374
-
8.**Use lowercase** - Generally using lowercase input won't go wrong, except for custom table/column naming
474
+
8.**Use lowercase for column names, match `$metadata` for navigation properties** - Column names in `$select`/`$filter`/record payloads use lowercase LogicalNames. Navigation properties in `$expand` and `@odata.bind` keys are case-sensitive and must match the entity's `$metadata` (PascalCase for custom lookups like `new_CustomerId`, lowercase for system lookups like `parentaccountid`)
375
475
9.**Test in non-production environments** first
376
476
10.**Use named constants** - Import cascade behavior constants from `PowerPlatform.Dataverse.common.constants`
0 commit comments