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: CHANGELOG.md
+25-23Lines changed: 25 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,43 +5,45 @@ All notable changes to this project will be documented in this file.
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
8
-
## [1.0.0] - 2026-05-25
8
+
## [1.0.0] - 2026-05-28
9
9
10
10
### Added
11
-
- Full async client: `AsyncDataverseClient` with complete feature parity to the sync SDK — all CRUD, query, batch, file, and DataFrame operations available as `async def`; install via `pip install PowerPlatform-Dataverse-Client[async]` and use `async with AsyncDataverseClient(...)` (#171)
12
-
-`client.records.retrieve(table, record_id, *, select, expand, include_annotations)` — fetch a single record by GUID; returns `None` on 404 instead of raising (#175)
13
-
-`client.records.list(table, *, filter, select, top, orderby, expand, page_size, count, include_annotations)` — eager fetch returning a flat `QueryResult`; GA replacement for `records.get()` without a record ID (#175)
14
-
-`client.records.list_pages(table, *, ...)` — lazy iterator yielding one `QueryResult` per HTTP page; streaming counterpart to `list()` (#175)
15
-
-`client.query.fetchxml(xml)` — FetchXML query support returning an inert `FetchXmlQuery`; no HTTP request is made until `.execute()` or `.execute_pages()` is called; implements the correct Dataverse paging cookie algorithm with a 10,000-page circuit breaker (#175)
-`QueryBuilder.where(expr)` — composable filter expressions using `col()` and Python operators (`==`, `!=`, `>`, `>=`, `<`, `<=`, `&`, `|`, `~`); replaces the deprecated `filter_eq()`, `filter_contains()`, and other `filter_*` helpers (#175)
18
-
-`QueryResult[0]` index access returns a `Record`; `QueryResult[1:5]` slice access returns a new `QueryResult` (#175)
19
-
-`DataverseModel` structural `Protocol` — implement on any entity class to enable typed integration with CRUD operations without specifying table names or serializing manually (#175)
20
-
-`col()`, `raw()`, `QueryResult`, and `DataverseModel` are now importable directly from `PowerPlatform.Dataverse` (#175)
21
-
- Shorter import paths: `Record`, `DataverseError`, `QueryBuilder`, and other public types are now importable directly from `PowerPlatform.Dataverse.models` and `PowerPlatform.Dataverse.core` without navigating to submodule paths (#165)
22
-
- v0→v1 migration tool: `dataverse-migrate` console script (also `python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1`) rewrites v0 call sites to the v1 API with `--dry-run` support; auto-rewrites `QueryBuilder.to_dataframe()` calls; marks files requiring manual attention with `[NEEDS-MANUAL]`; requires `pip install PowerPlatform-Dataverse-Client[migration]` (#175, #184)
11
+
-`AsyncDataverseClient` — full async counterpart to `DataverseClient`; all operation namespaces (`records`, `query`, `tables`, `files`, `batch`, `dataframe`) exposed as `async def` methods with `async with` lifecycle management; requires `pip install PowerPlatform-Dataverse-Client[async]` (#171)
12
+
-`client.records.retrieve(table, record_id, *, select, expand, include_annotations)` — fetch a single record by GUID; returns `None` on 404 instead of raising; `expand` adds `$expand` for navigation property expansion on the single-record GET; `include_annotations` maps to the `Prefer: odata.include-annotations` header for formatted values and lookup labels (#175)
13
+
-`client.records.list(table, *, filter, select, top, orderby, expand, page_size, count, include_annotations)` — eager fetch returning a flat `QueryResult`; GA replacement for `records.get()` without a record ID; `page_size` controls `Prefer: odata.maxpagesize`, `count=True` adds `$count=true`, `include_annotations` requests formatted values (#175)
14
+
-`client.records.list_pages(table, *, filter, select, top, orderby, expand, page_size, count, include_annotations)` — lazy iterator yielding one `QueryResult` per HTTP page; streaming counterpart to `list()`; same parameter set (#175)
15
+
-`client.query.fetchxml(xml)` — FetchXML support returning an inert `FetchXmlQuery`; no HTTP request is made until `.execute()` or `.execute_pages()` is called (#175)
16
+
-`FetchXmlQuery` implements the correct Dataverse paging cookie algorithm: annotation parsed as outer XML, `pagingcookie` attribute double URL-decoded, server-supplied `pagenumber` used for next page, `morerecords` handled as both `bool` and `"true"` string, `UserWarning` emitted on simple paging fallback, 32,768-character URL limit enforced (documented Dataverse GET cap), 10,000-page circuit breaker against runaway iteration (#175)
17
+
-`QueryBuilder.execute_pages()` — lazy per-page streaming returning one `QueryResult` per HTTP page; replaces deprecated `execute(by_page=True)` (#175)
18
+
-`QueryBuilder.where()` — composable filter expressions using `col()` and Python operators (`==`, `>`, `&`, `|`, `~`); replaces deprecated `filter_eq()`, `filter_contains()`, and other `filter_*` helpers (#175)
19
+
-`QueryResult.__getitem__` — index access (`result[0]`) returns a `Record`; slice access (`result[1:5]`) returns a new `QueryResult` (#175)
20
+
-`DataverseModel` structural `Protocol` (`models/protocol.py`) — implement on any entity class to enable typed integration with CRUD operations without specifying table names or serializing manually (#175)
21
+
-`col()`, `raw()`, `QueryResult`, and `DataverseModel` exported from the top-level `PowerPlatform.Dataverse` package (#175)
22
+
- v0→v1 migration tool: installed as the `dataverse-migrate` console script (also runnable via `python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1`); rewrites v0 call sites to the v1 API with `--dry-run` support; covers `create`, `update`, `delete`, `get`, `list`, `fetchxml`, and query builder patterns; requires the `[migration]` optional extra (`pip install PowerPlatform-Dataverse-Client[migration]`) (#175)
23
+
- Migration tool now auto-rewrites `QueryBuilder.to_dataframe()` → `.execute().to_dataframe()` (inserts `.execute()` when receiver is a recognised builder chain); output improved with `[NEEDS-MANUAL]` label for files that have no auto-rewrites but require manual attention, and a trailing note on `[MIGRATED]` lines when manual items remain (#175)
24
+
- Public types (`Record`, `DataverseError`, `QueryBuilder`, `BatchResult`, and others) are now importable directly from `PowerPlatform.Dataverse.models`, `PowerPlatform.Dataverse.core`, and `PowerPlatform.Dataverse.operations` without navigating into submodule paths (#165)
23
25
24
26
### Changed
25
27
-`QueryBuilder.execute()` now returns a flat `QueryResult` (all pages collected eagerly) instead of `Iterable[Record]` (#175)
26
-
-`records.get()` deprecation extended: calling with a `record_id` directs to `retrieve()`; calling without one directs to `list()` (#175)
28
+
-`records.get()` deprecation extended: calling with a `record_id` emits `DeprecationWarning` directing callers to `retrieve()`; calling without a `record_id` directs callers to `list()` (#175)
29
+
-`OperationContext` now validates keys against an allowlist (`app`, `skill`, `agent`) and values against per-key format rules; unknown keys or non-conforming values raise `ValidationError` at construction time, preventing PII from reaching the `User-Agent` header (#181)
30
+
-`client.tables.create()` now uses the `CreateEntities` API instead of `EntityDefinitions`, improving reliability and aligning with the current Dataverse API contract (#183)
27
31
-`float` and `double` column precision default raised from 2 to 5 decimal places, preventing silent truncation of values like `2.718` (#185)
28
-
- Server-side error detail (`error.innererror.message`) is now included in `HttpError.message` and batch failure details, making the offending field or data type visible without inspecting raw wire payloads (#185)
29
-
-`OperationContext` now validates keys and values against a fixed allowlist (`app`, `skill`, `agent`); unknown keys or non-conforming values raise `ValidationError` at construction time, preventing PII from reaching the `User-Agent` header (#181)
30
-
- Table creation now uses the `CreateEntities` API, improving reliability and aligning with the current Dataverse API contract (#183)
31
-
-`pandas.DataFrame` with MultiIndex columns now raises a descriptive error with a flatten hint at the point of use, instead of producing broken tuple keys deep in the serialization path (#185)
32
+
- Server-side error detail (`error.innererror.message`) is now appended to `HttpError.message` on both single-request and batch paths, surfacing the offending field or data type without inspecting raw wire payloads (#185)
33
+
-`pandas.DataFrame` with MultiIndex columns now raises a clear error with a flatten hint at the point of use, instead of producing broken tuple keys deep in the serialization path (#185)
32
34
33
35
### Deprecated
34
36
-`QueryBuilder.execute(by_page=True)` and `execute(by_page=False)` emit `UserWarning`; use `execute_pages()` and `execute()` respectively (#175)
35
-
-`client.query.odata_select()`, `odata_expands()`, `odata_expand()`, `odata_bind()` emit `DeprecationWarning`; use `QueryBuilder.expand()` instead (#175)
37
+
-`client.query.odata_select()`, `client.query.odata_expands()`, `client.query.odata_expand()`, `client.query.odata_bind()` emit `DeprecationWarning`; navigation property helpers are replaced by `QueryBuilder.expand()` (#175)
36
38
37
39
### Removed
38
-
- All v0 flat methods on `DataverseClient` (`create`, `update`, `delete`, `get`, `list`, `query_sql`, etc.); use the `client.records`, `client.query`, and `client.batch` namespaces (#175)
- All v0 flat methods on `DataverseClient` (`create`, `update`, `delete`, `get`, `list`, `query_sql`, etc.) removed (~570 lines); use the `client.records`, `client.query`, and `client.batch` namespaces (#175)
-`async for record in results:` now works correctly; `QueryResult` was missing `__aiter__`, causing `async for` to fail in async code (#187)
43
-
- Client creation raised an error on Python 3.10 and 3.11; all supported Python versions (3.10–3.14) now work correctly (#188)
44
44
- SQL guardrails now block write statements and statement stacking even when hidden inside comments, string literals, or zero-width prefixes (#185)
45
+
-`records.get()` deprecation warning now names both migration paths: `retrieve()` for single-by-ID lookups and `list(filter=...)` for filtered queries (#185)
46
+
- Fixed client creation error on Python 3.10 and 3.11; all supported Python versions (3.10–3.14) now work correctly (#188)
0 commit comments