Office365-REST-Python-Client 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl

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.
Files changed (245) hide show
  1. office365/communications/callrecords/call_record.py +18 -0
  2. office365/communications/calls/call.py +3 -0
  3. office365/communications/presences/presence.py +3 -0
  4. office365/communications/quotas.py +66 -0
  5. office365/communications/virtualevents/virtual_event.py +3 -0
  6. office365/directory/domains/domain.py +3 -0
  7. office365/directory/licenses/subscribed_sku.py +3 -0
  8. office365/directory/objects/object.py +3 -0
  9. office365/directory/quotas.py +34 -0
  10. office365/entity_collection.py +2 -2
  11. office365/graph_client.py +46 -2
  12. office365/limits.py +66 -0
  13. office365/migration/__init__.py +6 -1
  14. office365/migration/adapters/__init__.py +14 -4
  15. office365/migration/adapters/filesystem.py +4 -1
  16. office365/migration/assessment/export.py +5 -5
  17. office365/migration/assessment/issue.py +19 -0
  18. office365/migration/assessment/report.py +11 -0
  19. office365/migration/assessment/scanners/base.py +37 -49
  20. office365/migration/base.py +33 -8
  21. office365/migration/checkpoint.py +1 -1
  22. office365/migration/outlook/assessor.py +4 -4
  23. office365/migration/package/__init__.py +82 -0
  24. office365/migration/package/builder.py +603 -0
  25. office365/migration/package/crypto.py +54 -0
  26. office365/migration/package/manifest_xml.py +495 -0
  27. office365/migration/package/staging.py +180 -0
  28. office365/migration/report.py +7 -2
  29. office365/migration/report_io.py +79 -0
  30. office365/migration/runner.py +117 -10
  31. office365/migration/server_job.py +126 -14
  32. office365/migration/session.py +132 -49
  33. office365/migration/settings.py +67 -0
  34. office365/migration/sharepoint/__init__.py +2 -0
  35. office365/migration/sharepoint/adapters.py +207 -41
  36. office365/migration/sharepoint/assessor.py +45 -9
  37. office365/migration/sharepoint/options.py +57 -0
  38. office365/migration/sharepoint/package_target.py +209 -0
  39. office365/migration/sharepoint/registry.py +22 -0
  40. office365/migration/sharepoint/resolvers.py +96 -0
  41. office365/migration/sharepoint/scanners/__init__.py +29 -0
  42. office365/migration/sharepoint/scanners/browser_file_handling.py +55 -0
  43. office365/migration/sharepoint/scanners/checked_out_files.py +57 -0
  44. office365/migration/sharepoint/scanners/file_versions.py +56 -0
  45. office365/migration/sharepoint/scanners/files.py +5 -2
  46. office365/migration/sharepoint/scanners/large_excel_files.py +56 -0
  47. office365/migration/sharepoint/scanners/large_lists.py +58 -0
  48. office365/migration/sharepoint/scanners/large_sites.py +4 -3
  49. office365/migration/sharepoint/scanners/long_onedrive_urls.py +52 -0
  50. office365/migration/sharepoint/scanners/lookup.py +38 -0
  51. office365/migration/sharepoint/scanners/paths.py +9 -4
  52. office365/migration/sharepoint/scanners/permissions.py +20 -2
  53. office365/migration/sharepoint/scanners/smat.py +51 -0
  54. office365/migration/sharepoint/scanners/summary.py +1 -0
  55. office365/migration/sharepoint/scanners/thicket_folders.py +38 -0
  56. office365/migration/sharepoint/scanners/unsupported_site_templates.py +90 -0
  57. office365/migration/sharepoint/tenant_assessor.py +5 -3
  58. office365/migration/sharepoint/transfer.py +4 -2
  59. office365/migration/tasks.py +121 -0
  60. office365/migration/validators.py +2 -19
  61. office365/onedrive/driveitems/driveItem.py +116 -12
  62. office365/onedrive/termstore/groups/collection.py +2 -9
  63. office365/onedrive/termstore/sets/collection.py +2 -9
  64. office365/onedrive/termstore/terms/collection.py +3 -10
  65. office365/onedrive/workbooks/workbook.py +7 -0
  66. office365/outlook/contacts/folders/collection.py +4 -13
  67. office365/runtime/auth/authentication_context.py +5 -0
  68. office365/runtime/client_object.py +18 -0
  69. office365/runtime/client_object_collection.py +75 -245
  70. office365/runtime/client_request.py +77 -1
  71. office365/runtime/client_request_exception.py +127 -39
  72. office365/runtime/client_runtime_context.py +79 -8
  73. office365/runtime/converters/csv_writer.py +10 -4
  74. office365/runtime/converters/dataframe.py +84 -4
  75. office365/runtime/converters/json_file.py +12 -3
  76. office365/runtime/converters/parquet.py +67 -0
  77. office365/runtime/converters/records.py +31 -12
  78. office365/runtime/converters/registry.py +185 -0
  79. office365/runtime/converters/scalars.py +31 -0
  80. office365/runtime/converters/sql.py +69 -0
  81. office365/runtime/converters/streamers.py +105 -0
  82. office365/runtime/converters/streams.py +42 -0
  83. office365/runtime/converters/upsert.py +101 -0
  84. office365/runtime/exceptions.py +105 -0
  85. office365/runtime/http/prefer.py +26 -0
  86. office365/runtime/http/throttling.py +258 -56
  87. office365/runtime/imports.py +571 -0
  88. office365/runtime/limits.py +455 -0
  89. office365/runtime/odata/request.py +11 -2
  90. office365/runtime/odata/type.py +1 -0
  91. office365/runtime/odata/v3/batch_request.py +4 -2
  92. office365/runtime/odata/v4/batch_request.py +22 -7
  93. office365/runtime/operations.py +29 -0
  94. office365/runtime/parallel.py +12 -14
  95. office365/runtime/paths/v4/entity.py +2 -2
  96. office365/runtime/queries/batch.py +18 -1
  97. office365/runtime/queries/get_or_create.py +109 -0
  98. office365/runtime/record_collection.py +443 -0
  99. office365/runtime/retry.py +92 -26
  100. office365/runtime/transport/base.py +6 -1
  101. office365/runtime/transport/requests_transport.py +4 -0
  102. office365/runtime/transport/throttled_transport.py +67 -0
  103. office365/runtime/verification.py +40 -0
  104. office365/sharepoint/activities/logger.py +118 -8
  105. office365/sharepoint/administration/analytics/usage_service.py +15 -7
  106. office365/sharepoint/administration/sitemove/service.py +791 -8
  107. office365/sharepoint/alerts/alert.py +79 -0
  108. office365/sharepoint/apps/app_collection.py +25 -0
  109. office365/sharepoint/apps/launcher.py +16 -0
  110. office365/sharepoint/authpolicy/tenant/compliancereport.py +5 -0
  111. office365/sharepoint/businessdata/app_bdc_catalog.py +135 -0
  112. office365/sharepoint/businessdata/entityfieldvaluedictionary.py +65 -0
  113. office365/sharepoint/businessdata/entityidentifier.py +23 -0
  114. office365/sharepoint/businessdata/entityinstance.py +52 -0
  115. office365/sharepoint/businessdata/entityview.py +31 -0
  116. office365/sharepoint/businessdata/externalsubscriptionstore.py +9 -0
  117. office365/sharepoint/businessdata/typedescriptor.py +37 -0
  118. office365/sharepoint/client_context.py +40 -23
  119. office365/sharepoint/clientsidecomponent/hostedapps/app.py +21 -0
  120. office365/sharepoint/clientsidecomponent/hostedapps/manager.py +16 -0
  121. office365/sharepoint/comments/comment.py +84 -2
  122. office365/sharepoint/contentcenter/machinelearning/models/model.py +0 -23
  123. office365/sharepoint/contentcenter/machinelearning/publications/publication.py +0 -9
  124. office365/sharepoint/contentcenter/machinelearning/samples/sample.py +0 -14
  125. office365/sharepoint/contentcenter/machinelearning/workitems/item.py +0 -80
  126. office365/sharepoint/contentsolution/template_api.py +148 -0
  127. office365/sharepoint/contenttypes/collection.py +32 -10
  128. office365/sharepoint/convergence/addnoliveblobsinodcservicestateapi.py +9 -0
  129. office365/sharepoint/convergence/consumer_permissions.py +15 -0
  130. office365/sharepoint/convergence/migration_complete_state_api.py +25 -0
  131. office365/sharepoint/convergence/odcmetadatacleanedupapi.py +9 -0
  132. office365/sharepoint/documentmanagement/document_id.py +12 -0
  133. office365/sharepoint/entity.py +11 -2
  134. office365/sharepoint/entity_collection.py +2 -2
  135. office365/sharepoint/exceptions.py +194 -0
  136. office365/sharepoint/fields/builtin_field_id.py +45 -34
  137. office365/sharepoint/fields/builtin_field_name.py +123 -0
  138. office365/sharepoint/fields/coercion.py +131 -0
  139. office365/sharepoint/fields/collection.py +54 -37
  140. office365/sharepoint/fields/field.py +17 -0
  141. office365/sharepoint/fields/name.py +46 -2
  142. office365/sharepoint/files/collection.py +7 -1
  143. office365/sharepoint/files/file.py +54 -8
  144. office365/sharepoint/files/versions/version.py +38 -13
  145. office365/sharepoint/folders/collection.py +4 -0
  146. office365/sharepoint/folders/folder.py +91 -21
  147. office365/sharepoint/htmlpages/html_page.py +16 -0
  148. office365/sharepoint/htmlpages/html_page_service.py +19 -0
  149. office365/sharepoint/ir/migration.py +9 -0
  150. office365/sharepoint/listitems/caml/__init__.py +31 -0
  151. office365/sharepoint/listitems/caml/builder.py +129 -0
  152. office365/sharepoint/listitems/caml/expressions.py +424 -0
  153. office365/sharepoint/listitems/caml/fields.py +68 -0
  154. office365/sharepoint/listitems/caml/guard.py +63 -0
  155. office365/sharepoint/listitems/caml/query.py +71 -2
  156. office365/sharepoint/listitems/caml/values.py +113 -0
  157. office365/sharepoint/listitems/collection.py +21 -20
  158. office365/sharepoint/listitems/upsert.py +75 -0
  159. office365/sharepoint/lists/collection.py +29 -22
  160. office365/sharepoint/lists/exporter.py +4 -3
  161. office365/sharepoint/lists/list.py +397 -39
  162. office365/sharepoint/lists/smart_wiki_library_utility.py +58 -0
  163. office365/sharepoint/logger/export.py +12 -0
  164. office365/sharepoint/marketplace/app_metadata.py +97 -1
  165. office365/sharepoint/marketplace/appstatequeryjobdefinition.py +9 -0
  166. office365/sharepoint/marketplace/corporatecuratedgallery/carddesigns/card_designs.py +35 -0
  167. office365/sharepoint/marketplace/sitecollection/appcatalog/allowed_item.py +5 -0
  168. office365/sharepoint/marketplace/tenant/appcatalog/accessor.py +107 -30
  169. office365/sharepoint/microfeed/data.py +0 -6
  170. office365/sharepoint/microservice/manager.py +61 -0
  171. office365/sharepoint/migrationcenter/common/task.py +193 -4
  172. office365/sharepoint/migrationcenter/common/task_entity_data.py +1 -1
  173. office365/sharepoint/migrationcenter/deploy_status.py +13 -0
  174. office365/sharepoint/migrationcenter/service/properties.py +20 -0
  175. office365/sharepoint/migrationcenter/service/storage.py +34 -0
  176. office365/sharepoint/migrationcenter/spflight.py +13 -0
  177. office365/sharepoint/migrationcenter/urlparseresult.py +77 -0
  178. office365/sharepoint/multigeo/service/spmultigeoproxy.py +13 -0
  179. office365/sharepoint/navigation/servicerest.py +127 -0
  180. office365/sharepoint/oauth/authentication.py +13 -0
  181. office365/sharepoint/portal/theme_manager.py +120 -8
  182. office365/sharepoint/principal/users/user.py +10 -0
  183. office365/sharepoint/publishing/campaign/campaign.py +36 -1
  184. office365/sharepoint/publishing/feedvideopage.py +0 -7
  185. office365/sharepoint/publishing/pages/3d/page.py +0 -6
  186. office365/sharepoint/publishing/pages/page.py +0 -117
  187. office365/sharepoint/publishing/pages/reposts/repost.py +0 -7
  188. office365/sharepoint/publishing/pages/topics/topic.py +0 -7
  189. office365/sharepoint/publishing/subtitlefile.py +0 -40
  190. office365/sharepoint/quotamanagement/consumer/consumer_quota_info_api.py +14 -0
  191. office365/sharepoint/quotamanagement/consumer/non_quota_backfill_api.py +23 -0
  192. office365/sharepoint/quotamanagement/consumer/nonquotamigrationapi.py +9 -0
  193. office365/sharepoint/quotamanagement/consumer/quotamigrationapi.py +13 -0
  194. office365/sharepoint/request.py +57 -16
  195. office365/sharepoint/search/administration/document_crawl_log.py +24 -7
  196. office365/sharepoint/search/administration/providers/crawl_versions_info.py +29 -0
  197. office365/sharepoint/search/administration/providers/recycle_bin_info.py +23 -0
  198. office365/sharepoint/search/administration/providers/site_content_processing_info.py +67 -0
  199. office365/sharepoint/sites/site.py +976 -65
  200. office365/sharepoint/sites/siteprovisioninginfoprovider.py +13 -0
  201. office365/sharepoint/tenant/administration/datagovernance/insight_client.py +292 -23
  202. office365/sharepoint/tenant/administration/ddiadapter.py +97 -0
  203. office365/sharepoint/tenant/administration/internal/aad/collection.py +20 -0
  204. office365/sharepoint/tenant/administration/internal/aad/permission_grant.py +0 -19
  205. office365/sharepoint/tenant/administration/internal/appservice/permission_request.py +72 -1
  206. office365/sharepoint/tenant/administration/internal/appservice/principal.py +1 -3
  207. office365/sharepoint/tenant/administration/odb_license_enforcement_service.py +26 -0
  208. office365/sharepoint/tenant/administration/sites/properties.py +564 -6
  209. office365/sharepoint/tenant/administration/spdatagovernancesarrestapiclient.py +60 -1
  210. office365/sharepoint/tenant/insights/reports/manager.py +24 -12
  211. office365/sharepoint/tenant/management/office365_tenant.py +451 -2
  212. office365/sharepoint/tenantadministration/catalog_management_collection.py +69 -0
  213. office365/sharepoint/tenantadministration/catalog_management_custom_properties_collection.py +237 -0
  214. office365/sharepoint/tenantadministration/catalog_management_restricted_content_discovery_collection.py +56 -0
  215. office365/sharepoint/tenantadministration/sp_data_governance_client_exception.py +7 -0
  216. office365/sharepoint/thresholds.py +529 -0
  217. office365/sharepoint/translation/jobs/job.py +72 -2
  218. office365/sharepoint/translation/jobs/status.py +47 -9
  219. office365/sharepoint/translation/sync_translator.py +15 -2
  220. office365/sharepoint/utilities/move_copy_util.py +2 -1
  221. office365/sharepoint/viva/connections/page.py +27 -0
  222. office365/sharepoint/viva/home.py +38 -0
  223. office365/sharepoint/viva/learningmanager.py +9 -0
  224. office365/sharepoint/viva/vivaresources.py +60 -0
  225. office365/sharepoint/webhooks/subscription.py +0 -11
  226. office365/sharepoint/webparts/definitions/definition.py +13 -0
  227. office365/sharepoint/webparts/limited_manager.py +29 -7
  228. office365/sharepoint/webs/context_web_information.py +14 -9
  229. office365/sharepoint/webs/templates/__init__.py +11 -0
  230. office365/sharepoint/webs/templates/type.py +74 -15
  231. office365/sharepoint/workflow/definition.py +6 -0
  232. office365/sharepoint/workflow/deployment_service.py +155 -0
  233. office365/sharepoint/workflow/instances/service.py +33 -0
  234. office365/sharepoint/workflow/interopservice.py +68 -0
  235. office365/sharepoint/workflow/messagingservice.py +24 -0
  236. office365/sharepoint/workflow/servicesmanager.py +41 -2
  237. office365/sharepoint/workflow/subscriptions/service.py +67 -0
  238. office365/sharepoint/workflow/subscriptions/subscription.py +31 -0
  239. office365/sharepoint/workflow/task.py +425 -0
  240. office365_rest_python_client-3.2.0.dist-info/METADATA +605 -0
  241. {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/RECORD +242 -185
  242. office365/migration/_util.py +0 -73
  243. office365/sharepoint/listitems/caml/types.py +0 -70
  244. office365_rest_python_client-3.1.0.dist-info/METADATA +0 -475
  245. {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,113 @@
1
+ """Typed CAML ``<Value>`` nodes.
2
+
3
+ A :class:`Value` renders a ``<Value Type='...'>...</Value>`` element, inferring
4
+ the OData value type from the Python value (``str`` -> ``Text``, ``int`` ->
5
+ ``Integer``, ``bool`` -> ``Boolean``, ``datetime`` -> ``DateTime``) unless an
6
+ explicit type is given. Special values (``Now``/``Today``/``Month``/``UserID``/
7
+ ``ListProperty``) render a child element instead of literal text.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from datetime import date, datetime
13
+ from enum import Enum
14
+ from typing import Any, Optional
15
+ from xml.sax.saxutils import escape, quoteattr
16
+
17
+
18
+ class ValueType(str, Enum):
19
+ """The ``Type`` attribute of a CAML ``<Value>`` element."""
20
+
21
+ Text = "Text"
22
+ Note = "Note"
23
+ Integer = "Integer"
24
+ Counter = "Counter"
25
+ Number = "Number"
26
+ Currency = "Currency"
27
+ DateTime = "DateTime"
28
+ Boolean = "Boolean"
29
+ Lookup = "Lookup"
30
+ User = "User"
31
+ Choice = "Choice"
32
+ URL = "URL"
33
+ Guid = "Guid"
34
+ Calculated = "Calculated"
35
+
36
+
37
+ def infer_value_type(value: Any) -> ValueType:
38
+ """Infer the CAML ``ValueType`` from a Python value."""
39
+ if isinstance(value, bool): # bool before int (bool is an int subclass)
40
+ return ValueType.Boolean
41
+ if isinstance(value, int):
42
+ return ValueType.Integer
43
+ if isinstance(value, float):
44
+ return ValueType.Number
45
+ if isinstance(value, (datetime, date)):
46
+ return ValueType.DateTime
47
+ return ValueType.Text
48
+
49
+
50
+ class Value:
51
+ """A CAML ``<Value>`` element."""
52
+
53
+ def __init__(
54
+ self,
55
+ value: Any = None,
56
+ *,
57
+ type: Optional[ValueType | str] = None, # noqa: A002
58
+ include_time_value: bool = False,
59
+ lookup_id: bool = False,
60
+ child: Optional[str] = None,
61
+ ) -> None:
62
+ self._value = value
63
+ self._type = ValueType(type) if isinstance(type, str) else type
64
+ self._include_time_value = include_time_value
65
+ self._lookup_id = lookup_id
66
+ self._child = child # raw child XML (e.g. "<Now/>")
67
+
68
+ @property
69
+ def value_type(self) -> ValueType:
70
+ """The resolved ``ValueType`` (explicit type, else inferred)."""
71
+ return self._type or infer_value_type(self._value)
72
+
73
+ def to_xml(self) -> str:
74
+ attrs = [f"Type={quoteattr(self.value_type.value)}"]
75
+ if self._include_time_value:
76
+ attrs.append('IncludeTimeValue="TRUE"')
77
+ if self._lookup_id:
78
+ attrs.append('LookupId="TRUE"')
79
+ inner = self._child if self._child is not None else escape(self._format(self._value))
80
+ return f"<Value {' '.join(attrs)}>{inner}</Value>"
81
+
82
+ @staticmethod
83
+ def _format(value: Any) -> str:
84
+ if isinstance(value, bool):
85
+ return "1" if value else "0"
86
+ if isinstance(value, (datetime, date)):
87
+ return value.isoformat()
88
+ return str(value)
89
+
90
+
91
+ def now() -> Value:
92
+ """A ``<Value Type='DateTime'><Now/></Value>`` node."""
93
+ return Value(type=ValueType.DateTime, child="<Now/>")
94
+
95
+
96
+ def today() -> Value:
97
+ """A ``<Value Type='DateTime'><Today/></Value>`` node."""
98
+ return Value(type=ValueType.DateTime, child="<Today/>")
99
+
100
+
101
+ def month() -> Value:
102
+ """A ``<Value Type='Integer'><Month/></Value>`` node."""
103
+ return Value(type=ValueType.Integer, child="<Month/>")
104
+
105
+
106
+ def user_id() -> Value:
107
+ """A ``<Value Type='Integer'><UserID/></Value>`` node."""
108
+ return Value(type=ValueType.Integer, child="<UserID/>")
109
+
110
+
111
+ def list_property(name: str) -> Value:
112
+ """A ``<Value><ListProperty Name='...'/></Value>`` node."""
113
+ return Value(child=f"<ListProperty Name={quoteattr(name)}/>")
@@ -7,51 +7,52 @@ from typing_extensions import Self
7
7
  from office365.runtime.paths.service_operation import ServiceOperationPath
8
8
  from office365.sharepoint.entity_collection import EntityCollection
9
9
  from office365.sharepoint.listitems.listitem import ListItem
10
+ from office365.sharepoint.thresholds import LIST_VIEW_THRESHOLD
10
11
 
11
12
  if TYPE_CHECKING:
13
+ from office365.runtime.converters.upsert import UpsertTarget
12
14
  from office365.runtime.operations import ProgressCallback
13
15
 
14
16
 
15
17
  class ListItemCollection(EntityCollection[ListItem]):
16
18
  """List Item collection"""
17
19
 
18
- def __init__(self, context, resource_path=None):
19
- super().__init__(context, ListItem, resource_path)
20
+ _list_view_threshold = LIST_VIEW_THRESHOLD
21
+ _truncation_hint = "page it with List.get_items(query, page_size=2000) or items.get_all(page_size=2000)"
20
22
 
21
- def from_records(self, records, progress: "ProgressCallback | None" = None) -> Self:
23
+ def __init__(self, context, resource_path=None, parent=None):
24
+ super().__init__(context, ListItem, resource_path, parent)
25
+
26
+ def queue_records(self, records, progress: "ProgressCallback | None" = None) -> Self:
22
27
  """Queue an item create per record.
23
28
 
24
29
  List item columns are defined per list at runtime, not on the ``ListItem``
25
30
  class, so every record key is forwarded to the server (no unknown-column
26
31
  filtering). Deferred — run the creates with ``execute_query()``.
27
-
28
- Args:
29
- records: Plain dict records to import.
30
- progress: Optional hook invoked per queued create as it completes
31
- during ``execute_query()``.
32
-
33
- Returns:
34
- Self: The item collection, for method chaining.
35
32
  """
36
33
  from office365.runtime.converters.csv_reader import coerce_records
37
34
 
38
35
  return self._import_records(coerce_records(self._item_type, records, allow_unknown=True), progress=progress)
39
36
 
40
- def get_by_id(self, item_id: int) -> ListItem:
41
- """Returns the list item with the specified list item identifier.
37
+ def upsert_target(self, *, key_field: str = "MigrationKey", enforce_unique: bool = False) -> "UpsertTarget":
38
+ """The keyed skip/upsert target for this list (see ``from_records(key=...)``)."""
39
+ from office365.sharepoint.listitems.upsert import ListItemUpsertTarget
42
40
 
43
- Args:
44
- item_id (int): The list item identifier.
45
- """
41
+ return ListItemUpsertTarget(self, key_field=key_field, enforce_unique=enforce_unique)
42
+
43
+ def _key_column(self, column: str) -> str:
44
+ """List-item record keys are field internal names (``Name`` -> ``Name_``)."""
45
+ from office365.sharepoint.fields.name import internal_field_name
46
+
47
+ return internal_field_name(column)
48
+
49
+ def get_by_id(self, item_id: int) -> ListItem:
50
+ """Returns the list item with the specified list item identifier."""
46
51
  return ListItem(self.context, ServiceOperationPath("GetById", [item_id], self.resource_path))
47
52
 
48
53
  def get_by_string_id(self, s_id: str) -> ListItem:
49
54
  """Returns the list item with either the specified list item identifier or the specified identifier
50
55
  for an instance of an external content type.
51
-
52
- Args:
53
- s_id (str): Specifies the list item identifier, or if the list is an external list, specifies the identifier
54
- for an instance of an external content type as specified in[MS-ECTPWPS] section 3.1.4.1.2.1.
55
56
  """
56
57
  return ListItem(
57
58
  self.context,
@@ -0,0 +1,75 @@
1
+ """SharePoint list-item :class:`UpsertTarget` — the destination side of keyed imports.
2
+
3
+ Keeps the SharePoint-specific pieces (ensure the key column, load existing keys,
4
+ create/update items) out of the runtime upsert core, so the same
5
+ :func:`~office365.runtime.converters.upsert.keyed_queue` drives list imports and
6
+ the migration toolkit.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import TYPE_CHECKING, Any, Mapping, cast
12
+
13
+ from office365.runtime.paths.v3.entity import EntityPath
14
+ from office365.sharepoint.fields.type import FieldType
15
+ from office365.sharepoint.listitems.listitem import ListItem
16
+
17
+ if TYPE_CHECKING:
18
+ from office365.sharepoint.listitems.collection import ListItemCollection
19
+ from office365.sharepoint.lists.list import List
20
+
21
+
22
+ class ListItemUpsertTarget:
23
+ """Keyed skip/upsert target backed by a :class:`ListItemCollection`."""
24
+
25
+ def __init__(
26
+ self,
27
+ collection: "ListItemCollection",
28
+ *,
29
+ key_field: str = "MigrationKey",
30
+ enforce_unique: bool = False,
31
+ ) -> None:
32
+ self._collection = collection
33
+ self._key_field = key_field
34
+ self._enforce_unique = enforce_unique
35
+
36
+ @property
37
+ def key_field(self) -> str:
38
+ return self._key_field
39
+
40
+ def ensure_key_field(self) -> None:
41
+ """Queue the key column (Text, optionally unique+indexed) if missing."""
42
+ from office365.sharepoint.fields.creation_information import FieldCreationInformation
43
+
44
+ parent = cast("List", self._collection.parent)
45
+ if parent is None:
46
+ return
47
+ field = parent.fields.ensure(FieldCreationInformation(Title=self._key_field, FieldTypeKind=FieldType.Text))
48
+ if self._enforce_unique:
49
+ field.set_property("EnforceUniqueValues", True)
50
+ field.set_property("Indexed", True)
51
+ field.update()
52
+
53
+ def load_keys(self) -> Mapping[str, Any]:
54
+ """Return the existing ``{key: item_id}`` map (paged; key + ``Id`` only)."""
55
+ loaded = self._collection.select([self._key_field, "Id"]).get_all().execute_query()
56
+ keys: dict[str, Any] = {}
57
+ for item in loaded:
58
+ value = item.properties.get(self._key_field)
59
+ item_id = item.id
60
+ if value is not None and item_id is not None:
61
+ keys[str(value)] = item_id
62
+ return keys
63
+
64
+ def create_records(self, records: list[dict]) -> None:
65
+ self._collection.queue_records(records)
66
+
67
+ def update_record(self, item_id: Any, record: dict) -> None:
68
+ item = ListItem(
69
+ self._collection.context,
70
+ EntityPath(item_id, self._collection.resource_path),
71
+ parent_list=self._collection.parent,
72
+ )
73
+ for name, value in record.items():
74
+ item.set_property(name, value, persist_changes=True)
75
+ item.update()
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Optional
4
4
 
5
- from office365.runtime.client_request_exception import ClientRequestException, DuplicatedObjectException
6
5
  from office365.runtime.paths.resource_path import ResourcePath
7
6
  from office365.runtime.paths.service_operation import ServiceOperationPath
8
7
  from office365.runtime.paths.v3.entity import EntityPath
@@ -73,39 +72,47 @@ class ListCollection(EntityCollection[List]):
73
72
  )
74
73
 
75
74
  def ensure_list(
76
- self, title: str, description: str | None = None, template_type: ListTemplateType = ListTemplateType.GenericList
75
+ self,
76
+ title: str,
77
+ description: str | None = None,
78
+ template_type: ListTemplateType = ListTemplateType.GenericList,
79
+ allow_content_types: bool = False,
80
+ *,
81
+ on_conflict: str = "skip",
77
82
  ) -> List:
78
83
  """Gets the list with the given title, or creates it if it does not exist.
79
84
 
80
- Attempts to create the list via ``add_list``; if the server reports that
81
- a list with the title already exists (``DuplicatedObjectException``), the
82
- existing list is loaded instead and its properties are copied into the
83
- returned object. All other errors propagate.
84
-
85
- The result is deferred: resolve it with ``execute_query()`` before use,
86
- e.g.::
87
-
88
- >>> lst = ctx.web.lists.ensure_list("My List").execute_query()
85
+ Idempotent (deferred — resolve with ``execute_query()``). With
86
+ ``on_conflict="update"`` the existing list's description is reconciled.
89
87
 
90
88
  Args:
91
89
  title: Title of the list to get or create
92
90
  description: Description of the list to get or create
93
91
  template_type: Type of list to get or create
92
+ allow_content_types: Whether to enable content types
93
+ on_conflict: ``"skip"`` (default) or ``"update"``.
94
94
 
95
95
  Returns:
96
96
  List: The existing or newly created list
97
97
  """
98
- return_type = self.add_list(title=title, description=description, template_type=template_type)
99
-
100
- def _on_name_exists(error: ClientRequestException):
101
- if not isinstance(error, DuplicatedObjectException):
102
- raise error
103
- self.get_by_title(title).get().after_execute(
104
- lambda existing: return_type.copy_from(existing), execute_first=True
105
- )
106
-
107
- return_type.on_error(_on_name_exists)
108
- return return_type
98
+ from office365.runtime.queries.get_or_create import create_or_get
99
+
100
+ def _reconcile(lst: List) -> None:
101
+ if description is not None and lst.properties.get("Description") != description:
102
+ lst.set_property("Description", description)
103
+ lst.update()
104
+
105
+ return create_or_get(
106
+ create=lambda: self.add_list(
107
+ title=title,
108
+ description=description,
109
+ template_type=template_type,
110
+ allow_content_types=allow_content_types,
111
+ ),
112
+ find=lambda: self.get_by_title(title).get(),
113
+ on_conflict=on_conflict,
114
+ reconcile=_reconcile,
115
+ )
109
116
 
110
117
  def ensure_client_rendered_site_pages_library(self) -> List:
111
118
  """
@@ -5,6 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import IO, TYPE_CHECKING, Callable, Optional, Union
6
6
 
7
7
  from office365.runtime.client_result import ClientResult
8
+ from office365.sharepoint.fields.builtin_field_name import SPBuiltInFieldName
8
9
  from office365.sharepoint.files.system_object_type import FileSystemObjectType
9
10
  from office365.sharepoint.listitems.collection import ListItemCollection
10
11
  from office365.sharepoint.listitems.listitem import ListItem
@@ -72,9 +73,9 @@ class ListExporter:
72
73
  [
73
74
  "*",
74
75
  "Id",
75
- "FileRef",
76
- "FileDirRef",
77
- "FileLeafRef",
76
+ SPBuiltInFieldName.FileRef,
77
+ SPBuiltInFieldName.FileDirRef,
78
+ SPBuiltInFieldName.FileLeafRef,
78
79
  "FileSystemObjectType",
79
80
  ]
80
81
  )