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
|
@@ -9,9 +9,27 @@ from office365.directory.permissions.identity_set import IdentitySet
|
|
|
9
9
|
from office365.entity import Entity
|
|
10
10
|
from office365.entity_collection import EntityCollection
|
|
11
11
|
from office365.runtime.client_value_collection import ClientValueCollection
|
|
12
|
+
from office365.runtime.limits import Limit, LimitKind, limit
|
|
12
13
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
13
14
|
|
|
15
|
+
_DOC = "https://learn.microsoft.com/en-us/graph/throttling-limits"
|
|
14
16
|
|
|
17
|
+
|
|
18
|
+
@limit(
|
|
19
|
+
Limit("call records", 15_000, LimitKind.SUPPORTED, "requests", "app", doc=_DOC, window_seconds=20),
|
|
20
|
+
Limit("call records", 10_000, LimitKind.SUPPORTED, "requests", "tenant", doc=_DOC, window_seconds=20),
|
|
21
|
+
Limit("call records", 1_500, LimitKind.SUPPORTED, "requests", "app+tenant", doc=_DOC, window_seconds=20),
|
|
22
|
+
Limit(
|
|
23
|
+
"call records",
|
|
24
|
+
40,
|
|
25
|
+
LimitKind.SUPPORTED,
|
|
26
|
+
"requests",
|
|
27
|
+
"resource",
|
|
28
|
+
note="per call record",
|
|
29
|
+
doc=_DOC,
|
|
30
|
+
window_seconds=20,
|
|
31
|
+
),
|
|
32
|
+
)
|
|
15
33
|
class CallRecord(Entity):
|
|
16
34
|
"""Represents a single peer-to-peer call or a group call between multiple participants,
|
|
17
35
|
sometimes referred to as an online meeting."""
|
|
@@ -15,14 +15,17 @@ from office365.communications.operations.cancel_media_processing import CancelMe
|
|
|
15
15
|
from office365.communications.operations.comms import CommsOperation
|
|
16
16
|
from office365.communications.operations.unmute_participant import UnmuteParticipantOperation
|
|
17
17
|
from office365.communications.operations.update_recording_status import UpdateRecordingStatusOperation
|
|
18
|
+
from office365.communications.quotas import CALL_QUOTAS
|
|
18
19
|
from office365.communications.result_info import ResultInfo
|
|
19
20
|
from office365.entity import Entity
|
|
20
21
|
from office365.entity_collection import EntityCollection
|
|
21
22
|
from office365.runtime.client_value_collection import ClientValueCollection
|
|
23
|
+
from office365.runtime.limits import limit
|
|
22
24
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
23
25
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
@limit(*CALL_QUOTAS)
|
|
26
29
|
class Call(Entity):
|
|
27
30
|
"""
|
|
28
31
|
The call resource is created when there is an incoming call for the application or the application creates a
|
|
@@ -6,13 +6,16 @@ from typing import Optional
|
|
|
6
6
|
from typing_extensions import Self
|
|
7
7
|
|
|
8
8
|
from office365.communications.presences.status_message import PresenceStatusMessage
|
|
9
|
+
from office365.communications.quotas import PRESENCE_QUOTAS
|
|
9
10
|
from office365.entity import Entity
|
|
10
11
|
from office365.outlook.calendar.dateTimeTimeZone import DateTimeTimeZone
|
|
11
12
|
from office365.outlook.mail.item_body import ItemBody
|
|
13
|
+
from office365.runtime.limits import limit
|
|
12
14
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
13
15
|
from office365.runtime.types.duration import Duration
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
@limit(*PRESENCE_QUOTAS)
|
|
16
19
|
class Presence(Entity):
|
|
17
20
|
"""Contains information about a user's presence, including their availability and user activity."""
|
|
18
21
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Microsoft Graph cloud-communications throttling quotas (reference).
|
|
2
|
+
|
|
3
|
+
Declared on the model with ``@limit(*CALL_QUOTAS)`` etc. (see ``Call``,
|
|
4
|
+
``Presence``, ``VirtualEvent``). Reference-only — the library reacts to the
|
|
5
|
+
server signals (:mod:`office365.runtime.http.throttling`).
|
|
6
|
+
|
|
7
|
+
Source: https://learn.microsoft.com/en-us/graph/throttling-limits
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from office365.runtime.limits import Limit, LimitKind
|
|
13
|
+
|
|
14
|
+
_DOC = "https://learn.microsoft.com/en-us/graph/throttling-limits"
|
|
15
|
+
_SUPPORTED = LimitKind.SUPPORTED
|
|
16
|
+
|
|
17
|
+
CALL_QUOTAS = (
|
|
18
|
+
Limit(
|
|
19
|
+
"cloud communications",
|
|
20
|
+
50_000,
|
|
21
|
+
_SUPPORTED,
|
|
22
|
+
"requests",
|
|
23
|
+
"app+tenant",
|
|
24
|
+
note="calls",
|
|
25
|
+
doc=_DOC,
|
|
26
|
+
window_seconds=15,
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
PRESENCE_QUOTAS = (
|
|
31
|
+
Limit(
|
|
32
|
+
"cloud communications",
|
|
33
|
+
10_000,
|
|
34
|
+
_SUPPORTED,
|
|
35
|
+
"requests",
|
|
36
|
+
"app+tenant",
|
|
37
|
+
note="presence",
|
|
38
|
+
doc=_DOC,
|
|
39
|
+
window_seconds=30,
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
VIRTUAL_EVENT_QUOTAS = (
|
|
44
|
+
Limit(
|
|
45
|
+
"cloud communications",
|
|
46
|
+
750,
|
|
47
|
+
_SUPPORTED,
|
|
48
|
+
"requests",
|
|
49
|
+
"app",
|
|
50
|
+
request_type="read",
|
|
51
|
+
note="virtual events",
|
|
52
|
+
doc=_DOC,
|
|
53
|
+
window_seconds=30,
|
|
54
|
+
),
|
|
55
|
+
Limit(
|
|
56
|
+
"cloud communications",
|
|
57
|
+
15,
|
|
58
|
+
_SUPPORTED,
|
|
59
|
+
"requests",
|
|
60
|
+
"app",
|
|
61
|
+
request_type="write",
|
|
62
|
+
note="virtual events",
|
|
63
|
+
doc=_DOC,
|
|
64
|
+
window_seconds=30,
|
|
65
|
+
),
|
|
66
|
+
)
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
+
from office365.communications.quotas import VIRTUAL_EVENT_QUOTAS
|
|
3
4
|
from office365.communications.virtualevents.presenter import VirtualEventPresenter
|
|
4
5
|
from office365.communications.virtualevents.session import VirtualEventSession
|
|
5
6
|
from office365.entity import Entity
|
|
6
7
|
from office365.entity_collection import EntityCollection
|
|
7
8
|
from office365.outlook.mail.item_body import ItemBody
|
|
9
|
+
from office365.runtime.limits import limit
|
|
8
10
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
@limit(*VIRTUAL_EVENT_QUOTAS)
|
|
11
14
|
class VirtualEvent(Entity):
|
|
12
15
|
"""Represents an abstract base type for a virtual event.
|
|
13
16
|
Base type of virtualEventTownhall and virtualEventWebinar."""
|
|
@@ -2,14 +2,17 @@ from office365.directory.domains.dns_record import DomainDnsRecord
|
|
|
2
2
|
from office365.directory.domains.state import DomainState
|
|
3
3
|
from office365.directory.objects.collection import DirectoryObjectCollection
|
|
4
4
|
from office365.directory.permissions.require_permission import require_permission
|
|
5
|
+
from office365.directory.quotas import IDENTITY_QUOTAS
|
|
5
6
|
from office365.entity import Entity
|
|
6
7
|
from office365.entity_collection import EntityCollection
|
|
8
|
+
from office365.runtime.limits import limit
|
|
7
9
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
8
10
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
9
11
|
from office365.runtime.types.collections import StringCollection
|
|
10
12
|
from office365.runtime.types.odata_property import odata
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
@limit(*IDENTITY_QUOTAS)
|
|
13
16
|
class Domain(Entity):
|
|
14
17
|
"""
|
|
15
18
|
Represents a domain associated with the tenant.
|
|
@@ -2,12 +2,15 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
from office365.directory.licenses.service_plan_info import ServicePlanInfo
|
|
4
4
|
from office365.directory.licenses.units_detail import LicenseUnitsDetail
|
|
5
|
+
from office365.directory.quotas import IDENTITY_QUOTAS
|
|
5
6
|
from office365.entity import Entity
|
|
6
7
|
from office365.runtime.client_value_collection import ClientValueCollection
|
|
8
|
+
from office365.runtime.limits import limit
|
|
7
9
|
from office365.runtime.types.collections import StringCollection
|
|
8
10
|
from office365.runtime.types.odata_property import odata
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
@limit(*IDENTITY_QUOTAS)
|
|
11
14
|
class SubscribedSku(Entity):
|
|
12
15
|
"""Contains information about a service SKU that a company is subscribed to."""
|
|
13
16
|
|
|
@@ -4,13 +4,16 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
from typing_extensions import Self
|
|
6
6
|
|
|
7
|
+
from office365.directory.quotas import IDENTITY_QUOTAS
|
|
7
8
|
from office365.entity import Entity
|
|
8
9
|
from office365.runtime.client_result import ClientResult
|
|
10
|
+
from office365.runtime.limits import limit
|
|
9
11
|
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
10
12
|
from office365.runtime.types.collections import StringCollection
|
|
11
13
|
from office365.runtime.types.odata_property import odata
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
@limit(*IDENTITY_QUOTAS)
|
|
14
17
|
class DirectoryObject(Entity):
|
|
15
18
|
"""Represents an Azure Active Directory object. The directoryObject type is the base type for many other
|
|
16
19
|
directory entity types."""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Microsoft Graph identity throttling quotas (reference).
|
|
2
|
+
|
|
3
|
+
Declared on the model with ``@limit(*IDENTITY_QUOTAS)`` (see ``DirectoryObject``,
|
|
4
|
+
``SubscribedSku``, ``Domain``) — Graph throttles the identity API surface with a
|
|
5
|
+
**ResourceUnits** token bucket plus write quotas. Reference-only: the library
|
|
6
|
+
reacts to the server signals (:mod:`office365.runtime.http.throttling`).
|
|
7
|
+
|
|
8
|
+
Source: https://learn.microsoft.com/en-us/graph/throttling-limits
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from office365.runtime.limits import Limit, LimitKind
|
|
14
|
+
|
|
15
|
+
_DOC = "https://learn.microsoft.com/en-us/graph/throttling-limits"
|
|
16
|
+
_SUPPORTED = LimitKind.SUPPORTED
|
|
17
|
+
|
|
18
|
+
#: Identity and access quotas (the whole `/users`, `/groups`, ... surface).
|
|
19
|
+
IDENTITY_QUOTAS = (
|
|
20
|
+
Limit(
|
|
21
|
+
"identity",
|
|
22
|
+
3_500,
|
|
23
|
+
_SUPPORTED,
|
|
24
|
+
"resource_units",
|
|
25
|
+
"app+tenant",
|
|
26
|
+
note="small tenants (<50 users); 5,000 medium (50-500), 8,000 large (>500)",
|
|
27
|
+
doc=_DOC,
|
|
28
|
+
window_seconds=10,
|
|
29
|
+
),
|
|
30
|
+
Limit("identity", 150_000, _SUPPORTED, "resource_units", "app", doc=_DOC, window_seconds=20),
|
|
31
|
+
Limit("identity", 3_000, _SUPPORTED, "requests", "app+tenant", doc=_DOC, window_seconds=150, request_type="write"),
|
|
32
|
+
Limit("identity", 35_000, _SUPPORTED, "requests", "app", doc=_DOC, window_seconds=300, request_type="write"),
|
|
33
|
+
Limit("identity", 18_000, _SUPPORTED, "requests", "tenant", doc=_DOC, window_seconds=300, request_type="write"),
|
|
34
|
+
)
|
office365/entity_collection.py
CHANGED
|
@@ -4,16 +4,16 @@ from typing import TYPE_CHECKING, Any, Dict, Optional, Type, Union, cast
|
|
|
4
4
|
|
|
5
5
|
from office365.entity import Entity
|
|
6
6
|
from office365.runtime.client_object import ClientObjectT
|
|
7
|
-
from office365.runtime.client_object_collection import ClientObjectCollection
|
|
8
7
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
9
8
|
from office365.runtime.paths.v4.entity import EntityPath
|
|
10
9
|
from office365.runtime.queries.create_entity import CreateEntityQuery
|
|
10
|
+
from office365.runtime.record_collection import RecordCollection
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from office365.graph_client import GraphClient
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class EntityCollection(
|
|
16
|
+
class EntityCollection(RecordCollection[ClientObjectT]):
|
|
17
17
|
"""A collection container which represents a named collections of entities"""
|
|
18
18
|
|
|
19
19
|
def __init__(
|
office365/graph_client.py
CHANGED
|
@@ -65,6 +65,7 @@ from office365.outlook.calendar.rooms.list import RoomList
|
|
|
65
65
|
from office365.planner.planner import Planner
|
|
66
66
|
from office365.reports.root import ReportRoot
|
|
67
67
|
from office365.runtime.client_runtime_context import ClientRuntimeContext
|
|
68
|
+
from office365.runtime.http.request_options import RequestOptions
|
|
68
69
|
from office365.runtime.odata.v4.batch_request import DEFAULT_MAX_BATCH_BYTES, ODataV4BatchRequest
|
|
69
70
|
from office365.runtime.odata.v4.json_format import V4JsonFormat
|
|
70
71
|
from office365.runtime.paths.resource_path import ResourcePath
|
|
@@ -353,12 +354,37 @@ class GraphClient(ClientRuntimeContext):
|
|
|
353
354
|
)
|
|
354
355
|
return self
|
|
355
356
|
|
|
357
|
+
def with_throttle_priority(self, priority: str = "normal") -> Self:
|
|
358
|
+
"""Send ``x-ms-throttle-priority`` on every request (Graph throttling).
|
|
359
|
+
|
|
360
|
+
Per Microsoft Graph, low-priority requests are throttled first and
|
|
361
|
+
high-priority ones last; the header doesn't change the limits. Use
|
|
362
|
+
``"high"`` only for requests initiated by a user (where throttling causes
|
|
363
|
+
user-visible failures) and ``"low"`` for background work.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
priority: ``"low"``, ``"normal"`` (default) or ``"high"``.
|
|
367
|
+
|
|
368
|
+
Raises:
|
|
369
|
+
ValueError: When ``priority`` isn't one of the accepted values.
|
|
370
|
+
"""
|
|
371
|
+
value = priority.lower()
|
|
372
|
+
if value not in ("low", "normal", "high"):
|
|
373
|
+
raise ValueError("priority must be one of 'low', 'normal', 'high'")
|
|
374
|
+
|
|
375
|
+
def _set_header(request: RequestOptions) -> None:
|
|
376
|
+
request.set_header("x-ms-throttle-priority", value)
|
|
377
|
+
|
|
378
|
+
self.pending_request().before_execute(_set_header, once=False)
|
|
379
|
+
return self
|
|
380
|
+
|
|
356
381
|
def execute_batch(
|
|
357
382
|
self,
|
|
358
383
|
items_per_batch: int = 20,
|
|
359
384
|
success_callback: Optional[Callable[[List[Any]], None]] = None,
|
|
360
385
|
concurrency: int = 1,
|
|
361
386
|
max_batch_bytes: Optional[int] = None,
|
|
387
|
+
sequential: bool = False,
|
|
362
388
|
) -> Self:
|
|
363
389
|
"""
|
|
364
390
|
Execute batched requests
|
|
@@ -371,21 +397,39 @@ class GraphClient(ClientRuntimeContext):
|
|
|
371
397
|
writes aren't re-applied. ``success_callback`` runs on the caller thread
|
|
372
398
|
in completion order (not submission order).
|
|
373
399
|
|
|
400
|
+
Pass ``sequential=True`` to chain the sub-requests with ``dependsOn`` so
|
|
401
|
+
Graph runs them in order (a failed dependency yields HTTP 424). Use it
|
|
402
|
+
only for **ordering-dependent side effects** — read-after-write on the
|
|
403
|
+
same resource, delete-then-create, parent/child ordering — not for
|
|
404
|
+
independent requests (Graph's default parallel mode is faster). A batch
|
|
405
|
+
can't reference another sub-request's response, so it doesn't enable
|
|
406
|
+
create-then-use-id flows. Implies ``concurrency=1`` because Graph only
|
|
407
|
+
sequences within a single ``$batch``.
|
|
408
|
+
|
|
374
409
|
Args:
|
|
375
410
|
items_per_batch: Maximum items per batch (default: 20)
|
|
376
411
|
success_callback: Optional callback for successful requests
|
|
377
412
|
concurrency: Maximum number of concurrent batch requests (default 1)
|
|
378
413
|
max_batch_bytes: Maximum estimated batch payload size (default ~3 MB)
|
|
414
|
+
sequential: Chain sub-requests with ``dependsOn`` (Graph v4 only).
|
|
415
|
+
|
|
416
|
+
Raises:
|
|
417
|
+
ValueError: When ``sequential`` is combined with ``concurrency > 1``.
|
|
379
418
|
"""
|
|
419
|
+
if sequential and concurrency > 1:
|
|
420
|
+
raise ValueError("sequential=True requires concurrency=1 (Graph sequences within a batch only)")
|
|
380
421
|
max_bytes = DEFAULT_MAX_BATCH_BYTES if max_batch_bytes is None else max_batch_bytes
|
|
381
422
|
batches = self._split_batches(items_per_batch, max_bytes)
|
|
423
|
+
if sequential:
|
|
424
|
+
for batch in batches:
|
|
425
|
+
batch.sequential = True
|
|
382
426
|
if concurrency <= 1:
|
|
383
427
|
batch_request = ODataV4BatchRequest("", V4JsonFormat())
|
|
384
428
|
batch_request.beforeExecute += self.pending_request().authenticate_request
|
|
385
429
|
for qry in batches:
|
|
386
430
|
batch_request.execute_query_with_retry(qry)
|
|
387
|
-
if callable(success_callback) and qry.
|
|
388
|
-
success_callback(qry.
|
|
431
|
+
if callable(success_callback) and qry.return_types:
|
|
432
|
+
success_callback(qry.return_types)
|
|
389
433
|
return self
|
|
390
434
|
|
|
391
435
|
self.pending_request()
|
office365/limits.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Unified access to service limits — mechanics, catalogs and discovery.
|
|
2
|
+
|
|
3
|
+
The library declares limits in three layers:
|
|
4
|
+
|
|
5
|
+
- **Mechanics** (:mod:`office365.runtime.limits`): :class:`Limit` (a static
|
|
6
|
+
threshold *or* a rate quota), the :func:`limit` decorator, and the guardrails
|
|
7
|
+
(:func:`exceeds` / :func:`warn_if_exceeds` / :func:`ensure_within` /
|
|
8
|
+
:func:`hint`).
|
|
9
|
+
- **Catalogs**: product vocabularies — e.g. ``sharepoint.thresholds.Limits``
|
|
10
|
+
(SharePoint) and the model-bound Graph quotas (``directory.quotas`` /
|
|
11
|
+
``communications.quotas``).
|
|
12
|
+
- **Discovery**: a class carries its limits via ``@limit`` (``declared_limits()``);
|
|
13
|
+
:func:`limits_of` / :func:`verify_limits` inspect them; :func:`catalog`
|
|
14
|
+
aggregates every registered source.
|
|
15
|
+
|
|
16
|
+
Products register their catalogs with :func:`register_catalog`; ``@limit`` on a
|
|
17
|
+
class registers that class automatically (under ``product="model"``).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from office365.runtime.limits import (
|
|
23
|
+
DEFAULT_BATCH_SIZE,
|
|
24
|
+
SAFE_PAGE_SIZE,
|
|
25
|
+
Limit,
|
|
26
|
+
LimitCatalog,
|
|
27
|
+
LimitDecl,
|
|
28
|
+
LimitExceededError,
|
|
29
|
+
LimitKind,
|
|
30
|
+
LimitReport,
|
|
31
|
+
bounded,
|
|
32
|
+
catalog,
|
|
33
|
+
collect_class_limits,
|
|
34
|
+
collect_limit_meta,
|
|
35
|
+
ensure_within,
|
|
36
|
+
exceeds,
|
|
37
|
+
hint,
|
|
38
|
+
limit,
|
|
39
|
+
limits_of,
|
|
40
|
+
register_catalog,
|
|
41
|
+
verify_limits,
|
|
42
|
+
warn_if_exceeds,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"DEFAULT_BATCH_SIZE",
|
|
47
|
+
"SAFE_PAGE_SIZE",
|
|
48
|
+
"Limit",
|
|
49
|
+
"LimitCatalog",
|
|
50
|
+
"LimitDecl",
|
|
51
|
+
"LimitExceededError",
|
|
52
|
+
"LimitKind",
|
|
53
|
+
"LimitReport",
|
|
54
|
+
"bounded",
|
|
55
|
+
"catalog",
|
|
56
|
+
"collect_class_limits",
|
|
57
|
+
"collect_limit_meta",
|
|
58
|
+
"ensure_within",
|
|
59
|
+
"exceeds",
|
|
60
|
+
"hint",
|
|
61
|
+
"limit",
|
|
62
|
+
"limits_of",
|
|
63
|
+
"register_catalog",
|
|
64
|
+
"verify_limits",
|
|
65
|
+
"warn_if_exceeds",
|
|
66
|
+
]
|
office365/migration/__init__.py
CHANGED
|
@@ -44,7 +44,9 @@ from office365.migration.manifest import Manifest
|
|
|
44
44
|
from office365.migration.report import MigrationReport, build_report, export_reports
|
|
45
45
|
from office365.migration.runner import MigrationRunner
|
|
46
46
|
from office365.migration.server_job import MigrationServerJob
|
|
47
|
-
from office365.migration.session import MigrationSession
|
|
47
|
+
from office365.migration.session import MigrationSession, SessionTask
|
|
48
|
+
from office365.migration.settings import MigrationSettings
|
|
49
|
+
from office365.migration.tasks import MigrationTask
|
|
48
50
|
from office365.migration.validators import VerificationReport, verify
|
|
49
51
|
|
|
50
52
|
if TYPE_CHECKING:
|
|
@@ -91,10 +93,13 @@ __all__ = [
|
|
|
91
93
|
"MigrationRunner",
|
|
92
94
|
"MigrationServerJob",
|
|
93
95
|
"MigrationSession",
|
|
96
|
+
"MigrationSettings",
|
|
94
97
|
"MigrationStats",
|
|
98
|
+
"MigrationTask",
|
|
95
99
|
"ScanContainer",
|
|
96
100
|
"ScanDefinition",
|
|
97
101
|
"ScanReport",
|
|
102
|
+
"SessionTask",
|
|
98
103
|
"VerificationReport",
|
|
99
104
|
"build_report",
|
|
100
105
|
"export_reports",
|
|
@@ -9,8 +9,8 @@ SharePoint, the filesystem, and (later) S3 / PostgreSQL / Kafka all plug in here
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
from abc import ABC, abstractmethod
|
|
12
|
-
from collections.abc import
|
|
13
|
-
from typing import TYPE_CHECKING, Optional
|
|
12
|
+
from collections.abc import Iterable
|
|
13
|
+
from typing import TYPE_CHECKING, Callable, Optional
|
|
14
14
|
|
|
15
15
|
from office365.migration.base import ConflictResolution, MigrationItem
|
|
16
16
|
|
|
@@ -22,7 +22,12 @@ MigrationProgress = Optional[Callable[["Progress"], None]]
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class DataSource(ABC):
|
|
25
|
-
"""Reads migration items from a source.
|
|
25
|
+
"""Reads migration items from a source.
|
|
26
|
+
|
|
27
|
+
Optional hook (not declared here so ``hasattr`` reflects real support):
|
|
28
|
+
``read_permissions(item) -> list[PermissionEntry]`` — the item's ACLs, used
|
|
29
|
+
for best-effort ``preserve_permissions``. Absent when the source has none.
|
|
30
|
+
"""
|
|
26
31
|
|
|
27
32
|
@abstractmethod
|
|
28
33
|
def list_items(self, progress: MigrationProgress = None) -> list[MigrationItem]:
|
|
@@ -38,7 +43,12 @@ class DataSource(ABC):
|
|
|
38
43
|
|
|
39
44
|
|
|
40
45
|
class DataTarget(ABC):
|
|
41
|
-
"""Writes migration items to a target.
|
|
46
|
+
"""Writes migration items to a target.
|
|
47
|
+
|
|
48
|
+
Optional hooks (not declared here so ``hasattr`` reflects real support),
|
|
49
|
+
used for best-effort fidelity after a write:
|
|
50
|
+
``apply_timestamps(item)`` and ``apply_permissions(item, permissions)``.
|
|
51
|
+
"""
|
|
42
52
|
|
|
43
53
|
@abstractmethod
|
|
44
54
|
def exists(self, item: MigrationItem) -> bool:
|
|
@@ -12,9 +12,11 @@ import json
|
|
|
12
12
|
from datetime import datetime, timezone
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
-
from office365.migration._util import emit_progress, iso, record_to_json
|
|
16
15
|
from office365.migration.adapters import MigrationProgress
|
|
17
16
|
from office365.migration.base import MigrationItem
|
|
17
|
+
from office365.runtime.converters.json_file import record_to_json
|
|
18
|
+
from office365.runtime.converters.scalars import iso
|
|
19
|
+
from office365.runtime.operations import emit_progress
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class FileSystemSource:
|
|
@@ -41,6 +43,7 @@ class FileSystemSource:
|
|
|
41
43
|
dest_path=str(path.relative_to(self._root)).replace("\\", "/"),
|
|
42
44
|
size_bytes=stat.st_size,
|
|
43
45
|
modified=iso(datetime.fromtimestamp(stat.st_mtime, timezone.utc)),
|
|
46
|
+
created=iso(datetime.fromtimestamp(stat.st_ctime, timezone.utc)),
|
|
44
47
|
)
|
|
45
48
|
items.append(item)
|
|
46
49
|
emit_progress(progress, done=len(items), stage="planning", items=[item])
|
|
@@ -12,8 +12,8 @@ from __future__ import annotations
|
|
|
12
12
|
import os
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
-
from office365.migration._util import write_csv_json
|
|
16
15
|
from office365.migration.assessment.report import AssessmentReport
|
|
16
|
+
from office365.migration.report_io import write_formats
|
|
17
17
|
|
|
18
18
|
_COLUMNS = ["severity", "category", "location", "message", "suggestion"]
|
|
19
19
|
|
|
@@ -28,7 +28,7 @@ def export_assessment(report: AssessmentReport, output_dir: str | Path) -> list[
|
|
|
28
28
|
Returns:
|
|
29
29
|
List of written file paths.
|
|
30
30
|
"""
|
|
31
|
-
written =
|
|
31
|
+
written = write_formats(report.to_records(), output_dir, "AssessmentReport", columns=_COLUMNS)
|
|
32
32
|
written += export_scan_reports(report, output_dir)
|
|
33
33
|
return written
|
|
34
34
|
|
|
@@ -42,10 +42,10 @@ def export_scan_reports(report: AssessmentReport, output_dir: str | Path) -> lis
|
|
|
42
42
|
for scan in report.scan_reports.values():
|
|
43
43
|
if not scan.records:
|
|
44
44
|
continue
|
|
45
|
-
written +=
|
|
45
|
+
written += write_formats(
|
|
46
|
+
scan.to_records(),
|
|
46
47
|
os.path.join(output_dir, "ScannerReports"),
|
|
47
48
|
f"{scan.name}-detail",
|
|
48
|
-
scan.
|
|
49
|
-
list(scan.columns),
|
|
49
|
+
columns=list(scan.columns),
|
|
50
50
|
)
|
|
51
51
|
return written
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from office365.runtime.limits import Limit
|
|
2
7
|
|
|
3
8
|
|
|
4
9
|
@dataclass
|
|
@@ -8,3 +13,17 @@ class AssessmentIssue:
|
|
|
8
13
|
location: str # list/folder/field path
|
|
9
14
|
message: str
|
|
10
15
|
suggestion: str = ""
|
|
16
|
+
risk_code: str = "" # product-specific scan-assessment code (SPMT), when applicable
|
|
17
|
+
limit: Optional[Limit] = None # the authoritative (product-agnostic) limit, when applicable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#: SPMT scan-assessment risk codes the library can emit (a subset — the ones tied
|
|
21
|
+
#: to SharePoint service limits). See
|
|
22
|
+
#: https://learn.microsoft.com/en-us/sharepointmigration/spmt-scan-risk-codes
|
|
23
|
+
RISK_CODES: dict[str, str] = {
|
|
24
|
+
"LIST_VIEW_EXCEED_LIMIT": "The list view shows more items than the list view threshold.",
|
|
25
|
+
"ITEM_COUNT_EXCEED_INDEX_LIMIT": "Item count is too large to create a column index.",
|
|
26
|
+
"ITEM_COUNT_EXCEED_LIMIT": "Item count exceeds the per-list maximum.",
|
|
27
|
+
"LIST_VIEW_LOOKUP_EXCEED_LIMIT": "Lookup/people/managed-metadata columns exceed the list view lookup threshold.",
|
|
28
|
+
"UNIQUE_PERMISSION_EXCEED_LIMIT": "Unique permissions per list exceed the supported/recommended limit.",
|
|
29
|
+
}
|
|
@@ -172,6 +172,17 @@ class AssessmentReport(ClientValue):
|
|
|
172
172
|
"location": issue.location,
|
|
173
173
|
"message": issue.message,
|
|
174
174
|
"suggestion": issue.suggestion,
|
|
175
|
+
"risk_code": issue.risk_code,
|
|
175
176
|
}
|
|
176
177
|
for issue in self.issues
|
|
177
178
|
]
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def by_risk_code(self) -> dict[str, int]:
|
|
182
|
+
"""Issue counts per SPMT risk code (issues without a code are excluded)."""
|
|
183
|
+
self._ensure_finalized()
|
|
184
|
+
counts: dict[str, int] = {}
|
|
185
|
+
for issue in self.issues:
|
|
186
|
+
if issue.risk_code:
|
|
187
|
+
counts[issue.risk_code] = counts.get(issue.risk_code, 0) + 1
|
|
188
|
+
return counts
|