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.
- office365/communications/callrecords/call_record.py +18 -0
- office365/communications/calls/call.py +3 -0
- office365/communications/presences/presence.py +3 -0
- office365/communications/quotas.py +66 -0
- office365/communications/virtualevents/virtual_event.py +3 -0
- office365/directory/domains/domain.py +3 -0
- office365/directory/licenses/subscribed_sku.py +3 -0
- office365/directory/objects/object.py +3 -0
- office365/directory/quotas.py +34 -0
- office365/entity_collection.py +2 -2
- office365/graph_client.py +46 -2
- office365/limits.py +66 -0
- office365/migration/__init__.py +6 -1
- office365/migration/adapters/__init__.py +14 -4
- office365/migration/adapters/filesystem.py +4 -1
- office365/migration/assessment/export.py +5 -5
- office365/migration/assessment/issue.py +19 -0
- office365/migration/assessment/report.py +11 -0
- office365/migration/assessment/scanners/base.py +37 -49
- office365/migration/base.py +33 -8
- office365/migration/checkpoint.py +1 -1
- office365/migration/outlook/assessor.py +4 -4
- office365/migration/package/__init__.py +82 -0
- office365/migration/package/builder.py +603 -0
- office365/migration/package/crypto.py +54 -0
- office365/migration/package/manifest_xml.py +495 -0
- office365/migration/package/staging.py +180 -0
- office365/migration/report.py +7 -2
- office365/migration/report_io.py +79 -0
- office365/migration/runner.py +117 -10
- office365/migration/server_job.py +126 -14
- office365/migration/session.py +132 -49
- office365/migration/settings.py +67 -0
- office365/migration/sharepoint/__init__.py +2 -0
- office365/migration/sharepoint/adapters.py +207 -41
- office365/migration/sharepoint/assessor.py +45 -9
- office365/migration/sharepoint/options.py +57 -0
- office365/migration/sharepoint/package_target.py +209 -0
- office365/migration/sharepoint/registry.py +22 -0
- office365/migration/sharepoint/resolvers.py +96 -0
- office365/migration/sharepoint/scanners/__init__.py +29 -0
- office365/migration/sharepoint/scanners/browser_file_handling.py +55 -0
- office365/migration/sharepoint/scanners/checked_out_files.py +57 -0
- office365/migration/sharepoint/scanners/file_versions.py +56 -0
- office365/migration/sharepoint/scanners/files.py +5 -2
- office365/migration/sharepoint/scanners/large_excel_files.py +56 -0
- office365/migration/sharepoint/scanners/large_lists.py +58 -0
- office365/migration/sharepoint/scanners/large_sites.py +4 -3
- office365/migration/sharepoint/scanners/long_onedrive_urls.py +52 -0
- office365/migration/sharepoint/scanners/lookup.py +38 -0
- office365/migration/sharepoint/scanners/paths.py +9 -4
- office365/migration/sharepoint/scanners/permissions.py +20 -2
- office365/migration/sharepoint/scanners/smat.py +51 -0
- office365/migration/sharepoint/scanners/summary.py +1 -0
- office365/migration/sharepoint/scanners/thicket_folders.py +38 -0
- office365/migration/sharepoint/scanners/unsupported_site_templates.py +90 -0
- office365/migration/sharepoint/tenant_assessor.py +5 -3
- office365/migration/sharepoint/transfer.py +4 -2
- office365/migration/tasks.py +121 -0
- office365/migration/validators.py +2 -19
- office365/onedrive/driveitems/driveItem.py +116 -12
- office365/onedrive/termstore/groups/collection.py +2 -9
- office365/onedrive/termstore/sets/collection.py +2 -9
- office365/onedrive/termstore/terms/collection.py +3 -10
- office365/onedrive/workbooks/workbook.py +7 -0
- office365/outlook/contacts/folders/collection.py +4 -13
- office365/runtime/auth/authentication_context.py +5 -0
- office365/runtime/client_object.py +18 -0
- office365/runtime/client_object_collection.py +75 -245
- office365/runtime/client_request.py +77 -1
- office365/runtime/client_request_exception.py +127 -39
- office365/runtime/client_runtime_context.py +79 -8
- office365/runtime/converters/csv_writer.py +10 -4
- office365/runtime/converters/dataframe.py +84 -4
- office365/runtime/converters/json_file.py +12 -3
- office365/runtime/converters/parquet.py +67 -0
- office365/runtime/converters/records.py +31 -12
- office365/runtime/converters/registry.py +185 -0
- office365/runtime/converters/scalars.py +31 -0
- office365/runtime/converters/sql.py +69 -0
- office365/runtime/converters/streamers.py +105 -0
- office365/runtime/converters/streams.py +42 -0
- office365/runtime/converters/upsert.py +101 -0
- office365/runtime/exceptions.py +105 -0
- office365/runtime/http/prefer.py +26 -0
- office365/runtime/http/throttling.py +258 -56
- office365/runtime/imports.py +571 -0
- office365/runtime/limits.py +455 -0
- office365/runtime/odata/request.py +11 -2
- office365/runtime/odata/type.py +1 -0
- office365/runtime/odata/v3/batch_request.py +4 -2
- office365/runtime/odata/v4/batch_request.py +22 -7
- office365/runtime/operations.py +29 -0
- office365/runtime/parallel.py +12 -14
- office365/runtime/paths/v4/entity.py +2 -2
- office365/runtime/queries/batch.py +18 -1
- office365/runtime/queries/get_or_create.py +109 -0
- office365/runtime/record_collection.py +443 -0
- office365/runtime/retry.py +92 -26
- office365/runtime/transport/base.py +6 -1
- office365/runtime/transport/requests_transport.py +4 -0
- office365/runtime/transport/throttled_transport.py +67 -0
- office365/runtime/verification.py +40 -0
- office365/sharepoint/activities/logger.py +118 -8
- office365/sharepoint/administration/analytics/usage_service.py +15 -7
- office365/sharepoint/administration/sitemove/service.py +791 -8
- office365/sharepoint/alerts/alert.py +79 -0
- office365/sharepoint/apps/app_collection.py +25 -0
- office365/sharepoint/apps/launcher.py +16 -0
- office365/sharepoint/authpolicy/tenant/compliancereport.py +5 -0
- office365/sharepoint/businessdata/app_bdc_catalog.py +135 -0
- office365/sharepoint/businessdata/entityfieldvaluedictionary.py +65 -0
- office365/sharepoint/businessdata/entityidentifier.py +23 -0
- office365/sharepoint/businessdata/entityinstance.py +52 -0
- office365/sharepoint/businessdata/entityview.py +31 -0
- office365/sharepoint/businessdata/externalsubscriptionstore.py +9 -0
- office365/sharepoint/businessdata/typedescriptor.py +37 -0
- office365/sharepoint/client_context.py +40 -23
- office365/sharepoint/clientsidecomponent/hostedapps/app.py +21 -0
- office365/sharepoint/clientsidecomponent/hostedapps/manager.py +16 -0
- office365/sharepoint/comments/comment.py +84 -2
- office365/sharepoint/contentcenter/machinelearning/models/model.py +0 -23
- office365/sharepoint/contentcenter/machinelearning/publications/publication.py +0 -9
- office365/sharepoint/contentcenter/machinelearning/samples/sample.py +0 -14
- office365/sharepoint/contentcenter/machinelearning/workitems/item.py +0 -80
- office365/sharepoint/contentsolution/template_api.py +148 -0
- office365/sharepoint/contenttypes/collection.py +32 -10
- office365/sharepoint/convergence/addnoliveblobsinodcservicestateapi.py +9 -0
- office365/sharepoint/convergence/consumer_permissions.py +15 -0
- office365/sharepoint/convergence/migration_complete_state_api.py +25 -0
- office365/sharepoint/convergence/odcmetadatacleanedupapi.py +9 -0
- office365/sharepoint/documentmanagement/document_id.py +12 -0
- office365/sharepoint/entity.py +11 -2
- office365/sharepoint/entity_collection.py +2 -2
- office365/sharepoint/exceptions.py +194 -0
- office365/sharepoint/fields/builtin_field_id.py +45 -34
- office365/sharepoint/fields/builtin_field_name.py +123 -0
- office365/sharepoint/fields/coercion.py +131 -0
- office365/sharepoint/fields/collection.py +54 -37
- office365/sharepoint/fields/field.py +17 -0
- office365/sharepoint/fields/name.py +46 -2
- office365/sharepoint/files/collection.py +7 -1
- office365/sharepoint/files/file.py +54 -8
- office365/sharepoint/files/versions/version.py +38 -13
- office365/sharepoint/folders/collection.py +4 -0
- office365/sharepoint/folders/folder.py +91 -21
- office365/sharepoint/htmlpages/html_page.py +16 -0
- office365/sharepoint/htmlpages/html_page_service.py +19 -0
- office365/sharepoint/ir/migration.py +9 -0
- office365/sharepoint/listitems/caml/__init__.py +31 -0
- office365/sharepoint/listitems/caml/builder.py +129 -0
- office365/sharepoint/listitems/caml/expressions.py +424 -0
- office365/sharepoint/listitems/caml/fields.py +68 -0
- office365/sharepoint/listitems/caml/guard.py +63 -0
- office365/sharepoint/listitems/caml/query.py +71 -2
- office365/sharepoint/listitems/caml/values.py +113 -0
- office365/sharepoint/listitems/collection.py +21 -20
- office365/sharepoint/listitems/upsert.py +75 -0
- office365/sharepoint/lists/collection.py +29 -22
- office365/sharepoint/lists/exporter.py +4 -3
- office365/sharepoint/lists/list.py +397 -39
- office365/sharepoint/lists/smart_wiki_library_utility.py +58 -0
- office365/sharepoint/logger/export.py +12 -0
- office365/sharepoint/marketplace/app_metadata.py +97 -1
- office365/sharepoint/marketplace/appstatequeryjobdefinition.py +9 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/carddesigns/card_designs.py +35 -0
- office365/sharepoint/marketplace/sitecollection/appcatalog/allowed_item.py +5 -0
- office365/sharepoint/marketplace/tenant/appcatalog/accessor.py +107 -30
- office365/sharepoint/microfeed/data.py +0 -6
- office365/sharepoint/microservice/manager.py +61 -0
- office365/sharepoint/migrationcenter/common/task.py +193 -4
- office365/sharepoint/migrationcenter/common/task_entity_data.py +1 -1
- office365/sharepoint/migrationcenter/deploy_status.py +13 -0
- office365/sharepoint/migrationcenter/service/properties.py +20 -0
- office365/sharepoint/migrationcenter/service/storage.py +34 -0
- office365/sharepoint/migrationcenter/spflight.py +13 -0
- office365/sharepoint/migrationcenter/urlparseresult.py +77 -0
- office365/sharepoint/multigeo/service/spmultigeoproxy.py +13 -0
- office365/sharepoint/navigation/servicerest.py +127 -0
- office365/sharepoint/oauth/authentication.py +13 -0
- office365/sharepoint/portal/theme_manager.py +120 -8
- office365/sharepoint/principal/users/user.py +10 -0
- office365/sharepoint/publishing/campaign/campaign.py +36 -1
- office365/sharepoint/publishing/feedvideopage.py +0 -7
- office365/sharepoint/publishing/pages/3d/page.py +0 -6
- office365/sharepoint/publishing/pages/page.py +0 -117
- office365/sharepoint/publishing/pages/reposts/repost.py +0 -7
- office365/sharepoint/publishing/pages/topics/topic.py +0 -7
- office365/sharepoint/publishing/subtitlefile.py +0 -40
- office365/sharepoint/quotamanagement/consumer/consumer_quota_info_api.py +14 -0
- office365/sharepoint/quotamanagement/consumer/non_quota_backfill_api.py +23 -0
- office365/sharepoint/quotamanagement/consumer/nonquotamigrationapi.py +9 -0
- office365/sharepoint/quotamanagement/consumer/quotamigrationapi.py +13 -0
- office365/sharepoint/request.py +57 -16
- office365/sharepoint/search/administration/document_crawl_log.py +24 -7
- office365/sharepoint/search/administration/providers/crawl_versions_info.py +29 -0
- office365/sharepoint/search/administration/providers/recycle_bin_info.py +23 -0
- office365/sharepoint/search/administration/providers/site_content_processing_info.py +67 -0
- office365/sharepoint/sites/site.py +976 -65
- office365/sharepoint/sites/siteprovisioninginfoprovider.py +13 -0
- office365/sharepoint/tenant/administration/datagovernance/insight_client.py +292 -23
- office365/sharepoint/tenant/administration/ddiadapter.py +97 -0
- office365/sharepoint/tenant/administration/internal/aad/collection.py +20 -0
- office365/sharepoint/tenant/administration/internal/aad/permission_grant.py +0 -19
- office365/sharepoint/tenant/administration/internal/appservice/permission_request.py +72 -1
- office365/sharepoint/tenant/administration/internal/appservice/principal.py +1 -3
- office365/sharepoint/tenant/administration/odb_license_enforcement_service.py +26 -0
- office365/sharepoint/tenant/administration/sites/properties.py +564 -6
- office365/sharepoint/tenant/administration/spdatagovernancesarrestapiclient.py +60 -1
- office365/sharepoint/tenant/insights/reports/manager.py +24 -12
- office365/sharepoint/tenant/management/office365_tenant.py +451 -2
- office365/sharepoint/tenantadministration/catalog_management_collection.py +69 -0
- office365/sharepoint/tenantadministration/catalog_management_custom_properties_collection.py +237 -0
- office365/sharepoint/tenantadministration/catalog_management_restricted_content_discovery_collection.py +56 -0
- office365/sharepoint/tenantadministration/sp_data_governance_client_exception.py +7 -0
- office365/sharepoint/thresholds.py +529 -0
- office365/sharepoint/translation/jobs/job.py +72 -2
- office365/sharepoint/translation/jobs/status.py +47 -9
- office365/sharepoint/translation/sync_translator.py +15 -2
- office365/sharepoint/utilities/move_copy_util.py +2 -1
- office365/sharepoint/viva/connections/page.py +27 -0
- office365/sharepoint/viva/home.py +38 -0
- office365/sharepoint/viva/learningmanager.py +9 -0
- office365/sharepoint/viva/vivaresources.py +60 -0
- office365/sharepoint/webhooks/subscription.py +0 -11
- office365/sharepoint/webparts/definitions/definition.py +13 -0
- office365/sharepoint/webparts/limited_manager.py +29 -7
- office365/sharepoint/webs/context_web_information.py +14 -9
- office365/sharepoint/webs/templates/__init__.py +11 -0
- office365/sharepoint/webs/templates/type.py +74 -15
- office365/sharepoint/workflow/definition.py +6 -0
- office365/sharepoint/workflow/deployment_service.py +155 -0
- office365/sharepoint/workflow/instances/service.py +33 -0
- office365/sharepoint/workflow/interopservice.py +68 -0
- office365/sharepoint/workflow/messagingservice.py +24 -0
- office365/sharepoint/workflow/servicesmanager.py +41 -2
- office365/sharepoint/workflow/subscriptions/service.py +67 -0
- office365/sharepoint/workflow/subscriptions/subscription.py +31 -0
- office365/sharepoint/workflow/task.py +425 -0
- office365_rest_python_client-3.2.0.dist-info/METADATA +605 -0
- {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/RECORD +242 -185
- office365/migration/_util.py +0 -73
- office365/sharepoint/listitems/caml/types.py +0 -70
- office365_rest_python_client-3.1.0.dist-info/METADATA +0 -475
- {office365_rest_python_client-3.1.0.dist-info → office365_rest_python_client-3.2.0.dist-info}/WHEEL +0 -0
office365/runtime/retry.py
CHANGED
|
@@ -8,11 +8,16 @@ jitter (so a fleet of clients doesn't retry in lock-step).
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import random
|
|
11
|
+
from functools import wraps
|
|
11
12
|
from time import sleep
|
|
12
13
|
from typing import Any, Callable, Optional, Tuple, Type
|
|
13
14
|
|
|
15
|
+
from typing_extensions import ParamSpec
|
|
16
|
+
|
|
14
17
|
from office365.runtime.client_request_exception import ClientRequestException
|
|
15
18
|
|
|
19
|
+
_P = ParamSpec("_P")
|
|
20
|
+
|
|
16
21
|
TRANSIENT_STATUS_CODES = frozenset({408, 429, 500, 502, 503, 504})
|
|
17
22
|
|
|
18
23
|
|
|
@@ -85,8 +90,40 @@ def backoff_delay(attempt: int, base: int, max_delay: Optional[int] = None, jitt
|
|
|
85
90
|
return delay
|
|
86
91
|
|
|
87
92
|
|
|
88
|
-
def
|
|
93
|
+
def _run_with_retry(
|
|
89
94
|
func: Callable[[], Any],
|
|
95
|
+
*,
|
|
96
|
+
max_retry: int,
|
|
97
|
+
timeout_secs: int,
|
|
98
|
+
max_delay: Optional[int],
|
|
99
|
+
jitter: bool,
|
|
100
|
+
exceptions: Tuple[Type[Exception], ...],
|
|
101
|
+
is_retriable: Callable[[Exception], bool],
|
|
102
|
+
on_failure: Optional[Callable[[int, Exception], Optional[int]]],
|
|
103
|
+
on_success: Optional[Callable[[Any], None]],
|
|
104
|
+
) -> Any:
|
|
105
|
+
last_ex: Exception | None = None
|
|
106
|
+
for attempt in range(1, max_retry + 1):
|
|
107
|
+
try:
|
|
108
|
+
result = func()
|
|
109
|
+
if callable(on_success):
|
|
110
|
+
on_success(result)
|
|
111
|
+
return result
|
|
112
|
+
except exceptions as e:
|
|
113
|
+
if not callable(is_retriable) or not is_retriable(e):
|
|
114
|
+
raise
|
|
115
|
+
last_ex = e
|
|
116
|
+
retry_after: Optional[int] = None
|
|
117
|
+
if callable(on_failure):
|
|
118
|
+
retry_after = on_failure(attempt, e)
|
|
119
|
+
delay = retry_after if retry_after is not None else backoff_delay(attempt, timeout_secs, max_delay, jitter)
|
|
120
|
+
sleep(delay)
|
|
121
|
+
assert last_ex is not None
|
|
122
|
+
raise last_ex
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def retry(
|
|
126
|
+
func: Optional[Callable[[], Any]] = None,
|
|
90
127
|
max_retry: int = 5,
|
|
91
128
|
timeout_secs: int = 5,
|
|
92
129
|
max_delay: Optional[int] = None,
|
|
@@ -96,20 +133,27 @@ def retry(
|
|
|
96
133
|
on_failure: Optional[Callable[[int, Exception], Optional[int]]] = None,
|
|
97
134
|
on_success: Optional[Callable[[Any], None]] = None,
|
|
98
135
|
) -> Any:
|
|
99
|
-
"""Run ``func
|
|
136
|
+
"""Run ``func`` with retries — or, called without ``func``, return a decorator.
|
|
100
137
|
|
|
101
138
|
Permanent failures re-raise immediately; the last exception is re-raised
|
|
102
|
-
once retries are exhausted.
|
|
139
|
+
once retries are exhausted. The delay between attempts is exponential with
|
|
140
|
+
jitter (:func:`backoff_delay`); when ``on_failure`` returns a delay (e.g.
|
|
141
|
+
the server's ``Retry-After`` via :func:`retry_after_delay`) that value
|
|
142
|
+
overrides the backoff.
|
|
143
|
+
|
|
144
|
+
Two forms::
|
|
103
145
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
146
|
+
retry(do_request, max_retry=3) # run now (existing behaviour)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@retry(max_retry=3) # decorate a callable
|
|
150
|
+
def do_request(): ...
|
|
107
151
|
|
|
108
152
|
Args:
|
|
109
|
-
func: Callable to execute
|
|
153
|
+
func: Callable to execute. Omit to get a decorator.
|
|
110
154
|
max_retry: Maximum number of retry attempts
|
|
111
155
|
timeout_secs: Base delay for exponential backoff (seconds)
|
|
112
|
-
max_delay: Optional cap
|
|
156
|
+
max_delay: Optional cap for the exponential delay (seconds)
|
|
113
157
|
jitter: Whether to randomize the delay (default True)
|
|
114
158
|
exceptions: Exception types that are candidates for retry
|
|
115
159
|
is_retriable: Classifier deciding whether a caught exception is retried
|
|
@@ -117,21 +161,43 @@ def retry(
|
|
|
117
161
|
may return a retry delay (seconds) to override the backoff
|
|
118
162
|
on_success: Called with ``func()`` result on success
|
|
119
163
|
"""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
164
|
+
options = {
|
|
165
|
+
"max_retry": max_retry,
|
|
166
|
+
"timeout_secs": timeout_secs,
|
|
167
|
+
"max_delay": max_delay,
|
|
168
|
+
"jitter": jitter,
|
|
169
|
+
"exceptions": exceptions,
|
|
170
|
+
"is_retriable": is_retriable,
|
|
171
|
+
"on_failure": on_failure,
|
|
172
|
+
"on_success": on_success,
|
|
173
|
+
}
|
|
174
|
+
if func is None:
|
|
175
|
+
|
|
176
|
+
def decorator(fn: Callable[_P, Any]) -> Callable[_P, Any]:
|
|
177
|
+
@wraps(fn)
|
|
178
|
+
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> Any:
|
|
179
|
+
return _run_with_retry(lambda: fn(*args, **kwargs), **options) # type: ignore[arg-type]
|
|
180
|
+
|
|
181
|
+
return wrapper
|
|
182
|
+
|
|
183
|
+
return decorator
|
|
184
|
+
return _run_with_retry(func, **options) # type: ignore[arg-type]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def retry_on(*exception_types: Type[Exception]) -> Callable[[Exception], bool]:
|
|
188
|
+
"""Return a predicate that retries only the given exception types.
|
|
189
|
+
|
|
190
|
+
By default only transient failures are retried (HTTP 408/429/500/502/503/504
|
|
191
|
+
or non-HTTP errors). Use this as ``is_retriable`` to opt into retrying
|
|
192
|
+
failures the runtime does not consider transient - for example a file that
|
|
193
|
+
is locked while a user has it open (HTTP 423)::
|
|
194
|
+
|
|
195
|
+
from office365.runtime.exceptions import FileLockedException
|
|
196
|
+
from office365.runtime.retry import retry_on
|
|
197
|
+
|
|
198
|
+
folder.upload_file(name, content).execute_query_retry(is_retriable=retry_on(FileLockedException))
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
exception_types: Exception classes that should be retried.
|
|
202
|
+
"""
|
|
203
|
+
return lambda ex: isinstance(ex, exception_types)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from typing import Tuple
|
|
6
|
+
from typing import Any, Tuple
|
|
7
7
|
|
|
8
8
|
from requests import Response
|
|
9
9
|
from typing_extensions import Self
|
|
@@ -37,6 +37,11 @@ class BaseTransport(ABC):
|
|
|
37
37
|
"""Transport-level request timeout in seconds."""
|
|
38
38
|
return None
|
|
39
39
|
|
|
40
|
+
@property
|
|
41
|
+
def auth(self) -> Any | None:
|
|
42
|
+
"""Transport-level authentication handler, e.g. ``session.auth``."""
|
|
43
|
+
return None
|
|
44
|
+
|
|
40
45
|
def close(self) -> None: # noqa: B027
|
|
41
46
|
"""Release transport resources (connections, etc.)."""
|
|
42
47
|
|
|
@@ -44,6 +44,10 @@ class RequestsTransport(BaseTransport):
|
|
|
44
44
|
def timeout(self) -> int | Tuple[int, int] | None:
|
|
45
45
|
return self._timeout
|
|
46
46
|
|
|
47
|
+
@property
|
|
48
|
+
def auth(self) -> Any | None:
|
|
49
|
+
return self._session.auth
|
|
50
|
+
|
|
47
51
|
def execute(self, request: RequestOptions) -> Response:
|
|
48
52
|
kwargs: dict[str, Any] = {"headers": request.headers}
|
|
49
53
|
if request.verify is not None:
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Transport decorator that paces requests through a shared rate limiter.
|
|
2
|
+
|
|
3
|
+
Wraps any :class:`~office365.runtime.transport.base.BaseTransport` so every
|
|
4
|
+
request is gated by a shared :class:`~office365.runtime.http.throttling.RateLimiter`
|
|
5
|
+
before it is sent and every response (or error's response) is fed back into it.
|
|
6
|
+
The wrapper is transport-agnostic — it composes with ``requests`` today and with
|
|
7
|
+
an async transport later, and it keeps the pacing at the single I/O boundary so
|
|
8
|
+
call sites stay free of side effects.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Tuple
|
|
14
|
+
|
|
15
|
+
from office365.runtime.http.throttling import paced
|
|
16
|
+
from office365.runtime.transport.base import BaseTransport
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from requests import Response
|
|
20
|
+
|
|
21
|
+
from office365.runtime.http.request_options import RequestOptions
|
|
22
|
+
from office365.runtime.http.throttling import RateLimiter
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ThrottledTransport(BaseTransport):
|
|
26
|
+
"""Decorates a transport with fleet-wide pacing from a shared limiter.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
inner: The wrapped transport performing the actual I/O.
|
|
30
|
+
limiter: The shared :class:`RateLimiter` gating the fleet.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, inner: BaseTransport, limiter: "RateLimiter") -> None:
|
|
34
|
+
self._inner = inner
|
|
35
|
+
self._limiter = limiter
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def inner(self) -> BaseTransport:
|
|
39
|
+
"""The wrapped transport."""
|
|
40
|
+
return self._inner
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def limiter(self) -> "RateLimiter":
|
|
44
|
+
"""The shared rate limiter."""
|
|
45
|
+
return self._limiter
|
|
46
|
+
|
|
47
|
+
def execute(self, request: "RequestOptions") -> "Response":
|
|
48
|
+
return paced(lambda: self._inner.execute(request), self._limiter)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def proxies(self) -> dict[str, str] | None:
|
|
52
|
+
return self._inner.proxies
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def verify(self) -> bool | str:
|
|
56
|
+
return self._inner.verify
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def timeout(self) -> int | Tuple[int, int] | None:
|
|
60
|
+
return self._inner.timeout
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def auth(self) -> Any | None:
|
|
64
|
+
return self._inner.auth
|
|
65
|
+
|
|
66
|
+
def close(self) -> None:
|
|
67
|
+
self._inner.close()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Shared reconciliation report for the data pipeline and the migration toolkit.
|
|
2
|
+
|
|
3
|
+
One report type is returned by every ``verify*`` (``RecordCollection.verify``,
|
|
4
|
+
``List.verify``, ``ImportResult.verify`` and ``migration.verify``), so callers
|
|
5
|
+
reason about reconciliation the same way regardless of the operation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class VerificationReport:
|
|
15
|
+
"""Outcome of a source-vs-target reconciliation.
|
|
16
|
+
|
|
17
|
+
``source_count``/``target_count`` are the expected/found item counts,
|
|
18
|
+
``checked`` the number of items checked, ``missing`` the expected keys absent
|
|
19
|
+
on the target, and ``mismatches`` human-readable issues (content mismatches,
|
|
20
|
+
unexpected items, ...). ``ok`` is True when the counts match and both lists
|
|
21
|
+
are empty.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
source_count: int = 0
|
|
25
|
+
target_count: int = 0
|
|
26
|
+
checked: int = 0
|
|
27
|
+
missing: list[str] = field(default_factory=list)
|
|
28
|
+
mismatches: list[str] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def ok(self) -> bool:
|
|
32
|
+
"""Whether the source and target reconcile."""
|
|
33
|
+
return self.source_count == self.target_count and not self.missing and not self.mismatches
|
|
34
|
+
|
|
35
|
+
def summary(self) -> str:
|
|
36
|
+
status = "OK" if self.ok else "MISMATCH"
|
|
37
|
+
return (
|
|
38
|
+
f"{status} | source: {self.source_count}, target: {self.target_count}, "
|
|
39
|
+
f"checked: {self.checked}, missing: {len(self.missing)}, issues: {len(self.mismatches)}"
|
|
40
|
+
)
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
|
|
3
|
+
from typing_extensions import Self
|
|
4
|
+
|
|
5
|
+
from office365.runtime.client_value_collection import ClientValueCollection
|
|
1
6
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
2
7
|
from office365.sharepoint.entity import Entity
|
|
8
|
+
from office365.sharepoint.logger.activityrequest import LogActivityRequest
|
|
3
9
|
|
|
4
10
|
|
|
5
11
|
class ActivityLogger(Entity):
|
|
6
12
|
def log_activity(
|
|
7
|
-
self,
|
|
8
|
-
operation,
|
|
9
|
-
list_id,
|
|
10
|
-
list_item_unique_id,
|
|
11
|
-
affected_resource_url,
|
|
12
|
-
item_type,
|
|
13
|
-
audit_creation_time,
|
|
14
|
-
is_offline,
|
|
13
|
+
self, operation, list_id, list_item_unique_id, affected_resource_url, item_type, audit_creation_time, is_offline
|
|
15
14
|
):
|
|
16
15
|
"""Args:
|
|
17
16
|
operation (str):
|
|
@@ -38,3 +37,114 @@ class ActivityLogger(Entity):
|
|
|
38
37
|
@property
|
|
39
38
|
def entity_type_name(self):
|
|
40
39
|
return "Microsoft.SharePoint.Internal.ActivityLogger"
|
|
40
|
+
|
|
41
|
+
def feedback_direct(
|
|
42
|
+
self,
|
|
43
|
+
operation: str,
|
|
44
|
+
list_id: UUID,
|
|
45
|
+
list_item_unique_id: UUID,
|
|
46
|
+
affected_resource_url: str,
|
|
47
|
+
item_type: str,
|
|
48
|
+
json: str,
|
|
49
|
+
) -> Self:
|
|
50
|
+
"""FeedbackDirect operation.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
operation (str): Operation parameter
|
|
54
|
+
list_id (UUID): ListId parameter
|
|
55
|
+
list_item_unique_id (UUID): ListItemUniqueId parameter
|
|
56
|
+
affected_resource_url (str): AffectedResourceUrl parameter
|
|
57
|
+
item_type (str): ItemType parameter
|
|
58
|
+
json (str): json parameter
|
|
59
|
+
"""
|
|
60
|
+
qry = ServiceOperationQuery(
|
|
61
|
+
self,
|
|
62
|
+
"FeedbackDirect",
|
|
63
|
+
None,
|
|
64
|
+
{
|
|
65
|
+
"Operation": operation,
|
|
66
|
+
"ListId": list_id,
|
|
67
|
+
"ListItemUniqueId": list_item_unique_id,
|
|
68
|
+
"AffectedResourceUrl": affected_resource_url,
|
|
69
|
+
"ItemType": item_type,
|
|
70
|
+
"json": json,
|
|
71
|
+
},
|
|
72
|
+
None,
|
|
73
|
+
None,
|
|
74
|
+
)
|
|
75
|
+
self.context.add_query(qry)
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
def feedback_indirect(
|
|
79
|
+
self,
|
|
80
|
+
operation: str,
|
|
81
|
+
list_id: UUID,
|
|
82
|
+
list_item_unique_id: UUID,
|
|
83
|
+
affected_resource_url: str,
|
|
84
|
+
item_type: str,
|
|
85
|
+
json: str,
|
|
86
|
+
) -> Self:
|
|
87
|
+
"""FeedbackIndirect operation.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
operation (str): Operation parameter
|
|
91
|
+
list_id (UUID): ListId parameter
|
|
92
|
+
list_item_unique_id (UUID): ListItemUniqueId parameter
|
|
93
|
+
affected_resource_url (str): AffectedResourceUrl parameter
|
|
94
|
+
item_type (str): ItemType parameter
|
|
95
|
+
json (str): json parameter
|
|
96
|
+
"""
|
|
97
|
+
qry = ServiceOperationQuery(
|
|
98
|
+
self,
|
|
99
|
+
"FeedbackIndirect",
|
|
100
|
+
None,
|
|
101
|
+
{
|
|
102
|
+
"Operation": operation,
|
|
103
|
+
"ListId": list_id,
|
|
104
|
+
"ListItemUniqueId": list_item_unique_id,
|
|
105
|
+
"AffectedResourceUrl": affected_resource_url,
|
|
106
|
+
"ItemType": item_type,
|
|
107
|
+
"json": json,
|
|
108
|
+
},
|
|
109
|
+
None,
|
|
110
|
+
None,
|
|
111
|
+
)
|
|
112
|
+
self.context.add_query(qry)
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
def log_activity_bulk(
|
|
116
|
+
self,
|
|
117
|
+
operation: str,
|
|
118
|
+
site_id: UUID,
|
|
119
|
+
web_id: UUID,
|
|
120
|
+
list_id: UUID,
|
|
121
|
+
mailbox_type: str,
|
|
122
|
+
requests: ClientValueCollection[LogActivityRequest],
|
|
123
|
+
) -> Self:
|
|
124
|
+
"""LogActivityBulk operation.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
operation (str): Operation parameter
|
|
128
|
+
site_id (UUID): SiteId parameter
|
|
129
|
+
web_id (UUID): WebId parameter
|
|
130
|
+
list_id (UUID): ListId parameter
|
|
131
|
+
mailbox_type (str): MailboxType parameter
|
|
132
|
+
requests (ClientValueCollection[LogActivityRequest]): Requests parameter
|
|
133
|
+
"""
|
|
134
|
+
qry = ServiceOperationQuery(
|
|
135
|
+
self,
|
|
136
|
+
"LogActivityBulk",
|
|
137
|
+
None,
|
|
138
|
+
{
|
|
139
|
+
"Operation": operation,
|
|
140
|
+
"SiteId": site_id,
|
|
141
|
+
"WebId": web_id,
|
|
142
|
+
"ListId": list_id,
|
|
143
|
+
"MailboxType": mailbox_type,
|
|
144
|
+
"Requests": requests,
|
|
145
|
+
},
|
|
146
|
+
None,
|
|
147
|
+
None,
|
|
148
|
+
)
|
|
149
|
+
self.context.add_query(qry)
|
|
150
|
+
return self
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
from typing_extensions import Self
|
|
2
|
+
|
|
1
3
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
4
|
+
from office365.runtime.queries.function import FunctionQuery
|
|
2
5
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
6
|
+
from office365.sharepoint.administration.usageentry import UsageEntry
|
|
3
7
|
from office365.sharepoint.entity import Entity
|
|
4
8
|
|
|
5
9
|
|
|
@@ -20,13 +24,7 @@ class SPAnalyticsUsageService(Entity):
|
|
|
20
24
|
site (str):
|
|
21
25
|
user (str):
|
|
22
26
|
"""
|
|
23
|
-
payload = {
|
|
24
|
-
"EventTypeId": event_type_id,
|
|
25
|
-
"ItemId": item_id,
|
|
26
|
-
"ScopeId": scope_id,
|
|
27
|
-
"Site": site,
|
|
28
|
-
"User": user,
|
|
29
|
-
}
|
|
27
|
+
payload = {"EventTypeId": event_type_id, "ItemId": item_id, "ScopeId": scope_id, "Site": site, "User": user}
|
|
30
28
|
qry = ServiceOperationQuery(self, "logevent", None, payload)
|
|
31
29
|
self.context.add_query(qry)
|
|
32
30
|
return self
|
|
@@ -34,3 +32,13 @@ class SPAnalyticsUsageService(Entity):
|
|
|
34
32
|
@property
|
|
35
33
|
def entity_type_name(self):
|
|
36
34
|
return "Microsoft.SharePoint.Administration.SPAnalyticsUsageService"
|
|
35
|
+
|
|
36
|
+
def logevent(self, usage_entry: UsageEntry) -> Self:
|
|
37
|
+
"""logevent operation.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
usage_entry (UsageEntry): usageEntry parameter
|
|
41
|
+
"""
|
|
42
|
+
qry = FunctionQuery(self, "logevent", [usage_entry], None)
|
|
43
|
+
self.context.add_query(qry)
|
|
44
|
+
return self
|