|
5 | 5 |
|
6 | 6 | from __future__ import annotations |
7 | 7 |
|
8 | | -from typing import Any, AsyncIterator, Dict, List, Optional, Union, overload, TYPE_CHECKING |
| 8 | +from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Union, overload, TYPE_CHECKING |
9 | 9 |
|
10 | | -from ...models.record import Record |
| 10 | +from ...core.errors import HttpError |
| 11 | +from ...models.record import QueryResult, Record |
11 | 12 | from ...models.upsert import UpsertItem |
12 | 13 |
|
13 | 14 | if TYPE_CHECKING: |
@@ -463,6 +464,200 @@ async def _paged() -> AsyncIterator[List[Record]]: |
463 | 464 |
|
464 | 465 | return _paged() |
465 | 466 |
|
| 467 | + # --------------------------------------------------------------- retrieve |
| 468 | + |
| 469 | + async def retrieve( |
| 470 | + self, |
| 471 | + table: str, |
| 472 | + record_id: str, |
| 473 | + *, |
| 474 | + select: Optional[List[str]] = None, |
| 475 | + expand: Optional[List[str]] = None, |
| 476 | + include_annotations: Optional[str] = None, |
| 477 | + ) -> Optional[Record]: |
| 478 | + """Fetch a single record by its GUID, returning ``None`` if not found. |
| 479 | +
|
| 480 | + GA replacement for ``records.get(table, record_id)``. Returns ``None`` |
| 481 | + instead of raising when the record does not exist (HTTP 404). |
| 482 | +
|
| 483 | + :param table: Schema name of the table (e.g. ``"account"``). |
| 484 | + :type table: :class:`str` |
| 485 | + :param record_id: GUID of the record to retrieve. |
| 486 | + :type record_id: :class:`str` |
| 487 | + :param select: Optional list of column logical names to include. |
| 488 | + :type select: list[str] or None |
| 489 | + :param expand: Optional list of navigation properties to expand (e.g. |
| 490 | + ``["primarycontactid"]``). Navigation property names are |
| 491 | + case-sensitive and must match the entity's ``$metadata``. |
| 492 | + :type expand: list[str] or None |
| 493 | + :param include_annotations: OData annotation pattern for the |
| 494 | + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or |
| 495 | + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. |
| 496 | + :type include_annotations: :class:`str` or None |
| 497 | + :return: Typed record, or ``None`` if not found. |
| 498 | + :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None |
| 499 | +
|
| 500 | + Example:: |
| 501 | +
|
| 502 | + record = await client.records.retrieve( |
| 503 | + "account", account_id, |
| 504 | + select=["name", "statuscode"], |
| 505 | + expand=["primarycontactid"], |
| 506 | + include_annotations="OData.Community.Display.V1.FormattedValue", |
| 507 | + ) |
| 508 | + if record is not None: |
| 509 | + contact = record.get("primarycontactid") or {} |
| 510 | + print(contact.get("fullname")) |
| 511 | + """ |
| 512 | + async with self._client._scoped_odata() as od: |
| 513 | + try: |
| 514 | + raw = await od._get(table, record_id, select=select, expand=expand, include_annotations=include_annotations) |
| 515 | + except HttpError as exc: |
| 516 | + if exc.status_code == 404: |
| 517 | + return None |
| 518 | + raise |
| 519 | + return Record.from_api_response(table, raw, record_id=record_id) |
| 520 | + |
| 521 | + # -------------------------------------------------------------------- list |
| 522 | + |
| 523 | + async def list( |
| 524 | + self, |
| 525 | + table: str, |
| 526 | + *, |
| 527 | + filter: Optional[Union[str, Any]] = None, |
| 528 | + select: Optional[List[str]] = None, |
| 529 | + orderby: Optional[List[str]] = None, |
| 530 | + top: Optional[int] = None, |
| 531 | + expand: Optional[List[str]] = None, |
| 532 | + page_size: Optional[int] = None, |
| 533 | + count: bool = False, |
| 534 | + include_annotations: Optional[str] = None, |
| 535 | + ) -> QueryResult: |
| 536 | + """Fetch multiple records and return them as a :class:`QueryResult`. |
| 537 | +
|
| 538 | + GA replacement for ``records.get(table, filter=...)``. All pages are |
| 539 | + collected eagerly and returned as a single :class:`QueryResult`. |
| 540 | +
|
| 541 | + :param table: Schema name of the table (e.g. ``"account"``). |
| 542 | + :type table: :class:`str` |
| 543 | + :param filter: Optional OData filter string or :class:`FilterExpression`. |
| 544 | + :type filter: str or FilterExpression or None |
| 545 | + :param select: Optional list of column logical names to include. |
| 546 | + :type select: list[str] or None |
| 547 | + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). |
| 548 | + :type orderby: list[str] or None |
| 549 | + :param top: Maximum total number of records to return. |
| 550 | + :type top: int or None |
| 551 | + :param expand: Optional list of navigation properties to expand. |
| 552 | + :type expand: list[str] or None |
| 553 | + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. |
| 554 | + :type page_size: int or None |
| 555 | + :param count: If ``True``, adds ``$count=true`` to include a total record count. |
| 556 | + :type count: bool |
| 557 | + :param include_annotations: OData annotation pattern for the |
| 558 | + ``Prefer: odata.include-annotations`` header, or ``None``. |
| 559 | + :type include_annotations: :class:`str` or None |
| 560 | + :return: All matching records collected into a :class:`QueryResult`. |
| 561 | + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` |
| 562 | +
|
| 563 | + Example:: |
| 564 | +
|
| 565 | + result = await client.records.list( |
| 566 | + "account", |
| 567 | + filter="statecode eq 0", |
| 568 | + select=["name", "statuscode"], |
| 569 | + orderby=["name asc"], |
| 570 | + top=100, |
| 571 | + include_annotations="OData.Community.Display.V1.FormattedValue", |
| 572 | + ) |
| 573 | + for record in result: |
| 574 | + print(record["name"]) |
| 575 | + """ |
| 576 | + filter_str: Optional[str] = str(filter) if filter is not None else None |
| 577 | + all_records: List[Record] = [] |
| 578 | + async with self._client._scoped_odata() as od: |
| 579 | + async for page in od._get_multiple( |
| 580 | + table, |
| 581 | + select=select, |
| 582 | + filter=filter_str, |
| 583 | + orderby=orderby, |
| 584 | + top=top, |
| 585 | + expand=expand, |
| 586 | + page_size=page_size, |
| 587 | + count=count, |
| 588 | + include_annotations=include_annotations, |
| 589 | + ): |
| 590 | + all_records.extend(Record.from_api_response(table, row) for row in page) |
| 591 | + return QueryResult(all_records) |
| 592 | + |
| 593 | + # --------------------------------------------------------------- list_pages |
| 594 | + |
| 595 | + async def list_pages( |
| 596 | + self, |
| 597 | + table: str, |
| 598 | + *, |
| 599 | + filter: Optional[Union[str, Any]] = None, |
| 600 | + select: Optional[List[str]] = None, |
| 601 | + orderby: Optional[List[str]] = None, |
| 602 | + top: Optional[int] = None, |
| 603 | + expand: Optional[List[str]] = None, |
| 604 | + page_size: Optional[int] = None, |
| 605 | + count: bool = False, |
| 606 | + include_annotations: Optional[str] = None, |
| 607 | + ) -> AsyncIterator[QueryResult]: |
| 608 | + """Lazily yield one :class:`QueryResult` per HTTP page. |
| 609 | +
|
| 610 | + Streaming counterpart to :meth:`list`. Each iteration triggers one |
| 611 | + network request via ``@odata.nextLink``. One-shot — do not iterate |
| 612 | + more than once. |
| 613 | +
|
| 614 | + :param table: Schema name of the table (e.g. ``"account"``). |
| 615 | + :type table: :class:`str` |
| 616 | + :param filter: Optional OData filter string or :class:`FilterExpression`. |
| 617 | + :type filter: str or FilterExpression or None |
| 618 | + :param select: Optional list of column logical names to include. |
| 619 | + :type select: list[str] or None |
| 620 | + :param orderby: Optional list of sort expressions. |
| 621 | + :type orderby: list[str] or None |
| 622 | + :param top: Maximum total number of records to return. |
| 623 | + :type top: int or None |
| 624 | + :param expand: Optional list of navigation properties to expand. |
| 625 | + :type expand: list[str] or None |
| 626 | + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. |
| 627 | + :type page_size: int or None |
| 628 | + :param count: If ``True``, adds ``$count=true`` to include a total record count. |
| 629 | + :type count: bool |
| 630 | + :param include_annotations: OData annotation pattern for the |
| 631 | + ``Prefer: odata.include-annotations`` header, or ``None``. |
| 632 | + :type include_annotations: :class:`str` or None |
| 633 | + :return: Async iterator of per-page :class:`QueryResult` objects. |
| 634 | + :rtype: AsyncIterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] |
| 635 | +
|
| 636 | + Example:: |
| 637 | +
|
| 638 | + async for page in client.records.list_pages( |
| 639 | + "account", |
| 640 | + filter="statecode eq 0", |
| 641 | + orderby=["name asc"], |
| 642 | + page_size=200, |
| 643 | + ): |
| 644 | + process(page.to_dataframe()) |
| 645 | + """ |
| 646 | + filter_str: Optional[str] = str(filter) if filter is not None else None |
| 647 | + async with self._client._scoped_odata() as od: |
| 648 | + async for page in od._get_multiple( |
| 649 | + table, |
| 650 | + select=select, |
| 651 | + filter=filter_str, |
| 652 | + orderby=orderby, |
| 653 | + top=top, |
| 654 | + expand=expand, |
| 655 | + page_size=page_size, |
| 656 | + count=count, |
| 657 | + include_annotations=include_annotations, |
| 658 | + ): |
| 659 | + yield QueryResult([Record.from_api_response(table, row) for row in page]) |
| 660 | + |
466 | 661 | # ------------------------------------------------------------------ upsert |
467 | 662 |
|
468 | 663 | async def upsert(self, table: str, items: List[Union[UpsertItem, Dict[str, Any]]]) -> None: |
|
0 commit comments