Skip to content

Commit c7ae7a2

Browse files
committed
Merge main into feature/querybuilder-clean
Resolve README.md conflict: combine context manager row with QueryBuilder namespace description.
2 parents 247fae7 + 2af249b commit c7ae7a2

39 files changed

Lines changed: 2753 additions & 360 deletions

.claude/skills/dataverse-sdk-use/SKILL.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ credential = AzureCliCredential()
5353
credential = ClientSecretCredential(tenant_id, client_id, client_secret)
5454
credential = CertificateCredential(tenant_id, client_id, cert_path)
5555

56-
# Create client (no trailing slash on URL!)
56+
# Create client with context manager (recommended -- enables HTTP connection pooling)
57+
# No trailing slash on URL!
58+
with DataverseClient("https://yourorg.crm.dynamics.com", credential) as client:
59+
... # all operations here
60+
# Session closed, caches cleared automatically
61+
62+
# Or without context manager:
5763
client = DataverseClient("https://yourorg.crm.dynamics.com", credential)
5864
```
5965

@@ -232,7 +238,7 @@ client.tables.delete("new_Product")
232238

233239
#### Create One-to-Many Relationship
234240
```python
235-
from PowerPlatform.Dataverse.models.metadata import (
241+
from PowerPlatform.Dataverse.models.relationship import (
236242
LookupAttributeMetadata,
237243
OneToManyRelationshipMetadata,
238244
Label,
@@ -264,7 +270,7 @@ print(f"Created lookup field: {result['lookup_schema_name']}")
264270

265271
#### Create Many-to-Many Relationship
266272
```python
267-
from PowerPlatform.Dataverse.models.metadata import ManyToManyRelationshipMetadata
273+
from PowerPlatform.Dataverse.models.relationship import ManyToManyRelationshipMetadata
268274

269275
relationship = ManyToManyRelationshipMetadata(
270276
schema_name="new_employee_project",
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Warn when ADO pipeline YAML file changes
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- ".azdo/ci-pr.yaml"
7+
8+
permissions:
9+
contents: read
10+
pull-requests: write
11+
12+
jobs:
13+
warn:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Emit warning in logs
17+
run: |
18+
echo "::warning file=.azdo/ci-pr.yaml::This PR changes .azdo/ci-pr.yaml. After merge, Azure DevOps may disable/require approval for the PR pipeline YAML until it is re-enabled/approved."
19+
20+
echo "ADO pipeline: DV-Python-SDK-PullRequest (definitionId=29922)"
21+
echo "https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29922"
22+
23+
- name: Add workflow summary
24+
run: |
25+
{
26+
echo "## ADO PR pipeline YAML change detected"
27+
echo ""
28+
echo "**File changed:** \`.azdo/ci-pr.yaml\`"
29+
echo ""
30+
echo "**Why this matters:** After this is merged, Azure DevOps may disable/require approval for the PR pipeline YAML."
31+
echo ""
32+
echo "**Action required (post-merge):** Re-enable / approve the updated YAML for:"
33+
echo "- **DV-Python-SDK-PullRequest** (definitionId=29922)"
34+
echo "- https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29922"
35+
echo ""
36+
echo "Then trigger a run to confirm PR validation works."
37+
} >> "$GITHUB_STEP_SUMMARY"
38+
39+
- name: Post resolvable PR review comment
40+
env:
41+
GH_TOKEN: ${{ github.token }}
42+
run: |
43+
jq -n \
44+
--arg sha "${{ github.event.pull_request.head.sha }}" \
45+
'{
46+
path: ".azdo/ci-pr.yaml",
47+
subject_type: "file",
48+
commit_id: $sha,
49+
body: "> [!WARNING]\n> **ADO PR pipeline YAML change detected**\n>\n> This PR modifies `.azdo/ci-pr.yaml`. After merge, Azure DevOps may disable or require approval for the PR validation pipeline.\n>\n> **Action required (post-merge):** Re-enable / approve the updated YAML for:\n> - **DV-Python-SDK-PullRequest** (definitionId=29922)\n> - https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29922\n>\n> Please resolve this comment after completing the post-merge steps."
50+
}' | \
51+
gh api \
52+
--method POST \
53+
-H "Accept: application/vnd.github+json" \
54+
/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments \
55+
--input -

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.1.0b5] - 2026-02-27
9+
10+
### Fixed
11+
- UpsertMultiple: exclude alternate key fields from request body (#127). The create path of UpsertMultiple failed with `400 Bad Request` when alternate key column values appeared in both the body and `@odata.id`.
12+
13+
## [0.1.0b4] - 2026-02-25
14+
15+
### Added
16+
- Operation namespaces: `client.records`, `client.query`, `client.tables`, `client.files` (#102)
17+
- Relationship management: `create_one_to_many_relationship`, `create_many_to_many_relationship`, `get_relationship`, `delete_relationship`, `create_lookup_field` with typed `RelationshipInfo` return model (#105, #114)
18+
- `client.records.upsert()` for upsert operations with alternate key support (#106)
19+
- `client.files.upload()` for file upload operations (#111)
20+
- `client.tables.list(filter=, select=)` parameters for filtering and projecting table metadata (#112)
21+
- Cascade behavior constants (`CASCADE_BEHAVIOR_CASCADE`, `CASCADE_BEHAVIOR_REMOVE_LINK`, etc.) and input models (`CascadeConfiguration`, `LookupAttributeMetadata`, `Label`, `LocalizedLabel`)
22+
23+
### Deprecated
24+
- All flat methods on `DataverseClient` (`create`, `update`, `delete`, `get`, `query_sql`, `upload_file`, etc.) now emit `DeprecationWarning` and delegate to the corresponding namespaced operations
25+
26+
## [0.1.0b3] - 2025-12-19
27+
28+
### Added
29+
- Client-side correlation ID and client request ID for request tracing (#70)
30+
- Unit tests for `DataverseClient` (#71)
31+
32+
### Changed
33+
- Standardized package versioning (#84)
34+
- Updated package link (#69)
35+
36+
### Fixed
37+
- Retry logic for examples (#72)
38+
- Removed double space formatting issue (#82)
39+
- Updated CI trigger to include main branch (#81)
40+
41+
## [0.1.0b2] - 2025-11-17
42+
43+
### Added
44+
- Enforce Black formatting across the codebase (#61, #62)
45+
- Python 3.14 support added to `pyproject.toml` (#55)
46+
47+
### Changed
48+
- Removed `pandas` dependency (#57)
49+
- Refactored SDK architecture and quality improvements (#55)
50+
- Prefixed table names with schema name for consistency (#51)
51+
- Updated docstrings across core modules (#54, #63)
52+
53+
### Fixed
54+
- Fixed `get` for single-select option set columns (#52)
55+
- Fixed example filename references and documentation URLs (#60)
56+
- Fixed API documentation link in examples (#64)
57+
- Fixed CI pipeline to use modern `pyproject.toml` dev dependencies (#56, #59)
58+
859
## [0.1.0b1] - 2025-11-14
960

1061
### Added
@@ -19,6 +70,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1970
- Comprehensive error handling with specific exception types (`DataverseError`, `AuthenticationError`, etc.) (#22, #24)
2071
- HTTP retry logic with exponential backoff for resilient operations (#72)
2172

73+
[0.1.0b5]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/v0.1.0b4...v0.1.0b5
74+
[0.1.0b4]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/v0.1.0b3...v0.1.0b4
2275
[0.1.0b3]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/v0.1.0b2...v0.1.0b3
2376
[0.1.0b2]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/v0.1.0b1...v0.1.0b2
2477
[0.1.0b1]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases/tag/v0.1.0b1

CONTRIBUTING.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,16 @@ Brief summary of the release
109109
### Fixed
110110
- Bug fix 1 (#125)
111111
- Bug fix 2 (#126)
112+
```
113+
114+
**Post-Release Version Bump:**
115+
116+
After tagging and publishing a release, immediately bump the version on `main` to the next
117+
development target. This ensures builds from source are clearly distinguished from the
118+
published release:
119+
120+
```bash
121+
# After publishing v0.1.0b4, bump to v0.1.0b5 on main
122+
# Update version in pyproject.toml
123+
# Commit directly to main: "Bump version to 0.1.0b5 for next development cycle"
112124
```

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ The SDK provides a simple, pythonic interface for Dataverse operations:
114114
| Concept | Description |
115115
|---------|-------------|
116116
| **DataverseClient** | Main entry point; provides `records`, `query`, `tables`, and `files` namespaces |
117+
| **Context Manager** | Use `with DataverseClient(...) as client:` for automatic cleanup and HTTP connection pooling |
117118
| **Namespaces** | Operations are organized into `client.records` (CRUD), `client.query` (QueryBuilder & SQL), `client.tables` (metadata), and `client.files` (file uploads) |
118119
| **Records** | Dataverse records represented as Python dictionaries with column schema names |
119120
| **Schema names** | Use table schema names (`"account"`, `"new_MyTestTable"`) and column schema names (`"name"`, `"new_MyTestColumn"`). See: [Table definitions in Microsoft Dataverse](https://learn.microsoft.com/en-us/power-apps/developer/data-platform/entity-metadata) |
@@ -132,17 +133,18 @@ from PowerPlatform.Dataverse.client import DataverseClient
132133

133134
# Connect to Dataverse
134135
credential = InteractiveBrowserCredential()
135-
client = DataverseClient("https://yourorg.crm.dynamics.com", credential)
136136

137-
# Create a contact
138-
contact_id = client.records.create("contact", {"firstname": "John", "lastname": "Doe"})
137+
with DataverseClient("https://yourorg.crm.dynamics.com", credential) as client:
138+
# Create a contact
139+
contact_id = client.records.create("contact", {"firstname": "John", "lastname": "Doe"})
139140

140-
# Read the contact back
141-
contact = client.records.get("contact", contact_id, select=["firstname", "lastname"])
142-
print(f"Created: {contact['firstname']} {contact['lastname']}")
141+
# Read the contact back
142+
contact = client.records.get("contact", contact_id, select=["firstname", "lastname"])
143+
print(f"Created: {contact['firstname']} {contact['lastname']}")
143144

144-
# Clean up
145-
client.records.delete("contact", contact_id)
145+
# Clean up
146+
client.records.delete("contact", contact_id)
147+
# Session closed, caches cleared automatically
146148
```
147149

148150
### Basic CRUD operations
@@ -359,13 +361,12 @@ client.tables.delete("new_Product")
359361
Create relationships between tables using the relationship API. For a complete working example, see [examples/advanced/relationships.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/advanced/relationships.py).
360362

361363
```python
362-
from PowerPlatform.Dataverse.models.metadata import (
364+
from PowerPlatform.Dataverse.models.relationship import (
363365
LookupAttributeMetadata,
364366
OneToManyRelationshipMetadata,
365367
ManyToManyRelationshipMetadata,
366-
Label,
367-
LocalizedLabel,
368368
)
369+
from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel
369370

370371
# Create a one-to-many relationship: Department (1) -> Employee (N)
371372
# This adds a "Department" lookup field to the Employee table

0 commit comments

Comments
 (0)