Office365-REST-Python-Client 3.2.0__py3-none-any.whl → 3.3.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 (2963) hide show
  1. office365/admin/admin.py +45 -0
  2. office365/admin/baseline_parameter.py +3 -0
  3. office365/admin/baseline_parameter_type.py +14 -0
  4. office365/admin/browser_shared_cookie.py +99 -0
  5. office365/admin/browser_site.py +101 -0
  6. office365/admin/browser_site_list.py +87 -0
  7. office365/admin/configuration_management.py +86 -0
  8. office365/admin/edge/edge.py +25 -0
  9. office365/admin/exchange/admin.py +35 -0
  10. office365/admin/exchange/exchange_message_trace.py +15 -1
  11. office365/admin/exchange/exchange_message_trace_status.py +2 -0
  12. office365/admin/internet_explorer_mode.py +28 -0
  13. office365/admin/message_tracing_root.py +28 -0
  14. office365/admin/microsoft365/apps.py +26 -0
  15. office365/admin/microsoft365/m365_apps_installation_options.py +47 -0
  16. office365/admin/people/insights_settings.py +21 -0
  17. office365/admin/people/profile_property_setting.py +27 -0
  18. office365/admin/people/profile_source.py +48 -0
  19. office365/admin/people/pronouns_settings.py +16 -0
  20. office365/admin/people/settings.py +58 -0
  21. office365/admin/report_settings.py +13 -0
  22. office365/admin/sharepoint/settings.py +88 -1
  23. office365/backuprestore/browse_session_base.py +101 -0
  24. office365/backuprestore/email_notifications_setting.py +40 -0
  25. office365/backuprestore/onedrive_for_business_browse_session.py +16 -0
  26. office365/backuprestore/onedrive_for_business_restore_session.py +68 -0
  27. office365/backuprestore/protection_unit_base.py +92 -0
  28. office365/backuprestore/restorepoints/collection.py +61 -0
  29. office365/backuprestore/restorepoints/search_result.py +6 -0
  30. office365/backuprestore/root.py +354 -0
  31. office365/backuprestore/serviceapps/service_app.py +74 -0
  32. office365/backuprestore/sharepoint_browse_session.py +16 -0
  33. office365/backuprestore/sharepoint_protection_policy.py +60 -0
  34. office365/backuprestore/sharepoint_restore_session.py +66 -0
  35. office365/booking/appointment.py +197 -0
  36. office365/booking/business/business.py +75 -1
  37. office365/booking/business/collection.py +22 -0
  38. office365/booking/currency.py +13 -0
  39. office365/booking/custom_question.py +40 -0
  40. office365/booking/customer.py +51 -0
  41. office365/booking/service.py +145 -1
  42. office365/booking/staff/member.py +76 -0
  43. office365/common/add_large_gallery_view_operation.py +8 -0
  44. office365/common/agent_identity.py +45 -0
  45. office365/common/agent_identity_blueprint.py +41 -0
  46. office365/common/agent_identity_blueprint_principal.py +34 -0
  47. office365/common/agent_user.py +7 -0
  48. office365/common/ai_interaction.py +108 -0
  49. office365/common/akamai_web_application_firewall_provider.py +31 -0
  50. office365/common/anonymous_calendar_sharing_free_busy_detail.py +7 -0
  51. office365/common/anonymous_calendar_sharing_free_busy_reviewer.py +7 -0
  52. office365/common/anonymous_calendar_sharing_free_busy_simple.py +7 -0
  53. office365/common/anonymous_guest_conversation_member.py +16 -0
  54. office365/common/apple_device_features_configuration_base.py +7 -0
  55. office365/common/apple_managed_identity_provider.py +31 -0
  56. office365/common/arkose_fraud_protection_provider.py +31 -0
  57. office365/common/attribute_set.py +21 -0
  58. office365/common/azure_communication_services_user_conversation_member.py +16 -0
  59. office365/common/b2_b_registration_metrics_base.py +27 -0
  60. office365/common/b2_b_sign_in_activity_metrics_base.py +37 -0
  61. office365/common/base_map_feature.py +7 -0
  62. office365/common/building.py +39 -0
  63. office365/common/building_map.py +41 -0
  64. office365/common/complex_extension_value.py +10 -0
  65. office365/common/configuration_baseline.py +40 -0
  66. office365/common/configuration_drift.py +68 -0
  67. office365/common/configuration_monitor.py +103 -0
  68. office365/common/configuration_monitoring_result.py +56 -0
  69. office365/common/configuration_snapshot_job.py +71 -0
  70. office365/common/contract.py +32 -0
  71. office365/common/cross_tenant_access_policy_configuration_default.py +163 -0
  72. office365/common/cross_tenant_access_policy_configuration_partner.py +188 -0
  73. office365/common/cross_tenant_calendar_availability_basic.py +7 -0
  74. office365/common/cross_tenant_calendar_availability_limited_details.py +7 -0
  75. office365/common/cross_tenant_calendar_sharing_free_busy_detail.py +7 -0
  76. office365/common/cross_tenant_calendar_sharing_free_busy_reviewer.py +7 -0
  77. office365/common/cross_tenant_calendar_sharing_free_busy_simple.py +7 -0
  78. office365/common/cross_tenant_identity_sync_policy_partner.py +35 -0
  79. office365/common/cross_tenant_mail_tips_all.py +7 -0
  80. office365/common/cross_tenant_mail_tips_limited.py +7 -0
  81. office365/common/cross_tenant_migration.py +7 -0
  82. office365/common/cross_tenant_open_profile_card.py +7 -0
  83. office365/common/cross_tenant_places_desk_booking.py +7 -0
  84. office365/common/cross_tenant_places_room_booking.py +7 -0
  85. office365/common/custom_authentication_extension.py +25 -0
  86. office365/common/custom_data_provided_resource.py +25 -0
  87. office365/common/custom_data_provided_resource_access_review_upload_session.py +7 -0
  88. office365/common/data_source_configuration.py +19 -0
  89. office365/common/delta_participants.py +31 -0
  90. office365/common/desk.py +51 -0
  91. office365/common/dictionaries.py +10 -0
  92. office365/common/directory_object_partner_reference.py +32 -0
  93. office365/common/domain_dns_mx_record.py +21 -0
  94. office365/common/domain_dns_srv_record.py +41 -0
  95. office365/common/domain_dns_txt_record.py +16 -0
  96. office365/common/domain_dns_unavailable_record.py +16 -0
  97. office365/common/drive_protection_unit.py +26 -0
  98. office365/common/drive_protection_units_bulk_addition_job.py +20 -0
  99. office365/common/drive_restore_artifact.py +26 -0
  100. office365/common/drive_restore_artifacts_bulk_addition_request.py +20 -0
  101. office365/common/edition_upgrade_configuration.py +47 -0
  102. office365/common/email_authentication_method_configuration.py +44 -0
  103. office365/common/emergency_call_event.py +31 -0
  104. office365/common/end_user_settings.py +32 -0
  105. office365/common/endpoint.py +36 -0
  106. office365/common/engagement_async_operation.py +31 -0
  107. office365/common/engagement_conversation_discussion_message.py +7 -0
  108. office365/common/engagement_conversation_question_message.py +16 -0
  109. office365/common/engagement_conversation_system_message.py +7 -0
  110. office365/common/engagement_role.py +33 -0
  111. office365/common/engagement_role_member.py +33 -0
  112. office365/common/enrollment_troubleshooting_event.py +63 -0
  113. office365/common/enterprise_code_signing_certificate.py +57 -0
  114. office365/common/error_detail.py +16 -0
  115. office365/common/event_message_request.py +54 -0
  116. office365/common/event_message_response.py +29 -0
  117. office365/common/exchange_protection_policy.py +60 -0
  118. office365/common/exchange_restore_session.py +68 -0
  119. office365/common/exchange_settings.py +16 -0
  120. office365/common/external_authentication_method.py +21 -0
  121. office365/common/external_authentication_method_configuration.py +54 -0
  122. office365/common/external_domain_name.py +7 -0
  123. office365/common/external_users_self_service_sign_up_events_flow.py +85 -0
  124. office365/common/fido2_authentication_method_configuration.py +72 -0
  125. office365/common/fido2_combination_configuration.py +15 -0
  126. office365/common/fixture_map.py +16 -0
  127. office365/common/floor.py +16 -0
  128. office365/common/footprint_map.py +7 -0
  129. office365/common/granular_drive_restore_artifact.py +16 -0
  130. office365/common/granular_mailbox_restore_artifact.py +21 -0
  131. office365/common/granular_restore_artifact_base.py +62 -0
  132. office365/common/granular_site_restore_artifact.py +16 -0
  133. office365/common/human_security_fraud_protection_provider.py +21 -0
  134. office365/common/identity_built_in_user_flow_attribute.py +7 -0
  135. office365/common/identity_custom_user_flow_attribute.py +7 -0
  136. office365/common/image_info.py +17 -0
  137. office365/common/imported_windows_autopilot_device_identity_upload.py +52 -0
  138. office365/common/intune_brand.py +29 -0
  139. office365/common/ip_named_location.py +27 -0
  140. office365/common/ipv4_range.py +15 -0
  141. office365/common/ipv6_range.py +15 -0
  142. office365/common/key_value.py +15 -0
  143. office365/common/learning_assignment.py +48 -0
  144. office365/common/level_map.py +52 -0
  145. office365/common/mailbox.py +26 -0
  146. office365/common/mailbox_folder.py +104 -0
  147. office365/common/mailbox_item.py +61 -0
  148. office365/common/mailbox_protection_rule.py +16 -0
  149. office365/common/mailbox_protection_unit.py +26 -0
  150. office365/common/mailbox_protection_units_bulk_addition_job.py +20 -0
  151. office365/common/mailbox_restore_artifact.py +26 -0
  152. office365/common/mailbox_restore_artifacts_bulk_addition_request.py +20 -0
  153. office365/common/managed_android_lob_app.py +40 -0
  154. office365/common/managed_android_store_app.py +35 -0
  155. office365/common/managed_app_status_raw.py +21 -0
  156. office365/common/managed_ios_lob_app.py +56 -0
  157. office365/common/managed_ios_store_app.py +45 -0
  158. office365/common/managed_mobile_lob_app.py +45 -0
  159. office365/common/microsoft_account_user_conversation_member.py +16 -0
  160. office365/common/microsoft_authenticator_authentication_method_configuration.py +51 -0
  161. office365/common/microsoft_authenticator_authentication_method_target.py +25 -0
  162. office365/common/microsoft_store_for_business_app.py +45 -0
  163. office365/common/mime_content.py +15 -0
  164. office365/common/mute_participant_operation.py +8 -0
  165. office365/common/oidc_identity_provider.py +69 -0
  166. office365/common/on_attribute_collection_listener.py +25 -0
  167. office365/common/on_attribute_collection_start_custom_extension.py +7 -0
  168. office365/common/on_attribute_collection_start_listener.py +25 -0
  169. office365/common/on_attribute_collection_submit_custom_extension.py +7 -0
  170. office365/common/on_attribute_collection_submit_listener.py +25 -0
  171. office365/common/on_authentication_method_load_start_listener.py +25 -0
  172. office365/common/on_email_otp_send_listener.py +21 -0
  173. office365/common/on_fraud_protection_load_start_listener.py +25 -0
  174. office365/common/on_interactive_auth_flow_start_listener.py +25 -0
  175. office365/common/on_otp_send_custom_extension.py +7 -0
  176. office365/common/on_password_submit_custom_extension.py +7 -0
  177. office365/common/on_password_submit_listener.py +25 -0
  178. office365/common/on_token_issuance_start_listener.py +25 -0
  179. office365/common/on_user_create_start_listener.py +25 -0
  180. office365/common/on_verified_id_claim_validation_listener.py +25 -0
  181. office365/common/operation.py +35 -0
  182. office365/common/os_version_count.py +17 -0
  183. office365/common/participant_joining_notification.py +22 -0
  184. office365/common/participant_left_notification.py +27 -0
  185. office365/common/passkey_authentication_method_target.py +15 -0
  186. office365/common/passkey_profile.py +50 -0
  187. office365/common/phone_user_conversation_member.py +16 -0
  188. office365/common/play_prompt_operation.py +8 -0
  189. office365/common/privacy.py +32 -0
  190. office365/common/prompt.py +10 -0
  191. office365/common/protection_units_bulk_job_base.py +64 -0
  192. office365/common/provision_response.py +15 -0
  193. office365/common/proxied_domain.py +15 -0
  194. office365/common/qr_code.py +46 -0
  195. office365/common/qr_code_pin_authentication_method.py +41 -0
  196. office365/common/qr_code_pin_authentication_method_configuration.py +42 -0
  197. office365/common/qr_pin.py +32 -0
  198. office365/common/reading_assignment_submission.py +123 -0
  199. office365/common/reading_coach_passage.py +63 -0
  200. office365/common/record_operation.py +22 -0
  201. office365/common/reflect_check_in_response.py +70 -0
  202. office365/common/related_tenants_refresh_request.py +16 -0
  203. office365/common/reports_root.py +79 -0
  204. office365/common/request.py +47 -0
  205. office365/common/reseller_delegated_admin_relationship.py +21 -0
  206. office365/common/restore_artifact_base.py +59 -0
  207. office365/common/restore_artifacts_bulk_request_base.py +104 -0
  208. office365/common/restore_point.py +43 -0
  209. office365/common/restore_session_base.py +93 -0
  210. office365/common/role_assignment.py +40 -0
  211. office365/common/role_definition.py +52 -0
  212. office365/common/saml_or_ws_fed_external_domain_federation.py +28 -0
  213. office365/common/section.py +7 -0
  214. office365/common/section_map.py +16 -0
  215. office365/common/send_dtmf_tones_operation.py +26 -0
  216. office365/common/service_announcement_attachment.py +37 -0
  217. office365/common/setting_source.py +17 -0
  218. office365/common/shared_pc_configuration.py +88 -0
  219. office365/common/sharepoint_embedded_configuration.py +14 -0
  220. office365/common/site_protection_rule.py +16 -0
  221. office365/common/site_protection_unit.py +26 -0
  222. office365/common/site_protection_units_bulk_addition_job.py +20 -0
  223. office365/common/site_restore_artifact.py +26 -0
  224. office365/common/site_restore_artifacts_bulk_addition_request.py +20 -0
  225. office365/common/skype_for_business_user_conversation_member.py +21 -0
  226. office365/common/skype_user_conversation_member.py +16 -0
  227. office365/common/software_oath_authentication_method.py +16 -0
  228. office365/common/software_oath_authentication_method_configuration.py +32 -0
  229. office365/common/speaker_assignment_submission.py +108 -0
  230. office365/common/storage_quota_breakdown.py +26 -0
  231. office365/common/subscribe_to_tone_operation.py +8 -0
  232. office365/common/task_file_attachment.py +11 -0
  233. office365/common/teamwork_hosted_content.py +21 -0
  234. office365/common/temporary_access_pass_authentication_method_configuration.py +57 -0
  235. office365/common/unit_map.py +16 -0
  236. office365/common/verifiable_credential_authentication_method_target.py +15 -0
  237. office365/common/verifiable_credentials_authentication_method_configuration.py +34 -0
  238. office365/common/voice_authentication_method_configuration.py +37 -0
  239. office365/common/web_app.py +21 -0
  240. office365/common/what_if_analysis_result.py +30 -0
  241. office365/communications/aad_user_conversation_member_result.py +14 -0
  242. office365/communications/aad_user_notification_recipient.py +14 -0
  243. office365/communications/accept_join_response.py +10 -0
  244. office365/communications/app_hosted_media_config.py +14 -0
  245. office365/communications/application_identity.py +15 -0
  246. office365/communications/application_instance_identity.py +15 -0
  247. office365/communications/attendance_interval.py +17 -0
  248. office365/communications/audio_conferencing.py +20 -0
  249. office365/communications/azure_communication_services_user_identity.py +14 -0
  250. office365/communications/broadcast_meeting_caption_settings.py +17 -0
  251. office365/communications/call_participant_info.py +15 -0
  252. office365/communications/callrecords/administrative_unit_info.py +14 -0
  253. office365/communications/callrecords/call_record.py +13 -1
  254. office365/communications/callrecords/client_user_agent.py +19 -0
  255. office365/communications/callrecords/collection.py +8 -0
  256. office365/communications/callrecords/device_info.py +34 -0
  257. office365/communications/callrecords/direct_routing_log_row.py +47 -0
  258. office365/communications/callrecords/endpoint.py +8 -1
  259. office365/communications/callrecords/failure_info.py +16 -0
  260. office365/communications/callrecords/feedback_token_set.py +10 -0
  261. office365/communications/callrecords/media.py +23 -0
  262. office365/communications/callrecords/media_stream.py +65 -0
  263. office365/communications/callrecords/network_info.py +53 -0
  264. office365/communications/callrecords/organizer.py +7 -0
  265. office365/communications/callrecords/participant.py +7 -0
  266. office365/communications/callrecords/participant_base.py +31 -0
  267. office365/communications/callrecords/participant_endpoint.py +23 -0
  268. office365/communications/callrecords/pstn_call_log_row.py +40 -0
  269. office365/communications/callrecords/segment.py +34 -0
  270. office365/communications/callrecords/service_endpoint.py +10 -0
  271. office365/communications/callrecords/service_user_agent.py +15 -0
  272. office365/communications/callrecords/session.py +13 -1
  273. office365/communications/callrecords/trace_route_hop.py +20 -0
  274. office365/communications/callrecords/user_agent.py +15 -0
  275. office365/communications/callrecords/user_feedback.py +18 -0
  276. office365/communications/callrecords/user_identity.py +14 -0
  277. office365/communications/callrecords/wifi_band.py +15 -0
  278. office365/communications/callrecords/wifi_radio_type.py +18 -0
  279. office365/communications/calls/ai_insight.py +63 -0
  280. office365/communications/calls/audio_routing_group.py +32 -0
  281. office365/communications/calls/call.py +81 -5
  282. office365/communications/calls/content_sharing_session.py +7 -0
  283. office365/communications/calls/ended_event_message_detail.py +6 -0
  284. office365/communications/calls/event.py +40 -0
  285. office365/communications/calls/incoming_context.py +14 -1
  286. office365/communications/calls/invitation_participant_info.py +4 -0
  287. office365/communications/calls/media_state.py +15 -0
  288. office365/communications/calls/options.py +8 -0
  289. office365/communications/calls/participant.py +48 -1
  290. office365/communications/calls/participant_info.py +17 -1
  291. office365/communications/calls/route.py +13 -1
  292. office365/communications/calls/transcription_info.py +17 -0
  293. office365/communications/chat_members_notification_recipient.py +14 -0
  294. office365/communications/chat_renamed_event_message_detail.py +17 -0
  295. office365/communications/chat_restrictions.py +14 -0
  296. office365/communications/cloud_communications.py +35 -1
  297. office365/communications/comms_notification.py +15 -0
  298. office365/communications/comms_notifications.py +18 -0
  299. office365/communications/conversation_member_role_updated_event_message_detail.py +19 -0
  300. office365/communications/emergency_caller_info.py +19 -0
  301. office365/communications/encrypted_identity.py +10 -0
  302. office365/communications/engagement_identity_set.py +16 -0
  303. office365/communications/guest_identity.py +14 -0
  304. office365/communications/incoming_call_options.py +10 -0
  305. office365/communications/invite_new_bot_response.py +14 -0
  306. office365/communications/join_meeting_id_settings.py +16 -0
  307. office365/communications/max_work_location_details.py +15 -0
  308. office365/communications/media_config.py +10 -0
  309. office365/communications/media_info.py +15 -0
  310. office365/communications/media_prompt.py +15 -0
  311. office365/communications/meetings/join_meeting_id_info.py +13 -0
  312. office365/communications/onlinemeetings/attendance_record.py +62 -0
  313. office365/communications/onlinemeetings/base.py +86 -5
  314. office365/communications/onlinemeetings/broadcast_settings.py +6 -1
  315. office365/communications/onlinemeetings/chatmode.py +8 -9
  316. office365/communications/onlinemeetings/collection.py +9 -1
  317. office365/communications/onlinemeetings/engagement_conversation.py +16 -2
  318. office365/communications/onlinemeetings/meeting_attendance_report.py +64 -0
  319. office365/communications/onlinemeetings/online_meeting_info.py +6 -0
  320. office365/communications/onlinemeetings/restricted.py +16 -0
  321. office365/communications/onlinemeetings/sensitivity_label_assignment.py +14 -0
  322. office365/communications/operations/comms.py +22 -1
  323. office365/communications/organizer_meeting_info.py +15 -0
  324. office365/communications/out_of_office_settings.py +15 -0
  325. office365/communications/outgoing_call_options.py +10 -0
  326. office365/communications/participant_joining_response.py +10 -0
  327. office365/communications/phone_identity.py +10 -0
  328. office365/communications/presences/presence.py +19 -1
  329. office365/communications/presences/status_message.py +4 -0
  330. office365/communications/recording_info.py +17 -0
  331. office365/communications/reject_join_response.py +15 -0
  332. office365/communications/removed_state.py +14 -0
  333. office365/communications/service_hosted_media_config.py +16 -0
  334. office365/communications/teleconference_device_audio_quality.py +10 -0
  335. office365/communications/teleconference_device_media_quality.py +38 -0
  336. office365/communications/teleconference_device_screen_sharing_quality.py +10 -0
  337. office365/communications/teleconference_device_video_quality.py +17 -0
  338. office365/communications/teleconferencedevicequality.py +32 -1
  339. office365/communications/token_meeting_info.py +14 -0
  340. office365/communications/tone_info.py +16 -0
  341. office365/communications/user_identity.py +14 -0
  342. office365/communications/user_work_location.py +18 -0
  343. office365/communications/virtualevents/external_information.py +15 -0
  344. office365/communications/virtualevents/external_registration_information.py +15 -0
  345. office365/communications/virtualevents/presenter.py +35 -0
  346. office365/communications/virtualevents/presenter_details.py +21 -0
  347. office365/communications/virtualevents/presenter_info.py +17 -0
  348. office365/communications/virtualevents/registration.py +109 -0
  349. office365/communications/virtualevents/registration_configuration.py +52 -0
  350. office365/communications/virtualevents/registration_custom_question.py +31 -0
  351. office365/communications/virtualevents/registration_predefined_question.py +25 -0
  352. office365/communications/virtualevents/registration_question_answer.py +19 -0
  353. office365/communications/virtualevents/registration_question_base.py +21 -0
  354. office365/communications/virtualevents/root.py +21 -0
  355. office365/communications/virtualevents/session.py +35 -0
  356. office365/communications/virtualevents/settings.py +6 -0
  357. office365/communications/virtualevents/townhall.py +83 -0
  358. office365/communications/virtualevents/townhall_registration_configuration.py +7 -0
  359. office365/communications/virtualevents/virtual_event.py +78 -1
  360. office365/communications/virtualevents/webinar.py +65 -0
  361. office365/communications/virtualevents/webinar_registration_configuration.py +7 -0
  362. office365/copilot/action_result_part.py +4 -1
  363. office365/copilot/admin_catalog.py +28 -0
  364. office365/copilot/admin_limited_mode.py +21 -0
  365. office365/copilot/admin_setting.py +24 -0
  366. office365/copilot/ai_interaction_history.py +9 -0
  367. office365/copilot/ai_online_meeting.py +26 -0
  368. office365/copilot/ai_user.py +39 -0
  369. office365/copilot/copilot_package_request_status.py +14 -0
  370. office365/copilot/copilot_package_request_type.py +15 -0
  371. office365/copilot/package.py +153 -0
  372. office365/copilot/package_access_entity.py +3 -0
  373. office365/copilot/package_detail.py +92 -0
  374. office365/copilot/report_root.py +9 -0
  375. office365/copilot/retrieval_data_source.py +2 -0
  376. office365/delta_collection.py +37 -1
  377. office365/directory/administrative_unit.py +51 -1
  378. office365/directory/allowed_value.py +16 -0
  379. office365/directory/application_permission_type.py +13 -0
  380. office365/directory/application_resource_permission.py +18 -0
  381. office365/directory/applications/application.py +217 -14
  382. office365/directory/applications/applications_required_resource_access.py +21 -0
  383. office365/directory/applications/collection.py +40 -2
  384. office365/directory/applications/custom_security_attribute_exemption.py +2 -2
  385. office365/directory/applications/instance_resource_access.py +19 -0
  386. office365/directory/approval_settings.py +22 -0
  387. office365/directory/audit/provisioning/object_summary.py +19 -3
  388. office365/directory/audit/signins/activity.py +2 -2
  389. office365/directory/authentication/attributecollection/start_custom_extension_handler.py +8 -0
  390. office365/directory/authentication/attributecollection/submit_custom_extension_handler.py +8 -0
  391. office365/directory/authentication/authentication.py +124 -4
  392. office365/directory/authentication/behaviors.py +2 -0
  393. office365/directory/authentication/conditions/application.py +13 -0
  394. office365/directory/authentication/conditions/event_listener.py +32 -0
  395. office365/directory/authentication/error_correction_level.py +2 -0
  396. office365/directory/authentication/fraud_protection_provider.py +16 -0
  397. office365/directory/authentication/fraud_protection_provider_configuration.py +8 -0
  398. office365/directory/authentication/methods/fido.py +46 -1
  399. office365/directory/authentication/methods/method.py +12 -0
  400. office365/directory/authentication/methods/microsoft_authenticator.py +34 -0
  401. office365/directory/authentication/methods/phone.py +30 -0
  402. office365/directory/authentication/password_reset_response.py +4 -0
  403. office365/directory/authentication/platform_credential_authentication_method.py +51 -0
  404. office365/directory/authentication/qr_code_image_details.py +5 -0
  405. office365/directory/authentication/temporary_access_pass_authentication_method.py +42 -0
  406. office365/directory/authentication/windows_hello_for_business_authentication_method.py +39 -0
  407. office365/directory/b2b_registration_metrics.py +42 -0
  408. office365/directory/b2b_registration_metrics_initial.py +17 -0
  409. office365/directory/b2b_registration_metrics_recent.py +17 -0
  410. office365/directory/b2b_sign_in_activity_metrics.py +42 -0
  411. office365/directory/b2b_sign_in_activity_metrics_initial.py +17 -0
  412. office365/directory/b2b_sign_in_activity_metrics_recent.py +17 -0
  413. office365/directory/certificates/auth_pki.py +50 -0
  414. office365/directory/certificates/authority_detail.py +74 -0
  415. office365/directory/certificates/x509/authentication_method_configuration.py +98 -0
  416. office365/directory/certificates/x509/combination_configuration.py +20 -0
  417. office365/directory/claim_binding.py +17 -0
  418. office365/directory/claim_binding_source.py +12 -0
  419. office365/directory/claim_validation.py +15 -0
  420. office365/directory/credential.py +106 -0
  421. office365/directory/cross_cloud_azure_active_directory_tenant.py +16 -0
  422. office365/directory/custom_security_attribute_definition.py +65 -0
  423. office365/directory/datapolicy/operation.py +2 -1
  424. office365/directory/delegated_administration_role_assignment.py +24 -0
  425. office365/directory/delegated_administration_role_assignment_snapshot.py +20 -0
  426. office365/directory/delegatedadmin/access_assignment.py +60 -0
  427. office365/directory/delegatedadmin/access_container.py +18 -0
  428. office365/directory/delegatedadmin/access_details.py +16 -0
  429. office365/directory/delegatedadmin/customer.py +44 -0
  430. office365/directory/delegatedadmin/relationship.py +136 -0
  431. office365/directory/delegatedadmin/relationship_customer_participant.py +15 -0
  432. office365/directory/delegatedadmin/relationship_operation.py +56 -0
  433. office365/directory/delegatedadmin/relationship_request.py +48 -0
  434. office365/directory/delegatedadmin/service_management_detail.py +21 -0
  435. office365/directory/delegatedadmin/service_provider_constraints.py +28 -0
  436. office365/directory/device_local_credential_info.py +18 -1
  437. office365/directory/directory.py +86 -0
  438. office365/directory/domains/collection.py +57 -0
  439. office365/directory/domains/dns_cname_record.py +13 -0
  440. office365/directory/domains/dns_record.py +47 -0
  441. office365/directory/domains/domain.py +90 -0
  442. office365/directory/domains/internal_domain_federation.py +78 -0
  443. office365/directory/drifted_property.py +17 -0
  444. office365/directory/entity_type_and_ids.py +17 -0
  445. office365/directory/external_domain_federation.py +16 -0
  446. office365/directory/external_sponsors.py +10 -0
  447. office365/directory/external_token_based_sap_iag_connection_info.py +19 -0
  448. office365/directory/governance_invitation.py +42 -0
  449. office365/directory/governance_relationship.py +65 -0
  450. office365/directory/governance_request.py +76 -0
  451. office365/directory/groups/collection.py +19 -2
  452. office365/directory/groups/group.py +64 -11
  453. office365/directory/groups/setting.py +14 -1
  454. office365/directory/groups/setting_template.py +31 -0
  455. office365/directory/identities/api_connector.py +18 -0
  456. office365/directory/identities/authentication_events_flow.py +35 -0
  457. office365/directory/identities/b2x_identity_user_flow.py +89 -0
  458. office365/directory/identities/conditional_access_root.py +32 -3
  459. office365/directory/identities/conditional_access_template.py +47 -0
  460. office365/directory/identities/container.py +80 -8
  461. office365/directory/identities/named_location.py +29 -0
  462. office365/directory/identities/on_otp_send_custom_extension_handler.py +6 -0
  463. office365/directory/identities/on_password_migration_custom_extension_handler.py +8 -0
  464. office365/directory/identities/on_token_issuance_start_custom_extension.py +29 -0
  465. office365/directory/identities/on_token_issuance_start_custom_extension_handler.py +8 -0
  466. office365/directory/identities/on_verified_id_claim_validation_custom_extension.py +7 -0
  467. office365/directory/identities/on_verified_id_claim_validation_custom_extension_handler.py +8 -0
  468. office365/directory/identities/provider.py +16 -0
  469. office365/directory/identities/providers/saml_or_wsfed.py +32 -1
  470. office365/directory/identities/riskPrevention/container.py +70 -0
  471. office365/directory/identities/userflows/attribute.py +44 -0
  472. office365/directory/identities/userflows/b2x/user_flow.py +0 -10
  473. office365/directory/identities/userflows/language_configuration.py +11 -0
  474. office365/directory/identities/userflows/user_attribute_assignment.py +67 -0
  475. office365/directory/identities/userflows/user_flow.py +22 -1
  476. office365/directory/identities/verified_id_root.py +28 -0
  477. office365/directory/identities/web_application_firewall_provider.py +16 -0
  478. office365/directory/identities/web_application_firewall_verification_model.py +69 -0
  479. office365/directory/identity_set.py +19 -0
  480. office365/directory/identitygovernance/access_package_suggestion.py +43 -0
  481. office365/directory/identitygovernance/accessreview/context_data.py +10 -0
  482. office365/directory/identitygovernance/accessreview/context_data_base.py +15 -0
  483. office365/directory/identitygovernance/accessreview/custom_data_provided_resource_upload_session_request.py +4 -1
  484. office365/directory/identitygovernance/accessreview/history/definition.py +23 -3
  485. office365/directory/identitygovernance/accessreview/instance.py +49 -3
  486. office365/directory/identitygovernance/accessreview/instance_decision_item.py +49 -1
  487. office365/directory/identitygovernance/accessreview/instance_decision_item_apply_result.py +16 -0
  488. office365/directory/identitygovernance/accessreview/instance_decision_item_custom_data_provided_resource.py +16 -0
  489. office365/directory/identitygovernance/accessreview/instance_decision_item_permission.py +17 -0
  490. office365/directory/identitygovernance/accessreview/instance_decision_item_resource.py +2 -0
  491. office365/directory/identitygovernance/accessreview/principal_scope.py +5 -0
  492. office365/directory/identitygovernance/accessreview/principal_scope_type.py +15 -0
  493. office365/directory/identitygovernance/accessreview/resource_scope.py +5 -0
  494. office365/directory/identitygovernance/accessreview/resource_scope_type.py +16 -0
  495. office365/directory/identitygovernance/accessreview/reviewer_scope.py +5 -0
  496. office365/directory/identitygovernance/accessreview/reviewer_scope_type.py +18 -0
  497. office365/directory/identitygovernance/accessreview/schedule_definition.py +64 -3
  498. office365/directory/identitygovernance/accessreview/set.py +34 -1
  499. office365/directory/identitygovernance/accessreview/stage.py +31 -1
  500. office365/directory/identitygovernance/activities_container.py +30 -0
  501. office365/directory/identitygovernance/apply_decision_context_data.py +10 -0
  502. office365/directory/identitygovernance/awaited_workflow_processing_result.py +6 -1
  503. office365/directory/identitygovernance/content_activity.py +35 -0
  504. office365/directory/identitygovernance/custom_callout_extension_type.py +15 -0
  505. office365/directory/identitygovernance/custom_extension_resource_type.py +14 -0
  506. office365/directory/identitygovernance/custom_task_extension.py +18 -1
  507. office365/directory/identitygovernance/custom_task_extension_callout_data.py +13 -2
  508. office365/directory/identitygovernance/custom_task_extension_reply_mode.py +14 -0
  509. office365/directory/identitygovernance/custom_task_extension_response_data.py +1 -1
  510. office365/directory/identitygovernance/data.py +10 -0
  511. office365/directory/identitygovernance/data_security_and_governance.py +23 -0
  512. office365/directory/identitygovernance/deleted_item_container.py +17 -0
  513. office365/directory/identitygovernance/entitlementmanagement/accesspackage/assignment.py +37 -3
  514. office365/directory/identitygovernance/entitlementmanagement/accesspackage/assignment_policy.py +109 -3
  515. office365/directory/identitygovernance/entitlementmanagement/accesspackage/assignment_request.py +64 -4
  516. office365/directory/identitygovernance/entitlementmanagement/accesspackage/catalog.py +74 -2
  517. office365/directory/identitygovernance/entitlementmanagement/accesspackage/custom_data_provided_resource_file.py +27 -0
  518. office365/directory/identitygovernance/entitlementmanagement/accesspackage/custom_data_provided_resource_upload_session.py +80 -0
  519. office365/directory/identitygovernance/entitlementmanagement/accesspackage/multiple_choice_question.py +18 -1
  520. office365/directory/identitygovernance/entitlementmanagement/accesspackage/package.py +51 -1
  521. office365/directory/identitygovernance/entitlementmanagement/accesspackage/question.py +18 -1
  522. office365/directory/identitygovernance/entitlementmanagement/accesspackage/resource.py +52 -1
  523. office365/directory/identitygovernance/entitlementmanagement/accesspackage/resource_environment.py +11 -1
  524. office365/directory/identitygovernance/entitlementmanagement/accesspackage/resource_role.py +11 -1
  525. office365/directory/identitygovernance/entitlementmanagement/accesspackage/resource_role_scope.py +32 -0
  526. office365/directory/identitygovernance/entitlementmanagement/accesspackage/subject.py +31 -1
  527. office365/directory/identitygovernance/entitlementmanagement/available_access_package.py +42 -0
  528. office365/directory/identitygovernance/entitlementmanagement/connected_organization.py +16 -1
  529. office365/directory/identitygovernance/entitlementmanagement/control_configuration.py +37 -0
  530. office365/directory/identitygovernance/entitlementmanagement/entitlement_management.py +246 -1
  531. office365/directory/identitygovernance/entitlementmanagement/external_origin_resource_connector.py +58 -0
  532. office365/directory/identitygovernance/governance.py +39 -5
  533. office365/directory/identitygovernance/insights.py +3 -1
  534. office365/directory/identitygovernance/lifecycle_management_settings.py +15 -1
  535. office365/directory/identitygovernance/lifecycle_workflows_container.py +35 -2
  536. office365/directory/identitygovernance/lifecycletaskcategory.py +4 -0
  537. office365/directory/identitygovernance/lifecycleworkflows/category.py +4 -0
  538. office365/directory/identitygovernance/privilegedaccess/group.py +21 -0
  539. office365/directory/identitygovernance/privilegedaccess/group_resource.py +7 -0
  540. office365/directory/identitygovernance/privilegedaccess/role_management_custom_callout_extension.py +43 -0
  541. office365/directory/identitygovernance/privilegedaccess/root.py +27 -1
  542. office365/directory/identitygovernance/privilegedaccess/schedule/schedule.py +13 -1
  543. office365/directory/identitygovernance/privilegedaccess/schedule_request.py +22 -2
  544. office365/directory/identitygovernance/run.py +36 -5
  545. office365/directory/identitygovernance/subject_processing_result.py +121 -0
  546. office365/directory/identitygovernance/subject_type.py +13 -0
  547. office365/directory/identitygovernance/task_processing_result.py +18 -2
  548. office365/directory/identitygovernance/termsofuse/agreement.py +26 -4
  549. office365/directory/identitygovernance/termsofuse/agreement_file.py +4 -2
  550. office365/directory/identitygovernance/termsofuse/agreement_file_localization.py +4 -2
  551. office365/directory/identitygovernance/termsofuse/agreement_file_properties.py +15 -1
  552. office365/directory/identitygovernance/termsofuse/agreement_file_version.py +4 -2
  553. office365/directory/identitygovernance/user_data_security_and_governance.py +34 -0
  554. office365/directory/identitygovernance/user_protection_scope_container.py +9 -0
  555. office365/directory/identitygovernance/workflow/base.py +19 -3
  556. office365/directory/identitygovernance/workflow/executiontype.py +4 -0
  557. office365/directory/identitygovernance/workflow/setting.py +17 -0
  558. office365/directory/identitygovernance/workflow/version.py +28 -0
  559. office365/directory/identitygovernance/workflow/workflow.py +127 -0
  560. office365/directory/inheritable_permission.py +28 -0
  561. office365/directory/insights/identity.py +15 -0
  562. office365/directory/insights/resource_reference.py +4 -0
  563. office365/directory/insights/shared.py +27 -0
  564. office365/directory/insights/sharing_detail.py +4 -0
  565. office365/directory/insights/trending.py +22 -1
  566. office365/directory/insights/usage_details.py +4 -0
  567. office365/directory/insights/used.py +18 -0
  568. office365/directory/internal/sponsors.py +10 -0
  569. office365/directory/key_credential.py +9 -23
  570. office365/directory/licenses/collection.py +30 -0
  571. office365/directory/modified_property.py +16 -0
  572. office365/directory/multi_tenant_application_metrics.py +42 -0
  573. office365/directory/multi_tenant_application_metrics_base.py +27 -0
  574. office365/directory/multi_tenant_application_metrics_initial.py +17 -0
  575. office365/directory/multi_tenant_application_metrics_recent.py +17 -0
  576. office365/directory/multi_tenant_applications_to_provision.py +23 -0
  577. office365/directory/multi_tenant_applications_to_provision_snapshot.py +23 -0
  578. office365/directory/multi_tenant_organization_identity_sync_policy_template.py +37 -0
  579. office365/directory/multi_tenant_organization_join_request_record.py +54 -0
  580. office365/directory/multi_tenant_organization_member.py +76 -0
  581. office365/directory/multi_tenant_organization_partner_configuration_template.py +88 -0
  582. office365/directory/o_auth_consent_app_detail.py +17 -0
  583. office365/directory/objects/object.py +12 -0
  584. office365/directory/on_premises_directory_synchronization.py +37 -0
  585. office365/directory/password_credential.py +10 -23
  586. office365/directory/permissions/grants/condition_set.py +27 -1
  587. office365/directory/permissions/identity.py +4 -0
  588. office365/directory/permissions/identity_set.py +7 -25
  589. office365/directory/permissions/require_permission.py +78 -9
  590. office365/directory/policies/activity_based_timeout_policy.py +7 -0
  591. office365/directory/policies/admin_consent_request.py +50 -0
  592. office365/directory/policies/app_management.py +17 -1
  593. office365/directory/policies/authentication_flows.py +26 -1
  594. office365/directory/policies/authorization.py +12 -0
  595. office365/directory/policies/conditionalaccess/conditional_access.py +82 -0
  596. office365/directory/policies/crosstenant/access.py +43 -0
  597. office365/directory/policies/device_registration.py +69 -0
  598. office365/directory/policies/dlpaction.py +4 -0
  599. office365/directory/policies/feature_rollout.py +38 -0
  600. office365/directory/policies/federated_token_validation_policy.py +21 -0
  601. office365/directory/policies/ownerless_group_policy.py +59 -0
  602. office365/directory/policies/protected_application_metadata.py +1 -1
  603. office365/directory/policies/restrict_access_action_base.py +1 -1
  604. office365/directory/policies/root.py +142 -2
  605. office365/directory/policies/template.py +45 -0
  606. office365/directory/policies/tenant_app_management.py +39 -0
  607. office365/directory/policies/unifiedrolemanagement/policy.py +76 -1
  608. office365/directory/policies/unifiedrolemanagement/unified_role_assignment_schedule_request.py +145 -0
  609. office365/directory/policies/verified_id_profile_configuration.py +13 -1
  610. office365/directory/policies/verified_id_usage_configuration.py +5 -0
  611. office365/directory/profile_card_annotation.py +19 -0
  612. office365/directory/profile_source_localization.py +16 -0
  613. office365/directory/protection/bitlocker.py +32 -0
  614. office365/directory/protection/bitlocker_recovery_key.py +37 -0
  615. office365/directory/protection/information.py +18 -0
  616. office365/directory/protection/policy/base.py +82 -0
  617. office365/directory/protection/policy/one_drive_for_business.py +42 -0
  618. office365/directory/protection/riskyusers/risky_service_principal_history_item.py +15 -1
  619. office365/directory/protection/root.py +42 -0
  620. office365/directory/protection/rules/base.py +72 -0
  621. office365/directory/protection/rules/drive.py +13 -0
  622. office365/directory/protection/threatassessment/requests/email_file.py +26 -1
  623. office365/directory/protection/threatassessment/requests/file.py +11 -0
  624. office365/directory/protection/threatassessment/requests/mail.py +32 -0
  625. office365/directory/protection/threatassessment/requests/request.py +52 -1
  626. office365/directory/protection/threatassessment/requests/threat_assessment_result.py +36 -0
  627. office365/directory/protection/threatassessment/requests/url.py +6 -0
  628. office365/directory/recovery/change_object_base.py +31 -0
  629. office365/directory/recovery/job.py +26 -0
  630. office365/directory/recovery/job_base.py +59 -0
  631. office365/directory/recovery/job_entity_name_and_ids_filter.py +18 -0
  632. office365/directory/recovery/job_entity_names_filter.py +18 -0
  633. office365/directory/recovery/job_filtering_criteria_base.py +10 -0
  634. office365/directory/recovery/preview_job.py +9 -0
  635. office365/directory/recovery/recovery.py +37 -0
  636. office365/directory/related_tenant.py +88 -0
  637. office365/directory/related_tenants_refresh_status.py +17 -0
  638. office365/directory/relationship_creation_type.py +13 -0
  639. office365/directory/relationship_policy.py +29 -0
  640. office365/directory/relationship_status.py +14 -0
  641. office365/directory/remote_tenant_group.py +37 -0
  642. office365/directory/request_schedule.py +21 -0
  643. office365/directory/request_status.py +14 -0
  644. office365/directory/resource_visualization.py +21 -0
  645. office365/directory/role_template.py +15 -0
  646. office365/directory/role_type.py +15 -0
  647. office365/directory/rolemanagement/app_scope.py +18 -0
  648. office365/directory/rolemanagement/application.py +120 -0
  649. office365/directory/rolemanagement/resource_action.py +4 -0
  650. office365/directory/rolemanagement/roles/collection.py +26 -3
  651. office365/directory/rolemanagement/roles/permission.py +4 -0
  652. office365/directory/rolemanagement/unifiedrole/assignment.py +33 -1
  653. office365/directory/rolemanagement/unifiedrole/definition.py +30 -0
  654. office365/directory/service_provider_constraints.py +7 -0
  655. office365/directory/service_storage_quota_breakdown.py +7 -0
  656. office365/directory/serviceprincipals/approved_client_app.py +16 -0
  657. office365/directory/serviceprincipals/collection.py +48 -4
  658. office365/directory/serviceprincipals/identity.py +14 -0
  659. office365/directory/serviceprincipals/remote_desktop_security_configuration.py +39 -1
  660. office365/directory/serviceprincipals/service_principal.py +330 -19
  661. office365/directory/serviceprincipals/target_device_group.py +16 -0
  662. office365/directory/snapshot.py +50 -0
  663. office365/directory/status.py +15 -0
  664. office365/directory/subscriptions/company.py +75 -0
  665. office365/directory/synchronization/attribute_mapping.py +1 -1
  666. office365/directory/synchronization/attribute_mapping_function_schema.py +1 -1
  667. office365/directory/synchronization/directory_definition.py +1 -1
  668. office365/directory/synchronization/job.py +13 -1
  669. office365/directory/synchronization/job_subject.py +6 -3
  670. office365/directory/synchronization/linked_objects.py +8 -9
  671. office365/directory/synchronization/rule.py +4 -4
  672. office365/directory/synchronization/schema.py +19 -2
  673. office365/directory/synchronization/status.py +1 -1
  674. office365/directory/synchronization/synchronization.py +22 -0
  675. office365/directory/synchronization/template.py +20 -1
  676. office365/directory/tenant_governance.py +98 -0
  677. office365/directory/tenant_governance_policy_template.py +75 -0
  678. office365/directory/tenant_governance_setting.py +21 -0
  679. office365/directory/tenantinformation/multi_organization.py +72 -0
  680. office365/directory/tenantinformation/relationship.py +41 -0
  681. office365/directory/ticket_info.py +15 -0
  682. office365/directory/unified_approval_stage.py +25 -0
  683. office365/directory/unified_rbac_resource_action.py +41 -0
  684. office365/directory/unified_rbac_resource_namespace.py +37 -0
  685. office365/directory/unified_role.py +14 -0
  686. office365/directory/unified_role_assignment_schedule.py +47 -0
  687. office365/directory/unified_role_assignment_schedule_instance.py +60 -0
  688. office365/directory/unified_role_eligibility_schedule.py +26 -0
  689. office365/directory/unified_role_eligibility_schedule_instance.py +32 -0
  690. office365/directory/unified_role_eligibility_schedule_request.py +133 -0
  691. office365/directory/unified_role_management_policy_approval_rule.py +21 -0
  692. office365/directory/unified_role_management_policy_assignment.py +48 -0
  693. office365/directory/unified_role_management_policy_authentication_context_rule.py +21 -0
  694. office365/directory/unified_role_management_policy_custom_extension_rule.py +32 -0
  695. office365/directory/unified_role_management_policy_enablement_rule.py +15 -0
  696. office365/directory/unified_role_management_policy_expiration_rule.py +24 -0
  697. office365/directory/unified_role_management_policy_notification_rule.py +37 -0
  698. office365/directory/unified_role_management_policy_rule.py +25 -0
  699. office365/directory/unified_role_management_policy_rule_target.py +31 -0
  700. office365/directory/unified_role_schedule_base.py +96 -0
  701. office365/directory/unified_role_schedule_instance_base.py +75 -0
  702. office365/directory/unified_root.py +66 -0
  703. office365/directory/unified_storage_quota.py +62 -0
  704. office365/directory/users/activities/activity.py +24 -2
  705. office365/directory/users/activities/history_item.py +42 -1
  706. office365/directory/users/collection.py +17 -0
  707. office365/directory/users/identity.py +4 -0
  708. office365/directory/users/inference_classification.py +23 -0
  709. office365/directory/users/inference_classification_override.py +33 -0
  710. office365/directory/users/item_insights.py +7 -0
  711. office365/directory/users/note.py +101 -0
  712. office365/directory/users/provisioned_plan.py +16 -0
  713. office365/directory/users/settings.py +50 -1
  714. office365/directory/users/storage.py +15 -0
  715. office365/directory/users/user.py +146 -16
  716. office365/directory/users/windows_setting.py +50 -0
  717. office365/directory/users/windows_setting_instance.py +32 -0
  718. office365/directory/users/work_hours_and_locations_setting.py +53 -0
  719. office365/directory/users/work_plan_occurrence.py +56 -0
  720. office365/directory/users/work_plan_recurrence.py +51 -0
  721. office365/directory/verified_id_profile.py +91 -0
  722. office365/directory/visual_info.py +20 -0
  723. office365/education/challenging_word.py +15 -0
  724. office365/education/rubric_criterion.py +10 -0
  725. office365/education/rubric_level.py +15 -0
  726. office365/education/rubric_quality.py +21 -0
  727. office365/education/rubric_quality_feedback_model.py +14 -0
  728. office365/education/rubric_quality_selected_column_model.py +15 -0
  729. office365/entity.py +72 -3
  730. office365/entity_collection.py +33 -2
  731. office365/externalconnectors/acl.py +21 -0
  732. office365/externalconnectors/activity_settings.py +18 -0
  733. office365/externalconnectors/connection_operation.py +17 -2
  734. office365/externalconnectors/connector_type.py +12 -0
  735. office365/externalconnectors/content_category.py +18 -4
  736. office365/externalconnectors/display_template.py +20 -0
  737. office365/externalconnectors/external_activity.py +14 -1
  738. office365/externalconnectors/external_activity_result.py +12 -0
  739. office365/externalconnectors/external_group.py +15 -1
  740. office365/externalconnectors/external_item_content.py +19 -0
  741. office365/externalconnectors/external_item_information_protection_label.py +14 -0
  742. office365/externalconnectors/identity.py +19 -0
  743. office365/externalconnectors/item_id_resolver.py +16 -0
  744. office365/externalconnectors/principal.py +20 -0
  745. office365/externalconnectors/properties.py +10 -0
  746. office365/externalconnectors/property.py +28 -0
  747. office365/externalconnectors/property_rule.py +22 -0
  748. office365/externalconnectors/url_match_info.py +16 -0
  749. office365/externalconnectors/url_to_item_resolver_base.py +14 -0
  750. office365/graph_client.py +138 -33
  751. office365/graph_request.py +56 -4
  752. office365/intune/audit/actor.py +26 -0
  753. office365/intune/audit/event.py +48 -0
  754. office365/intune/audit/property.py +15 -0
  755. office365/intune/audit/resource.py +15 -1
  756. office365/intune/devices/and_app_management_role_assignment.py +15 -0
  757. office365/intune/devices/and_app_management_role_definition.py +7 -0
  758. office365/intune/devices/category.py +18 -0
  759. office365/intune/devices/compliance/android_compliance_policy.py +134 -0
  760. office365/intune/devices/compliance/android_work_profile_compliance_policy.py +134 -0
  761. office365/intune/devices/compliance/compliance.py +7 -0
  762. office365/intune/devices/compliance/information.py +19 -0
  763. office365/intune/devices/compliance/ios_compliance_policy.py +99 -0
  764. office365/intune/devices/compliance/mac_os_compliance_policy.py +114 -0
  765. office365/intune/devices/compliance/management_partner.py +91 -0
  766. office365/intune/devices/compliance/management_partner_assignment.py +17 -0
  767. office365/intune/devices/compliance/policy.py +155 -0
  768. office365/intune/devices/compliance/policy_state.py +61 -0
  769. office365/intune/devices/compliance/setting_state_device_summary.py +56 -0
  770. office365/intune/devices/compliance/windows10_compliance_policy.py +115 -0
  771. office365/intune/devices/compliance/windows10_mobile_compliance_policy.py +100 -0
  772. office365/intune/devices/compliance/windows81_compliance_policy.py +75 -0
  773. office365/intune/devices/compliance/windows_phone81_compliance_policy.py +75 -0
  774. office365/intune/devices/compliance_action_item.py +41 -0
  775. office365/intune/devices/compliance_device_overview.py +47 -0
  776. office365/intune/devices/compliance_device_status.py +52 -0
  777. office365/intune/devices/compliance_policy_assignment.py +25 -0
  778. office365/intune/devices/compliance_policy_device_state_summary.py +56 -0
  779. office365/intune/devices/compliance_policy_setting_state_summary.py +91 -0
  780. office365/intune/devices/compliance_scheduled_action_for_rule.py +39 -0
  781. office365/intune/devices/compliance_setting_state.py +72 -0
  782. office365/intune/devices/compliance_user_overview.py +47 -0
  783. office365/intune/devices/compliance_user_status.py +42 -0
  784. office365/intune/devices/configuration_assignment.py +25 -0
  785. office365/intune/devices/configuration_device_overview.py +47 -0
  786. office365/intune/devices/configuration_device_state_summary.py +46 -0
  787. office365/intune/devices/configuration_device_status.py +52 -0
  788. office365/intune/devices/configuration_state.py +63 -0
  789. office365/intune/devices/configuration_user_overview.py +47 -0
  790. office365/intune/devices/configuration_user_status.py +42 -0
  791. office365/intune/devices/configurations/android/android_lob_app.py +15 -1
  792. office365/intune/devices/configurations/android/android_store_app.py +15 -1
  793. office365/intune/devices/configurations/android/custom_configuration.py +22 -0
  794. office365/intune/devices/configurations/android/general_device_configuration.py +282 -0
  795. office365/intune/devices/configurations/android/minimum_operating_system.py +29 -0
  796. office365/intune/devices/configurations/android/mobile_app_identifier.py +14 -0
  797. office365/intune/devices/configurations/android/work_profile_custom_configuration.py +22 -0
  798. office365/intune/devices/configurations/android/work_profile_general_device_configuration.py +214 -0
  799. office365/intune/devices/configurations/configuration.py +136 -0
  800. office365/intune/devices/configurations/edge_search_engine.py +17 -0
  801. office365/intune/devices/configurations/edge_search_engine_base.py +10 -0
  802. office365/intune/devices/configurations/edge_search_engine_custom.py +14 -0
  803. office365/intune/devices/configurations/ios/certificate_profile.py +7 -0
  804. office365/intune/devices/configurations/ios/custom_configuration.py +26 -0
  805. office365/intune/devices/configurations/ios/features_configuration.py +62 -0
  806. office365/intune/devices/configurations/ios/general/device_configuration.py +761 -0
  807. office365/intune/devices/configurations/ios/home_screen_app.py +14 -0
  808. office365/intune/devices/configurations/ios/home_screen_folder.py +20 -0
  809. office365/intune/devices/configurations/ios/home_screen_folder_page.py +19 -0
  810. office365/intune/devices/configurations/ios/home_screen_item.py +14 -0
  811. office365/intune/devices/configurations/ios/home_screen_page.py +21 -0
  812. office365/intune/devices/configurations/ios/minimum_operating_system.py +21 -0
  813. office365/intune/devices/configurations/ios/network_usage_rule.py +18 -0
  814. office365/intune/devices/configurations/ios/notification_settings.py +25 -0
  815. office365/intune/devices/configurations/ios/update_configuration.py +38 -0
  816. office365/intune/devices/configurations/macos/mac_os_custom_configuration.py +26 -0
  817. office365/intune/devices/configurations/macos/mac_os_device_features_configuration.py +7 -0
  818. office365/intune/devices/configurations/macos/mac_os_general_device_configuration.py +88 -0
  819. office365/intune/devices/configurations/macos/mac_os_minimum_operating_system.py +25 -0
  820. office365/intune/devices/configurations/media_content_rating_australia.py +21 -0
  821. office365/intune/devices/configurations/media_content_rating_canada.py +21 -0
  822. office365/intune/devices/configurations/media_content_rating_france.py +21 -0
  823. office365/intune/devices/configurations/media_content_rating_germany.py +21 -0
  824. office365/intune/devices/configurations/media_content_rating_ireland.py +21 -0
  825. office365/intune/devices/configurations/media_content_rating_japan.py +21 -0
  826. office365/intune/devices/configurations/media_content_rating_new_zealand.py +21 -0
  827. office365/intune/devices/configurations/media_content_rating_united_kingdom.py +21 -0
  828. office365/intune/devices/configurations/media_content_rating_united_states.py +21 -0
  829. office365/intune/devices/configurations/oma_setting.py +16 -0
  830. office365/intune/devices/configurations/oma_setting_base64.py +15 -0
  831. office365/intune/devices/configurations/oma_setting_boolean.py +14 -0
  832. office365/intune/devices/configurations/oma_setting_date_time.py +15 -0
  833. office365/intune/devices/configurations/oma_setting_floating_point.py +14 -0
  834. office365/intune/devices/configurations/oma_setting_integer.py +14 -0
  835. office365/intune/devices/configurations/oma_setting_string.py +14 -0
  836. office365/intune/devices/configurations/oma_setting_string_xml.py +15 -0
  837. office365/intune/devices/configurations/rgb_color.py +16 -0
  838. office365/intune/devices/configurations/windows/bit_locker_removable_drive_policy.py +19 -0
  839. office365/intune/devices/configurations/windows/defender_advanced_threat_protection_configuration.py +21 -0
  840. office365/intune/devices/configurations/windows/firewall_network_profile.py +28 -0
  841. office365/intune/devices/configurations/windows/minimum_operating_system.py +16 -0
  842. office365/intune/devices/configurations/windows/out_of_box_experience_setting.py +25 -0
  843. office365/intune/devices/configurations/windows/phone81_custom_configuration.py +22 -0
  844. office365/intune/devices/configurations/windows/phone81_general_configuration.py +172 -0
  845. office365/intune/devices/configurations/windows/shared_pc_account_manager_policy.py +20 -0
  846. office365/intune/devices/configurations/windows/update_active_hours_install.py +16 -0
  847. office365/intune/devices/configurations/windows/update_for_business_configuration.py +253 -0
  848. office365/intune/devices/configurations/windows/update_install_schedule_type.py +10 -0
  849. office365/intune/devices/configurations/windows/update_scheduled_install.py +19 -0
  850. office365/intune/devices/configurations/windows/windows10_custom_configuration.py +22 -0
  851. office365/intune/devices/configurations/windows/windows10_endpoint_protection_configuration.py +255 -0
  852. office365/intune/devices/configurations/windows/windows10_enrollment_completion_page_configuration.py +16 -0
  853. office365/intune/devices/configurations/windows/windows10_enterprise_modern_app_management_configuration.py +16 -0
  854. office365/intune/devices/configurations/windows/windows10_general_configuration.py +1177 -0
  855. office365/intune/devices/configurations/windows/windows10_network_proxy_server.py +17 -0
  856. office365/intune/devices/configurations/windows/windows10_secure_assessment_configuration.py +36 -0
  857. office365/intune/devices/configurations/windows/windows10_team_general_configuration.py +128 -0
  858. office365/intune/devices/configurations/windows/windows81_general_configuration.py +210 -0
  859. office365/intune/devices/data.py +11 -0
  860. office365/intune/devices/device.py +138 -1
  861. office365/intune/devices/enrollment/configuration.py +48 -1
  862. office365/intune/devices/enrollment/configuration_assignment.py +22 -0
  863. office365/intune/devices/enrollment/limit_configuration.py +13 -0
  864. office365/intune/devices/enrollment_platform_restrictions_configuration.py +61 -0
  865. office365/intune/devices/enrollment_windows_hello_for_business_configuration.py +93 -0
  866. office365/intune/devices/install_state.py +61 -0
  867. office365/intune/devices/log_collection_response.py +60 -0
  868. office365/intune/devices/management/action_result.py +19 -0
  869. office365/intune/devices/management/android_managed_app_registration.py +7 -0
  870. office365/intune/devices/management/app_management.py +157 -3
  871. office365/intune/devices/management/apple_push_notification_certificate.py +52 -0
  872. office365/intune/devices/management/compliance_policy_setting_state.py +28 -0
  873. office365/intune/devices/management/configuration_manager_client_enabled_features.py +19 -0
  874. office365/intune/devices/management/configuration_manager_collection_assignment_target.py +14 -0
  875. office365/intune/devices/management/configuration_setting_state.py +28 -0
  876. office365/intune/devices/management/defender_detected_malware_actions.py +18 -0
  877. office365/intune/devices/management/delete_user_from_shared_apple_device_action_result.py +14 -0
  878. office365/intune/devices/management/detected_app.py +65 -0
  879. office365/intune/devices/management/enrollment_platform_restriction.py +17 -0
  880. office365/intune/devices/management/exchange_access_state_summary.py +18 -0
  881. office365/intune/devices/management/geo_location.py +22 -0
  882. office365/intune/devices/management/group_peer_outlier_recommendation_insight_settings.py +10 -0
  883. office365/intune/devices/management/health_attestation_state.py +46 -0
  884. office365/intune/devices/management/imported_windows_autopilot_device_identity.py +55 -0
  885. office365/intune/devices/management/imported_windows_autopilot_device_identity_state.py +22 -0
  886. office365/intune/devices/management/insight_value_double.py +14 -0
  887. office365/intune/devices/management/insight_value_int.py +14 -0
  888. office365/intune/devices/management/integer_range.py +15 -0
  889. office365/intune/devices/management/ios_managed_app_registration.py +7 -0
  890. office365/intune/devices/management/ios_update_device_status.py +79 -0
  891. office365/intune/devices/management/localized_notification_message.py +37 -0
  892. office365/intune/devices/management/locate_device_action_result.py +15 -0
  893. office365/intune/devices/management/m365_capability_base.py +32 -0
  894. office365/intune/devices/management/m365_capability_inbound_access.py +18 -0
  895. office365/intune/devices/management/m365_capability_resource_scope.py +16 -0
  896. office365/intune/devices/management/m365_capability_resource_scopes.py +23 -0
  897. office365/intune/devices/management/m365_resource_type.py +14 -0
  898. office365/intune/devices/management/managed/app/diagnostic_status.py +15 -0
  899. office365/intune/devices/management/managed/app/operation.py +32 -0
  900. office365/intune/devices/management/managed/app/protections/android.py +31 -1
  901. office365/intune/devices/management/managed/app/protections/default.py +34 -2
  902. office365/intune/devices/management/managed/app/protections/ios.py +34 -2
  903. office365/intune/devices/management/managed/app/protections/targeted.py +22 -1
  904. office365/intune/devices/management/managed/app/protections/targeted_managed_app_policy_assignment.py +25 -0
  905. office365/intune/devices/management/managed/app/protections/windows.py +230 -1
  906. office365/intune/devices/management/managed/app/registration.py +29 -2
  907. office365/intune/devices/management/managed/app_policy_deployment_summary.py +50 -0
  908. office365/intune/devices/management/managed/collection.py +26 -0
  909. office365/intune/devices/management/managed/device_mobile_app_configuration.py +138 -0
  910. office365/intune/devices/management/managed/device_mobile_app_configuration_assignment.py +25 -0
  911. office365/intune/devices/management/managed/device_mobile_app_configuration_device_status.py +52 -0
  912. office365/intune/devices/management/managed/device_mobile_app_configuration_device_summary.py +47 -0
  913. office365/intune/devices/management/managed/device_mobile_app_configuration_user_status.py +42 -0
  914. office365/intune/devices/management/managed/device_mobile_app_configuration_user_summary.py +47 -0
  915. office365/intune/devices/management/managed/device_overview.py +52 -0
  916. office365/intune/devices/management/managed/ebook/e_book_assignment.py +33 -0
  917. office365/intune/devices/management/managed/ebook/ebook.py +72 -1
  918. office365/intune/devices/management/managed/ebook/install_summary.py +41 -0
  919. office365/intune/devices/management/managed/ebook/user_install_state_summary.py +48 -0
  920. office365/intune/devices/management/managed/managed.py +87 -3
  921. office365/intune/devices/management/managed/mobile_app.py +26 -0
  922. office365/intune/devices/management/management.py +995 -6
  923. office365/intune/devices/management/mdm_windows_information_protection_policy.py +7 -0
  924. office365/intune/devices/management/mobile_threat_defense_connector.py +96 -0
  925. office365/intune/devices/management/mobileapps/app_configuration_setting_item.py +17 -0
  926. office365/intune/devices/management/mobileapps/app_list_item.py +17 -0
  927. office365/intune/devices/management/mobileapps/app_log_collection_download_details.py +19 -0
  928. office365/intune/devices/management/mobileapps/app_log_collection_request.py +40 -0
  929. office365/intune/devices/management/mobileapps/apps_installation_options_for_mac.py +15 -0
  930. office365/intune/devices/management/mobileapps/apps_installation_options_for_windows.py +17 -0
  931. office365/intune/devices/management/mobileapps/assignment.py +28 -0
  932. office365/intune/devices/management/mobileapps/content.py +24 -1
  933. office365/intune/devices/management/mobileapps/ios/ios_mobile_app_configuration.py +18 -1
  934. office365/intune/devices/management/mobileapps/ios/iosi_pad_os_web_clip.py +21 -0
  935. office365/intune/devices/management/mobileapps/ios/lob_app.py +58 -0
  936. office365/intune/devices/management/mobileapps/ios/lob_app_assignment_settings.py +16 -0
  937. office365/intune/devices/management/mobileapps/ios/lob_app_provisioning_configuration_assignment.py +25 -0
  938. office365/intune/devices/management/mobileapps/ios/mobile_app_identifier.py +14 -0
  939. office365/intune/devices/management/mobileapps/ios/store_app.py +47 -0
  940. office365/intune/devices/management/mobileapps/ios/store_app_assignment_settings.py +16 -0
  941. office365/intune/devices/management/mobileapps/ios/vpp_app.py +81 -0
  942. office365/intune/devices/management/mobileapps/ios/vpp_app_assignment_settings.py +15 -0
  943. office365/intune/devices/management/mobileapps/ios/vpp_e_book.py +53 -0
  944. office365/intune/devices/management/mobileapps/ios/vpp_e_book_assignment.py +7 -0
  945. office365/intune/devices/management/mobileapps/macos/mac_os_dmg_app.py +49 -0
  946. office365/intune/devices/management/mobileapps/macos/mac_os_included_app.py +15 -0
  947. office365/intune/devices/management/mobileapps/macos/mac_os_lob_app.py +70 -0
  948. office365/intune/devices/management/mobileapps/macos/mac_os_lob_app_assignment_settings.py +14 -0
  949. office365/intune/devices/management/mobileapps/macos/mac_os_lob_child_app.py +16 -0
  950. office365/intune/devices/management/mobileapps/macos/mac_os_microsoft_defender_app.py +7 -0
  951. office365/intune/devices/management/mobileapps/macos/mac_os_microsoft_edge_app.py +25 -0
  952. office365/intune/devices/management/mobileapps/macos/mac_os_office_suite_app.py +7 -0
  953. office365/intune/devices/management/mobileapps/managed/app_policy_deployment_summary_per_app.py +18 -0
  954. office365/intune/devices/management/mobileapps/microsoft_store_for_business_app_assignment_settings.py +14 -0
  955. office365/intune/devices/management/mobileapps/mobile_app.py +31 -2
  956. office365/intune/devices/management/mobileapps/mobile_app_assignment_settings.py +10 -0
  957. office365/intune/devices/management/mobileapps/mobile_app_identifier.py +10 -0
  958. office365/intune/devices/management/mobileapps/mobile_app_install_time_settings.py +17 -0
  959. office365/intune/devices/management/mobileapps/mobile_app_troubleshooting_event.py +28 -0
  960. office365/intune/devices/management/mobileapps/mobile_lob_app.py +20 -1
  961. office365/intune/devices/management/mobileapps/vpp_licensing_type.py +15 -0
  962. office365/intune/devices/management/mobileapps/win32/lob_app.py +115 -0
  963. office365/intune/devices/management/mobileapps/win32/lob_app_assignment_settings.py +35 -0
  964. office365/intune/devices/management/mobileapps/win32/lob_app_auto_update_settings.py +19 -0
  965. office365/intune/devices/management/mobileapps/win32/lob_app_file_system_rule.py +25 -0
  966. office365/intune/devices/management/mobileapps/win32/lob_app_install_experience.py +21 -0
  967. office365/intune/devices/management/mobileapps/win32/lob_app_msi_information.py +23 -0
  968. office365/intune/devices/management/mobileapps/win32/lob_app_power_shell_script_rule.py +32 -0
  969. office365/intune/devices/management/mobileapps/win32/lob_app_product_code_rule.py +19 -0
  970. office365/intune/devices/management/mobileapps/win32/lob_app_registry_rule.py +25 -0
  971. office365/intune/devices/management/mobileapps/win32/lob_app_restart_settings.py +16 -0
  972. office365/intune/devices/management/mobileapps/win32/lob_app_return_code.py +18 -0
  973. office365/intune/devices/management/mobileapps/win32/lob_app_rule.py +17 -0
  974. office365/intune/devices/management/mobileapps/windows/app_x.py +62 -0
  975. office365/intune/devices/management/mobileapps/windows/app_x_app_assignment_settings.py +14 -0
  976. office365/intune/devices/management/mobileapps/windows/microsoft_edge_app.py +30 -0
  977. office365/intune/devices/management/mobileapps/windows/mobile_msi.py +31 -0
  978. office365/intune/devices/management/mobileapps/windows/universal_app_x.py +93 -0
  979. office365/intune/devices/management/mobileapps/windows/universal_app_x_app_assignment_settings.py +14 -0
  980. office365/intune/devices/management/mobileapps/windows/universal_app_x_contained_app.py +16 -0
  981. office365/intune/devices/management/mobileapps/windows/web_app.py +16 -0
  982. office365/intune/devices/management/notification_message_template.py +68 -0
  983. office365/intune/devices/management/obliteration_behavior.py +15 -0
  984. office365/intune/devices/management/on_premises_conditional_access_settings.py +32 -0
  985. office365/intune/devices/management/operating_system_summary.py +25 -0
  986. office365/intune/devices/management/partner_assignment.py +17 -0
  987. office365/intune/devices/management/remote_lock_action_result.py +14 -0
  988. office365/intune/devices/management/remoteassistance/partner.py +41 -0
  989. office365/intune/devices/management/reports/reports.py +27 -0
  990. office365/intune/devices/management/reset_passcode_action_result.py +15 -0
  991. office365/intune/devices/management/resource_operation.py +26 -0
  992. office365/intune/devices/management/rotate_bit_locker_keys_device_action_result.py +14 -0
  993. office365/intune/devices/management/software_update_status_summary.py +86 -0
  994. office365/intune/devices/management/system_browser_enabled_on.py +16 -0
  995. office365/intune/devices/management/targeted_managed_app_configuration.py +68 -0
  996. office365/intune/devices/management/terms_and_conditions.py +87 -0
  997. office365/intune/devices/management/terms_and_conditions_acceptance_status.py +50 -0
  998. office365/intune/devices/management/terms_and_conditions_assignment.py +25 -0
  999. office365/intune/devices/management/update_windows_device_account_action_parameter.py +22 -0
  1000. office365/intune/devices/management/user_last_sign_in_recommendation_insight_setting.py +22 -0
  1001. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_app_performance_by_app_version_details.py +51 -0
  1002. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_app_performance_by_app_version_device_id.py +52 -0
  1003. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_app_performance_by_os_version.py +56 -0
  1004. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_application_performance.py +56 -0
  1005. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_device_model_performance.py +50 -0
  1006. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_device_performance.py +76 -0
  1007. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_device_performance_details.py +47 -0
  1008. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_app_health_os_version_performance.py +36 -0
  1009. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_autopilot_devices_summary.py +16 -0
  1010. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_baseline.py +121 -0
  1011. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_category.py +47 -0
  1012. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_cloud_identity_devices_summary.py +14 -0
  1013. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_cloud_management_devices_summary.py +16 -0
  1014. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_device_performance.py +127 -0
  1015. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_device_scores.py +65 -0
  1016. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_device_startup_history.py +101 -0
  1017. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_device_startup_process.py +36 -0
  1018. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_device_startup_process_performance.py +41 -0
  1019. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_insight.py +26 -0
  1020. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_insight_value.py +10 -0
  1021. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_metric.py +21 -0
  1022. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_metric_history.py +27 -0
  1023. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_model_scores.py +65 -0
  1024. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_overview.py +28 -0
  1025. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_score_history.py +17 -0
  1026. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_windows10_devices_summary.py +14 -0
  1027. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_work_from_anywhere_device.py +197 -0
  1028. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_work_from_anywhere_devices_summary.py +47 -0
  1029. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_work_from_anywhere_hardware_readiness_metric.py +66 -0
  1030. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_work_from_anywhere_metric.py +34 -0
  1031. office365/intune/devices/management/userexperienceanalytics/user_experience_analytics_work_from_anywhere_model_performance.py +65 -0
  1032. office365/intune/devices/management/virtualendpoint/virtual_endpoint.py +137 -0
  1033. office365/intune/devices/management/windows_autopilot_deployment_profile.py +106 -0
  1034. office365/intune/devices/management/windows_autopilot_deployment_profile_assignment.py +7 -0
  1035. office365/intune/devices/management/windows_autopilot_device_identity.py +97 -0
  1036. office365/intune/devices/management/windows_defender_scan_action_result.py +14 -0
  1037. office365/intune/devices/management/windows_device_account.py +14 -0
  1038. office365/intune/devices/management/windows_device_ad_account.py +15 -0
  1039. office365/intune/devices/management/windows_device_azure_ad_account.py +14 -0
  1040. office365/intune/devices/management/windows_information_protection_app.py +18 -0
  1041. office365/intune/devices/management/windows_information_protection_app_learning_summary.py +35 -0
  1042. office365/intune/devices/management/windows_information_protection_app_locker_file.py +31 -0
  1043. office365/intune/devices/management/windows_information_protection_data_recovery_certificate.py +18 -0
  1044. office365/intune/devices/management/windows_information_protection_desktop_app.py +16 -0
  1045. office365/intune/devices/management/windows_information_protection_ip_range_collection.py +17 -0
  1046. office365/intune/devices/management/windows_information_protection_network_learning_summary.py +21 -0
  1047. office365/intune/devices/management/windows_information_protection_pin_character_requirements.py +13 -0
  1048. office365/intune/devices/management/windows_information_protection_policy.py +76 -0
  1049. office365/intune/devices/management/windows_information_protection_proxied_domain_collection.py +19 -0
  1050. office365/intune/devices/management/windows_information_protection_resource_collection.py +16 -0
  1051. office365/intune/devices/management/windows_information_protection_store_app.py +10 -0
  1052. office365/intune/devices/management/windows_malware_overview.py +34 -1
  1053. office365/intune/devices/management/windows_protection_state.py +152 -0
  1054. office365/intune/devices/management/windowsmalware/malware_state_for_windows_device.py +58 -0
  1055. office365/intune/devices/management/windowsmalware/windows_device_malware_state.py +99 -0
  1056. office365/intune/devices/management/windowsmalware/windows_malware_category_count.py +22 -0
  1057. office365/intune/devices/management/windowsmalware/windows_malware_execution_state_count.py +20 -0
  1058. office365/intune/devices/management/windowsmalware/windows_malware_information.py +72 -0
  1059. office365/intune/devices/management/windowsmalware/windows_malware_name_count.py +18 -0
  1060. office365/intune/devices/management/windowsmalware/windows_malware_severity_count.py +21 -0
  1061. office365/intune/devices/management/windowsmalware/windows_malware_state_count.py +22 -0
  1062. office365/intune/devices/management_cached_report_configuration.py +7 -0
  1063. office365/intune/devices/management_exchange_connector.py +73 -0
  1064. office365/intune/devices/management_export_job.py +82 -0
  1065. office365/intune/devices/management_partner.py +84 -0
  1066. office365/intune/devices/management_troubleshooting_event.py +22 -0
  1067. office365/intune/organizations/branding.py +29 -0
  1068. office365/intune/organizations/branding_properties.py +187 -0
  1069. office365/intune/organizations/contact.py +132 -0
  1070. office365/intune/organizations/content_customization.py +9 -1
  1071. office365/intune/organizations/organization.py +125 -1
  1072. office365/intune/organizations/organizational_branding_localization.py +7 -0
  1073. office365/intune/print/air_print_settings.py +17 -0
  1074. office365/intune/print/archived_print_job.py +26 -0
  1075. office365/intune/print/certificate_signing_request.py +15 -0
  1076. office365/intune/print/connectors/connector.py +44 -0
  1077. office365/intune/print/document.py +37 -0
  1078. office365/intune/print/document_upload_properties.py +16 -0
  1079. office365/intune/print/job.py +91 -0
  1080. office365/intune/print/job_configuration.py +46 -0
  1081. office365/intune/print/job_status.py +22 -0
  1082. office365/intune/print/margin.py +17 -0
  1083. office365/intune/print/operation.py +28 -0
  1084. office365/intune/print/operation_status.py +18 -0
  1085. office365/intune/print/print.py +65 -0
  1086. office365/intune/print/printers/base.py +66 -0
  1087. office365/intune/print/printers/capabilities.py +82 -0
  1088. office365/intune/print/printers/create_operation.py +28 -0
  1089. office365/intune/print/printers/defaults.py +40 -0
  1090. office365/intune/print/printers/discovery_settings.py +15 -0
  1091. office365/intune/print/printers/location.py +31 -0
  1092. office365/intune/print/printers/printer.py +70 -0
  1093. office365/intune/print/printers/share_viewpoint.py +15 -0
  1094. office365/intune/print/printers/status.py +23 -0
  1095. office365/intune/print/services/endpoint.py +18 -0
  1096. office365/intune/print/settings.py +16 -0
  1097. office365/intune/print/shares/share.py +60 -0
  1098. office365/intune/print/task.py +47 -0
  1099. office365/intune/print/task_definition.py +38 -0
  1100. office365/intune/print/task_status.py +16 -0
  1101. office365/intune/print/task_trigger.py +32 -0
  1102. office365/intune/print/user_print.py +7 -0
  1103. office365/intune/servicecommunications/announcement_base.py +40 -0
  1104. office365/intune/servicecommunications/health/health.py +42 -0
  1105. office365/intune/servicecommunications/issues/issue.py +48 -1
  1106. office365/intune/servicecommunications/messages/update.py +105 -0
  1107. office365/intune/servicecommunications/service_health_issue_post.py +19 -0
  1108. office365/intune/servicecommunications/service_update_message_viewpoint.py +16 -0
  1109. office365/migration/__init__.py +24 -2
  1110. office365/migration/adapters/__init__.py +36 -6
  1111. office365/migration/base.py +105 -8
  1112. office365/migration/checkpoint.py +17 -16
  1113. office365/migration/delta.py +205 -0
  1114. office365/migration/fidelity.py +283 -0
  1115. office365/migration/job.py +172 -13
  1116. office365/migration/logging.py +373 -0
  1117. office365/migration/manifest.py +8 -3
  1118. office365/migration/map.py +84 -0
  1119. office365/migration/package/builder.py +80 -14
  1120. office365/migration/package/manifest_xml.py +19 -4
  1121. office365/migration/runner.py +156 -27
  1122. office365/migration/server_job.py +79 -1
  1123. office365/migration/session.py +335 -18
  1124. office365/migration/settings.py +67 -3
  1125. office365/migration/sharepoint/__init__.py +148 -0
  1126. office365/migration/sharepoint/adapters.py +2883 -114
  1127. office365/migration/sharepoint/branding.py +260 -0
  1128. office365/migration/sharepoint/handoff.py +252 -0
  1129. office365/migration/sharepoint/navigation.py +197 -0
  1130. office365/migration/sharepoint/package_target.py +132 -3
  1131. office365/migration/sharepoint/pages.py +853 -0
  1132. office365/migration/sharepoint/resolvers.py +695 -30
  1133. office365/migration/sharepoint/security.py +504 -0
  1134. office365/migration/sharepoint/site_scope.py +222 -0
  1135. office365/migration/sharepoint/site_settings.py +196 -0
  1136. office365/migration/sharepoint/site_verify.py +207 -0
  1137. office365/migration/sharepoint/structure.py +1426 -0
  1138. office365/migration/sharepoint/verify.py +186 -0
  1139. office365/migration/teams/_extract.py +2 -1
  1140. office365/migration/teams/models.py +1 -0
  1141. office365/migration/throttling.py +215 -0
  1142. office365/migration/validators.py +250 -15
  1143. office365/onedrive/columns/definition.py +11 -0
  1144. office365/onedrive/contenttypes/content_type.py +24 -0
  1145. office365/onedrive/deleted.py +14 -0
  1146. office365/onedrive/driveitems/collection.py +30 -0
  1147. office365/onedrive/driveitems/copy_result.py +115 -0
  1148. office365/onedrive/driveitems/driveItem.py +315 -41
  1149. office365/onedrive/driveitems/retention_label_settings.py +1 -1
  1150. office365/onedrive/drives/drive.py +8 -9
  1151. office365/onedrive/filestorage/containers/container.py +157 -1
  1152. office365/onedrive/filestorage/containers/custom_property_value.py +2 -0
  1153. office365/onedrive/filestorage/containertypes/registration_settings.py +2 -0
  1154. office365/onedrive/filestorage/containertypes/settings.py +2 -0
  1155. office365/onedrive/filestorage/containertypes/settings_override.py +2 -0
  1156. office365/onedrive/internal/paths/children.py +7 -1
  1157. office365/onedrive/listitems/collection.py +34 -4
  1158. office365/onedrive/listitems/list_item.py +11 -0
  1159. office365/onedrive/lists/list.py +28 -1
  1160. office365/onedrive/operations/long_running.py +34 -2
  1161. office365/onedrive/operations/public_inner_error.py +7 -17
  1162. office365/onedrive/operations/rich_long_running.py +27 -1
  1163. office365/onedrive/permissions/permission.py +10 -0
  1164. office365/onedrive/sharepoint/migration/jobs/error_event.py +35 -1
  1165. office365/onedrive/shares/drive_item.py +2 -3
  1166. office365/onedrive/sitepages/collection.py +34 -4
  1167. office365/onedrive/sitepages/site_page.py +1 -1
  1168. office365/onedrive/sites/site.py +140 -1
  1169. office365/onedrive/sites/sites_with_root.py +21 -2
  1170. office365/onedrive/storage/settings.py +13 -0
  1171. office365/onedrive/termstore/groups/collection.py +23 -3
  1172. office365/onedrive/termstore/groups/group.py +26 -3
  1173. office365/onedrive/termstore/relation.py +8 -0
  1174. office365/onedrive/termstore/sets/collection.py +27 -12
  1175. office365/onedrive/termstore/sets/name.py +4 -0
  1176. office365/onedrive/termstore/sets/set.py +16 -3
  1177. office365/onedrive/termstore/store.py +27 -3
  1178. office365/onedrive/termstore/store_manager.py +42 -10
  1179. office365/onedrive/termstore/terms/description.py +4 -0
  1180. office365/onedrive/termstore/terms/label.py +4 -0
  1181. office365/onedrive/termstore/terms/term.py +6 -1
  1182. office365/onedrive/wellknown_list_name.py +11 -0
  1183. office365/onedrive/workbooks/charts/area_format.py +27 -0
  1184. office365/onedrive/workbooks/charts/axis.py +79 -0
  1185. office365/onedrive/workbooks/charts/axis_format.py +32 -0
  1186. office365/onedrive/workbooks/charts/axis_title.py +38 -0
  1187. office365/onedrive/workbooks/charts/axis_title_format.py +22 -0
  1188. office365/onedrive/workbooks/charts/chart.py +44 -1
  1189. office365/onedrive/workbooks/charts/data_label_format.py +30 -0
  1190. office365/onedrive/workbooks/charts/data_labels.py +54 -1
  1191. office365/onedrive/workbooks/charts/fill.py +6 -0
  1192. office365/onedrive/workbooks/charts/font.py +38 -0
  1193. office365/onedrive/workbooks/charts/gridlines.py +33 -0
  1194. office365/onedrive/workbooks/charts/gridlines_format.py +24 -0
  1195. office365/onedrive/workbooks/charts/legend/format.py +27 -0
  1196. office365/onedrive/workbooks/charts/legend/legend.py +24 -1
  1197. office365/onedrive/workbooks/charts/line_format.py +16 -0
  1198. office365/onedrive/workbooks/charts/point.py +31 -0
  1199. office365/onedrive/workbooks/charts/point_format.py +22 -0
  1200. office365/onedrive/workbooks/charts/series/format.py +29 -0
  1201. office365/onedrive/workbooks/charts/series/series.py +13 -0
  1202. office365/onedrive/workbooks/charts/title.py +23 -0
  1203. office365/onedrive/workbooks/charts/title_format.py +27 -0
  1204. office365/onedrive/workbooks/comments/reply.py +18 -0
  1205. office365/onedrive/workbooks/filter.py +4 -0
  1206. office365/onedrive/workbooks/functions/functions.py +6 -0
  1207. office365/onedrive/workbooks/functions/result.py +13 -0
  1208. office365/onedrive/workbooks/names/named_item.py +27 -1
  1209. office365/onedrive/workbooks/operations/workbook.py +2 -1
  1210. office365/onedrive/workbooks/ranges/fill.py +6 -0
  1211. office365/onedrive/workbooks/ranges/format.py +6 -0
  1212. office365/onedrive/workbooks/ranges/format_protection.py +18 -0
  1213. office365/onedrive/workbooks/ranges/range.py +58 -1
  1214. office365/onedrive/workbooks/ranges/sort.py +6 -0
  1215. office365/onedrive/workbooks/ranges/view.py +67 -1
  1216. office365/onedrive/workbooks/tables/columns/column.py +18 -0
  1217. office365/onedrive/workbooks/tables/rows/row.py +6 -0
  1218. office365/onedrive/workbooks/tables/sort.py +22 -1
  1219. office365/onedrive/workbooks/tables/table.py +16 -0
  1220. office365/onedrive/workbooks/workbook.py +4 -0
  1221. office365/onedrive/workbooks/worksheets/protection.py +11 -0
  1222. office365/onedrive/workbooks/worksheets/worksheet.py +16 -0
  1223. office365/onenote/entity_base_model.py +13 -0
  1224. office365/onenote/notebooks/collection.py +38 -0
  1225. office365/onenote/notebooks/notebook.py +45 -4
  1226. office365/onenote/onenote.py +3 -3
  1227. office365/onenote/operations/onenote.py +24 -1
  1228. office365/onenote/pages/collection.py +36 -20
  1229. office365/onenote/pages/page.py +32 -0
  1230. office365/onenote/resources/resource.py +11 -0
  1231. office365/onenote/sectiongroups/section_group.py +20 -5
  1232. office365/onenote/sections/collection.py +47 -0
  1233. office365/onenote/sections/section.py +9 -5
  1234. office365/outlook/attachment_info.py +11 -0
  1235. office365/outlook/calendar/check_in_claim.py +32 -0
  1236. office365/outlook/calendar/events/event.py +29 -2
  1237. office365/outlook/calendar/place.py +56 -1
  1238. office365/outlook/calendar/place_collection.py +34 -0
  1239. office365/outlook/calendar/rooms/room.py +60 -1
  1240. office365/outlook/calendar/rooms/workspace.py +38 -0
  1241. office365/outlook/calendar/sharing_message.py +30 -0
  1242. office365/outlook/contacts/folders/collection.py +23 -3
  1243. office365/outlook/mail/attachments/attachment.py +11 -0
  1244. office365/outlook/mail/attachments/file.py +12 -1
  1245. office365/outlook/mail/attachments/type.py +7 -2
  1246. office365/outlook/mail/export_item_response.py +1 -1
  1247. office365/outlook/mail/folders/collection.py +38 -0
  1248. office365/outlook/mail/folders/folder.py +8 -3
  1249. office365/outlook/mail/messages/event_message.py +71 -0
  1250. office365/outlook/mail/messages/message.py +32 -13
  1251. office365/outlook/mail/post.py +118 -1
  1252. office365/outlook/people/person_type.py +2 -0
  1253. office365/outlook/people/profile_card_property.py +29 -0
  1254. office365/outlook/person.py +29 -1
  1255. office365/partners/billing/billed_reconciliation.py +3 -1
  1256. office365/partners/billing/billed_usage.py +3 -1
  1257. office365/partners/billing/billing.py +1 -1
  1258. office365/partners/billing/blob.py +15 -0
  1259. office365/partners/billing/export_success_operation.py +2 -1
  1260. office365/partners/billing/failed_operation.py +14 -1
  1261. office365/partners/billing/manifest.py +12 -1
  1262. office365/partners/billing/metrics.py +34 -0
  1263. office365/partners/billing/metrics_base.py +53 -0
  1264. office365/partners/billing/metrics_initial.py +17 -0
  1265. office365/partners/billing/metrics_recent.py +17 -0
  1266. office365/partners/billing/operation.py +35 -1
  1267. office365/partners/billing/reconciliation.py +40 -0
  1268. office365/partners/billing/running_operation.py +2 -1
  1269. office365/partners/billing/unbilled_reconciliation.py +3 -1
  1270. office365/partners/billing/unbilled_usage.py +3 -1
  1271. office365/partners/partnertenanttype.py +8 -12
  1272. office365/planner/applied_categories.py +10 -0
  1273. office365/planner/assigned_to_task_board_task_format.py +30 -0
  1274. office365/planner/assignment.py +31 -0
  1275. office365/planner/assignments.py +45 -1
  1276. office365/planner/bucket_task_board_task_format.py +16 -0
  1277. office365/planner/buckets/bucket.py +21 -8
  1278. office365/planner/buckets/collection.py +56 -15
  1279. office365/planner/category_descriptions.py +59 -0
  1280. office365/planner/entity.py +52 -0
  1281. office365/planner/external_reference.py +20 -0
  1282. office365/planner/internal/paths/collections.py +27 -0
  1283. office365/planner/order_hints_by_assignee.py +10 -0
  1284. office365/planner/planner.py +2 -2
  1285. office365/planner/plans/container.py +4 -0
  1286. office365/planner/plans/details.py +6 -2
  1287. office365/planner/plans/plan.py +36 -25
  1288. office365/planner/progress_task_board_task_format.py +16 -0
  1289. office365/planner/tasks/check_list_item.py +28 -1
  1290. office365/planner/tasks/check_list_items.py +46 -9
  1291. office365/planner/tasks/collection.py +67 -32
  1292. office365/planner/tasks/task.py +178 -24
  1293. office365/planner/tasks/task_details.py +54 -5
  1294. office365/planner/user.py +8 -6
  1295. office365/reports/internal/queries/create_report_query.py +3 -24
  1296. office365/reports/partners.py +11 -0
  1297. office365/reports/report.py +4 -0
  1298. office365/reports/report_export.py +197 -0
  1299. office365/reports/report_result.py +55 -0
  1300. office365/reports/root.py +90 -45
  1301. office365/reports/security/root.py +12 -6
  1302. office365/runtime/auth/authentication_context.py +90 -19
  1303. office365/runtime/auth/certificate.py +105 -0
  1304. office365/runtime/auth/entra/authentication_context.py +130 -9
  1305. office365/runtime/client_object.py +145 -5
  1306. office365/runtime/client_object_collection.py +350 -10
  1307. office365/runtime/client_request.py +323 -22
  1308. office365/runtime/client_request_exception.py +64 -2
  1309. office365/runtime/client_result.py +80 -2
  1310. office365/runtime/client_runtime_context.py +667 -30
  1311. office365/runtime/client_value.py +29 -10
  1312. office365/runtime/client_value_collection.py +30 -15
  1313. office365/runtime/converters/scalars.py +27 -0
  1314. office365/runtime/converters/upsert.py +3 -3
  1315. office365/runtime/converters/value.py +31 -4
  1316. office365/runtime/display.py +232 -0
  1317. office365/runtime/exceptions.py +65 -4
  1318. office365/runtime/http/prefer.py +123 -2
  1319. office365/runtime/http/request_options.py +10 -3
  1320. office365/runtime/http/throttling.py +151 -21
  1321. office365/runtime/imports.py +160 -34
  1322. office365/runtime/lro.py +751 -0
  1323. office365/runtime/odata/batch_util.py +33 -1
  1324. office365/runtime/odata/literals.py +49 -0
  1325. office365/runtime/odata/request.py +9 -4
  1326. office365/runtime/odata/type.py +10 -4
  1327. office365/runtime/odata/v3/batch_request.py +174 -26
  1328. office365/runtime/odata/v4/batch_request.py +146 -15
  1329. office365/runtime/odata/v4/upload_session_request.py +18 -0
  1330. office365/runtime/paths/alternate_key.py +44 -0
  1331. office365/runtime/paths/appid.py +8 -8
  1332. office365/runtime/paths/builder.py +11 -5
  1333. office365/runtime/persistence.py +71 -0
  1334. office365/runtime/pollable.py +234 -0
  1335. office365/runtime/progress.py +52 -0
  1336. office365/runtime/queries/batch.py +13 -5
  1337. office365/runtime/queries/client_query.py +27 -1
  1338. office365/runtime/queries/deferred.py +7 -0
  1339. office365/runtime/queries/get_or_create.py +19 -3
  1340. office365/runtime/queries/upsert_entity.py +103 -0
  1341. office365/runtime/query_capabilities.py +333 -0
  1342. office365/runtime/record_collection.py +176 -4
  1343. office365/runtime/respond_async.py +132 -0
  1344. office365/runtime/retry.py +124 -11
  1345. office365/runtime/transport/base.py +160 -4
  1346. office365/runtime/transport/httpx_transport.py +263 -0
  1347. office365/runtime/transport/offload.py +156 -0
  1348. office365/runtime/transport/requests_transport.py +163 -18
  1349. office365/runtime/transport/throttled_transport.py +29 -6
  1350. office365/runtime/verification.py +41 -9
  1351. office365/search/acronyms/acronym.py +19 -0
  1352. office365/search/aggregation_option.py +12 -0
  1353. office365/search/alteration_response.py +18 -0
  1354. office365/search/altered_query_token.py +16 -0
  1355. office365/search/answers/keyword.py +17 -0
  1356. office365/search/answers/variant.py +19 -0
  1357. office365/search/bookmarks/bookmark.py +21 -1
  1358. office365/search/bucket.py +4 -0
  1359. office365/search/bucket_aggregation_definition.py +23 -0
  1360. office365/search/bucket_aggregation_range.py +15 -0
  1361. office365/search/collapse_property.py +16 -0
  1362. office365/search/connection_item.py +14 -0
  1363. office365/search/entity_type.py +1 -1
  1364. office365/search/external/connection.py +88 -0
  1365. office365/search/external/item.py +29 -0
  1366. office365/search/external/item_configuration.py +18 -0
  1367. office365/search/external/search_settings.py +11 -1
  1368. office365/search/hits/container.py +4 -0
  1369. office365/search/hits/hit.py +10 -0
  1370. office365/search/identity.py +16 -0
  1371. office365/search/identity_set.py +8 -1
  1372. office365/search/qnas/qna.py +21 -1
  1373. office365/search/query.py +4 -0
  1374. office365/search/recommended_action.py +16 -0
  1375. office365/search/request.py +17 -0
  1376. office365/search/response.py +10 -0
  1377. office365/search/result_template.py +16 -0
  1378. office365/search/result_template_dictionary.py +10 -0
  1379. office365/search/result_template_option.py +14 -0
  1380. office365/search/retrieval_extract.py +15 -0
  1381. office365/search/retrieval_hit.py +27 -0
  1382. office365/search/retrieval_response.py +18 -0
  1383. office365/search/search_alteration.py +21 -0
  1384. office365/search/search_alteration_options.py +15 -0
  1385. office365/search/search_resource_metadata_dictionary.py +10 -0
  1386. office365/search/search_result.py +14 -0
  1387. office365/search/sharepoint_onedrive_options.py +7 -0
  1388. office365/search/sort_property.py +4 -0
  1389. office365/security/account.py +24 -0
  1390. office365/security/account_target_content.py +17 -0
  1391. office365/security/activity_metadata.py +15 -0
  1392. office365/security/address_book_account_target_content.py +15 -0
  1393. office365/security/ai_agent_evidence.py +19 -0
  1394. office365/security/alerts/alert.py +459 -0
  1395. office365/security/alerts/blob_container_evidence.py +17 -0
  1396. office365/security/alerts/blob_evidence.py +21 -0
  1397. office365/security/alerts/collection.py +49 -0
  1398. office365/security/alerts/detection.py +16 -0
  1399. office365/security/alerts/evidence.py +28 -0
  1400. office365/security/alerts/file_security_state.py +23 -0
  1401. office365/security/alerts/history_state.py +31 -0
  1402. office365/security/alerts/malware_evidence.py +22 -0
  1403. office365/security/alerts/process_evidence.py +27 -0
  1404. office365/security/alerts/sas_token_evidence.py +26 -0
  1405. office365/security/alerts/trigger.py +16 -0
  1406. office365/security/analyzed_message_evidence.py +34 -0
  1407. office365/security/article.py +75 -0
  1408. office365/security/assigned_training_info.py +16 -0
  1409. office365/security/attacksimulations/automation.py +90 -0
  1410. office365/security/attacksimulations/automation_run.py +43 -0
  1411. office365/security/attacksimulations/event.py +22 -0
  1412. office365/security/attacksimulations/landing_page.py +105 -0
  1413. office365/security/attacksimulations/landing_page_detail.py +26 -0
  1414. office365/security/attacksimulations/login_page.py +83 -0
  1415. office365/security/attacksimulations/operations/operation.py +37 -0
  1416. office365/security/attacksimulations/payload.py +192 -0
  1417. office365/security/attacksimulations/repeat_offender.py +21 -0
  1418. office365/security/attacksimulations/reports/overview.py +30 -0
  1419. office365/security/attacksimulations/reports/report.py +29 -0
  1420. office365/security/attacksimulations/root.py +124 -0
  1421. office365/security/attacksimulations/simulation.py +220 -0
  1422. office365/security/attacksimulations/training_user_coverage.py +22 -0
  1423. office365/security/attacksimulations/user.py +20 -0
  1424. office365/security/attacksimulations/user_coverage.py +26 -0
  1425. office365/security/attacksimulations/users/details.py +48 -0
  1426. office365/security/attribute_definition.py +37 -0
  1427. office365/security/attribute_mapping.py +11 -0
  1428. office365/security/attribute_mapping_source.py +25 -0
  1429. office365/security/audit/audit_data.py +15 -0
  1430. office365/security/audit/core_root.py +26 -0
  1431. office365/security/audit/log_query.py +114 -0
  1432. office365/security/audit/log_record.py +84 -0
  1433. office365/security/audit/whois_record.py +22 -0
  1434. office365/security/average_comparative_score.py +15 -0
  1435. office365/security/base_end_user_notification.py +20 -0
  1436. office365/security/cases/case.py +48 -0
  1437. office365/security/cases/ediscovery.py +136 -0
  1438. office365/security/cases/operation.py +67 -0
  1439. office365/security/cases/root.py +21 -0
  1440. office365/security/category_template.py +30 -0
  1441. office365/security/certification_control.py +15 -0
  1442. office365/security/cloud_app_security_state.py +16 -0
  1443. office365/security/cloud_application_evidence.py +20 -0
  1444. office365/security/cloud_clipboard_item.py +44 -0
  1445. office365/security/cloud_clipboard_item_payload.py +15 -0
  1446. office365/security/cloud_clipboard_root.py +28 -0
  1447. office365/security/cloud_flare_rule_model.py +16 -0
  1448. office365/security/cloud_flare_ruleset_model.py +16 -0
  1449. office365/security/cloud_flare_verified_details_model.py +25 -0
  1450. office365/security/cloud_flare_web_application_firewall_provider.py +21 -0
  1451. office365/security/cloud_logon_session_evidence.py +24 -0
  1452. office365/security/cloud_pc_audit_event.py +105 -0
  1453. office365/security/cloud_pc_device_image.py +95 -0
  1454. office365/security/cloud_pc_gallery_image.py +71 -0
  1455. office365/security/cloud_pc_on_premises_connection.py +130 -0
  1456. office365/security/cloud_pc_provisioning_policy.py +156 -0
  1457. office365/security/cloud_pc_provisioning_policy_assignment.py +38 -0
  1458. office365/security/cloud_pc_report.py +9 -0
  1459. office365/security/cloud_pc_service_plan.py +31 -0
  1460. office365/security/cloud_pc_user_setting.py +70 -0
  1461. office365/security/cloud_pc_user_setting_assignment.py +31 -0
  1462. office365/security/cloud_video_interop_info.py +16 -0
  1463. office365/security/coachmark_location.py +18 -0
  1464. office365/security/coachmark_location_type.py +17 -0
  1465. office365/security/collaboration/analyzed_email.py +288 -0
  1466. office365/security/collaboration/analyzed_email_attachment.py +25 -0
  1467. office365/security/collaboration/analyzed_email_delivery_detail.py +21 -0
  1468. office365/security/collaboration/analyzed_email_url.py +21 -0
  1469. office365/security/collaboration/collaboration_root.py +22 -0
  1470. office365/security/compromise_indicator.py +17 -0
  1471. office365/security/container_evidence.py +28 -0
  1472. office365/security/container_image_evidence.py +17 -0
  1473. office365/security/control_score.py +17 -0
  1474. office365/security/custom_training_setting.py +24 -0
  1475. office365/security/cvss_summary.py +18 -0
  1476. office365/security/data_source_container.py +44 -0
  1477. office365/security/datasecurity/classifcation_error_base.py +18 -0
  1478. office365/security/datasecurity/classification_error.py +20 -0
  1479. office365/security/datasecurity/compute_rights_and_inheritance_result.py +25 -0
  1480. office365/security/datasecurity/governance.py +42 -0
  1481. office365/security/datasecurity/label_content_right.py +44 -0
  1482. office365/security/datasecurity/policy_scope_base.py +26 -0
  1483. office365/security/datasecurity/policy_tenant_scope.py +15 -0
  1484. office365/security/datasecurity/policy_tip_action.py +16 -0
  1485. office365/security/datasecurity/process_content_batch_request.py +17 -0
  1486. office365/security/datasecurity/process_content_request.py +33 -0
  1487. office365/security/datasecurity/process_content_response.py +24 -0
  1488. office365/security/datasecurity/process_content_responses.py +16 -0
  1489. office365/security/datasecurity/process_file_metadata.py +16 -0
  1490. office365/security/datasecurity/sensitivity_label.py +117 -0
  1491. office365/security/datasecurity/tenant_protection_scope_container.py +9 -0
  1492. office365/security/datasecurity/usage_rights_included.py +31 -0
  1493. office365/security/detonation_details.py +34 -0
  1494. office365/security/device_evidence.py +50 -0
  1495. office365/security/dns_evidence.py +19 -0
  1496. office365/security/ediscovery/case_member.py +31 -0
  1497. office365/security/ediscovery/custodian.py +76 -0
  1498. office365/security/ediscovery/ediscovery_case_settings.py +57 -0
  1499. office365/security/ediscovery/ediscovery_estimate_operation.py +66 -0
  1500. office365/security/ediscovery/ediscovery_export_operation.py +64 -0
  1501. office365/security/ediscovery/ediscovery_index_operation.py +8 -0
  1502. office365/security/ediscovery/ediscovery_review_tag.py +34 -0
  1503. office365/security/ediscovery/noncustodial_data_source.py +37 -0
  1504. office365/security/ediscovery/operations/add_to_review_set.py +64 -0
  1505. office365/security/ediscovery/operations/hold.py +8 -0
  1506. office365/security/ediscovery/operations/hold_policy_sync.py +23 -0
  1507. office365/security/ediscovery/operations/purge_data.py +23 -0
  1508. office365/security/ediscovery/operations/search_export.py +83 -0
  1509. office365/security/ediscovery/operations/tag.py +8 -0
  1510. office365/security/ediscovery/review_set.py +22 -0
  1511. office365/security/ediscovery/review_set_query.py +9 -0
  1512. office365/security/ediscovery/search.py +81 -0
  1513. office365/security/email_payload_detail.py +17 -0
  1514. office365/security/end_user_notification.py +110 -0
  1515. office365/security/end_user_notification_detail.py +46 -0
  1516. office365/security/end_user_notification_setting.py +25 -0
  1517. office365/security/evaluation_outcome.py +13 -0
  1518. office365/security/event_propagation_result.py +18 -0
  1519. office365/security/event_query.py +17 -0
  1520. office365/security/expression_input_object.py +20 -0
  1521. office365/security/file_evidence.py +18 -0
  1522. office365/security/file_hash.py +21 -0
  1523. office365/security/file_hash_evidence.py +16 -0
  1524. office365/security/file_plan_applied_category.py +15 -0
  1525. office365/security/filter.py +22 -0
  1526. office365/security/filter_clause.py +17 -0
  1527. office365/security/filter_group.py +17 -0
  1528. office365/security/formatted_content.py +16 -0
  1529. office365/security/google_cloud_resource_evidence.py +21 -0
  1530. office365/security/health_issue.py +81 -0
  1531. office365/security/host_security_state.py +22 -0
  1532. office365/security/host_ssl_certificate_port.py +17 -0
  1533. office365/security/hunting_query_results.py +24 -0
  1534. office365/security/hyperlink.py +15 -0
  1535. office365/security/identity_accounts.py +47 -0
  1536. office365/security/identity_provider.py +14 -0
  1537. office365/security/incidents/incident.py +148 -0
  1538. office365/security/include_all_account_target_content.py +10 -0
  1539. office365/security/indicator.py +28 -0
  1540. office365/security/intelligence_profile_country_or_region_of_origin.py +15 -0
  1541. office365/security/investigation_security_state.py +15 -0
  1542. office365/security/invoke_action_result.py +24 -0
  1543. office365/security/io_t_device_evidence.py +46 -0
  1544. office365/security/ip_address.py +36 -0
  1545. office365/security/ip_evidence.py +19 -0
  1546. office365/security/kubernetes_cluster_evidence.py +20 -0
  1547. office365/security/kubernetes_controller_evidence.py +19 -0
  1548. office365/security/kubernetes_namespace_evidence.py +18 -0
  1549. office365/security/kubernetes_pod_evidence.py +38 -0
  1550. office365/security/kubernetes_secret_evidence.py +17 -0
  1551. office365/security/kubernetes_service_account_evidence.py +16 -0
  1552. office365/security/kubernetes_service_evidence.py +29 -0
  1553. office365/security/kubernetes_service_port.py +20 -0
  1554. office365/security/labels/retention/collection.py +68 -0
  1555. office365/security/labels/retention/duration_in_days.py +16 -0
  1556. office365/security/labels/retention/fileplan_descriptor.py +104 -0
  1557. office365/security/labels/retention/label.py +158 -0
  1558. office365/security/labels/root.py +83 -0
  1559. office365/security/mailbox_configuration_evidence.py +24 -0
  1560. office365/security/mailbox_evidence.py +18 -0
  1561. office365/security/malware_state.py +18 -0
  1562. office365/security/message_security_state.py +23 -0
  1563. office365/security/microsoft_custom_training_setting.py +26 -0
  1564. office365/security/microsoft_managed_training_setting.py +19 -0
  1565. office365/security/microsoft_training_assignment_mapping.py +26 -0
  1566. office365/security/monitor_mode.py +12 -0
  1567. office365/security/monitor_run_status.py +14 -0
  1568. office365/security/monitor_status.py +13 -0
  1569. office365/security/network_connection.py +39 -0
  1570. office365/security/network_connection_evidence.py +21 -0
  1571. office365/security/nic_evidence.py +18 -0
  1572. office365/security/no_training_notification_setting.py +15 -0
  1573. office365/security/no_training_setting.py +10 -0
  1574. office365/security/object_definition.py +25 -0
  1575. office365/security/object_mapping.py +33 -0
  1576. office365/security/parse_expression_response.py +22 -0
  1577. office365/security/passive_dns_record.py +47 -0
  1578. office365/security/payload_coachmark.py +21 -0
  1579. office365/security/payload_detail.py +20 -0
  1580. office365/security/positive_reinforcement_notification.py +17 -0
  1581. office365/security/process.py +28 -0
  1582. office365/security/processing_error.py +15 -0
  1583. office365/security/registry_key_state.py +26 -0
  1584. office365/security/retention_event.py +98 -0
  1585. office365/security/retention_event_status.py +18 -0
  1586. office365/security/scorecontrol/profile.py +135 -0
  1587. office365/security/search/data_source.py +34 -0
  1588. office365/security/securescores/control_state_update.py +19 -0
  1589. office365/security/securescores/secure_score.py +85 -0
  1590. office365/security/security.py +208 -0
  1591. office365/security/security_resource.py +18 -0
  1592. office365/security/security_vendor_information.py +17 -0
  1593. office365/security/sensitivity_label_info.py +18 -0
  1594. office365/security/sensor.py +84 -0
  1595. office365/security/sensor_candidate_activation_configuration.py +25 -0
  1596. office365/security/sensor_settings.py +20 -0
  1597. office365/security/service_principal_evidence.py +21 -0
  1598. office365/security/settings_container.py +19 -0
  1599. office365/security/simulation_event.py +15 -0
  1600. office365/security/simulation_events_content.py +19 -0
  1601. office365/security/simulation_notification.py +17 -0
  1602. office365/security/ssl_certificate.py +78 -0
  1603. office365/security/ssl_certificate_entity.py +24 -0
  1604. office365/security/string_key_attribute_mapping_source_value_pair.py +19 -0
  1605. office365/security/subdomain.py +26 -0
  1606. office365/security/subjectrightsrequests/authored_note.py +35 -0
  1607. office365/security/subjectrightsrequests/request.py +255 -0
  1608. office365/security/teams_message_evidence.py +52 -0
  1609. office365/security/threatintelligence/host.py +138 -0
  1610. office365/security/threatintelligence/host_component.py +46 -0
  1611. office365/security/threatintelligence/host_cookie.py +41 -0
  1612. office365/security/threatintelligence/host_logon_session_evidence.py +21 -0
  1613. office365/security/threatintelligence/host_pair.py +41 -0
  1614. office365/security/threatintelligence/host_port.py +106 -0
  1615. office365/security/threatintelligence/host_port_component.py +19 -0
  1616. office365/security/threatintelligence/host_reputation.py +43 -0
  1617. office365/security/threatintelligence/host_reputation_rule.py +21 -0
  1618. office365/security/threatintelligence/host_ssl_certificate.py +56 -0
  1619. office365/security/threatintelligence/host_tracker.py +41 -0
  1620. office365/security/threatintelligence/hosts/__init__.py +0 -0
  1621. office365/security/threatintelligence/profile.py +114 -0
  1622. office365/security/threatintelligence/threat_intelligence.py +193 -0
  1623. office365/security/threatintelligence/whoisrecords/__init__.py +0 -0
  1624. office365/security/timeline_event.py +25 -0
  1625. office365/security/training.py +123 -0
  1626. office365/security/training_language_detail.py +64 -0
  1627. office365/security/training_notification_setting.py +21 -0
  1628. office365/security/training_reminder_notification.py +17 -0
  1629. office365/security/training_setting.py +17 -0
  1630. office365/security/triggers/__init__.py +0 -0
  1631. office365/security/triggers/root.py +30 -0
  1632. office365/security/triggertypes/__init__.py +0 -0
  1633. office365/security/triggertypes/collection.py +9 -0
  1634. office365/security/triggertypes/root.py +24 -0
  1635. office365/security/unified_group_source.py +22 -0
  1636. office365/security/uri_click_security_state.py +20 -0
  1637. office365/security/user.py +21 -0
  1638. office365/security/user_account.py +27 -0
  1639. office365/security/user_evidence.py +17 -0
  1640. office365/security/user_security_state.py +33 -0
  1641. office365/security/user_simulation_event_info.py +21 -0
  1642. office365/security/user_source.py +27 -0
  1643. office365/security/user_training_content_event_info.py +19 -0
  1644. office365/security/user_training_event_info.py +22 -0
  1645. office365/security/user_training_status_info.py +19 -0
  1646. office365/security/vm_metadata.py +19 -0
  1647. office365/security/vulnerability.py +134 -0
  1648. office365/security/vulnerability_state.py +16 -0
  1649. office365/security/whois_base_record.py +127 -0
  1650. office365/security/whois_contact.py +20 -0
  1651. office365/security/whois_nameserver.py +21 -0
  1652. office365/sharepoint/activities/entity.py +9 -0
  1653. office365/sharepoint/administration/sitemove/service.py +70 -1
  1654. office365/sharepoint/administration/sitemove/systemsitelockexpres.py +8 -0
  1655. office365/sharepoint/administration/spfarm.py +8 -0
  1656. office365/sharepoint/administration/spwebapplication.py +23 -1
  1657. office365/sharepoint/alerts/alert.py +10 -0
  1658. office365/sharepoint/approvals/approvals.py +262 -1
  1659. office365/sharepoint/apps/access_requests.py +69 -0
  1660. office365/sharepoint/apps/appsitecontextutility.py +23 -1
  1661. office365/sharepoint/apps/apptileproperties.py +63 -1
  1662. office365/sharepoint/apps/catalog.py +18 -1
  1663. office365/sharepoint/apps/instance.py +87 -1
  1664. office365/sharepoint/apps/management/principal_info.py +19 -0
  1665. office365/sharepoint/attachments/collection.py +20 -3
  1666. office365/sharepoint/audit/audit.py +5 -0
  1667. office365/sharepoint/authentication/auth_file_detonation_request_response.py +15 -0
  1668. office365/sharepoint/authpolicy/events/event.py +20 -0
  1669. office365/sharepoint/authpolicy/tenant/compliancereportentitydata.py +12 -0
  1670. office365/sharepoint/automations/automations.py +682 -1
  1671. office365/sharepoint/brandcenter/brand_center.py +277 -0
  1672. office365/sharepoint/businessdata/infrastructure/securestore/connection_settings.py +42 -0
  1673. office365/sharepoint/businessdata/runtime/subscription.py +16 -0
  1674. office365/sharepoint/changes/alert.py +8 -0
  1675. office365/sharepoint/changes/app_consent_principal.py +9 -0
  1676. office365/sharepoint/changes/change.py +7 -0
  1677. office365/sharepoint/changes/content_type.py +8 -0
  1678. office365/sharepoint/changes/field.py +8 -0
  1679. office365/sharepoint/changes/folder.py +7 -0
  1680. office365/sharepoint/changes/item.py +53 -0
  1681. office365/sharepoint/changes/list.py +23 -0
  1682. office365/sharepoint/client_context.py +291 -14
  1683. office365/sharepoint/clientsidecomponent/hostedapps/add_response.py +19 -0
  1684. office365/sharepoint/comments/collection.py +1 -1
  1685. office365/sharepoint/comments/comment.py +20 -1
  1686. office365/sharepoint/compliance/sensitivitylabels/collection.py +33 -1
  1687. office365/sharepoint/content_solution.py +18 -0
  1688. office365/sharepoint/contentcenter/machinelearning/enabled.py +37 -0
  1689. office365/sharepoint/contentcenter/machinelearning/hub.py +57 -8
  1690. office365/sharepoint/contentcenter/machinelearning/models/entity_data.py +135 -0
  1691. office365/sharepoint/contentcenter/machinelearning/models/model.py +20 -2
  1692. office365/sharepoint/contentcenter/machinelearning/publications/publication.py +7 -0
  1693. office365/sharepoint/contentcenter/machinelearning/samples/entity_data.py +14 -0
  1694. office365/sharepoint/contentcenter/machinelearning/samples/sample.py +7 -0
  1695. office365/sharepoint/contentcenter/machinelearning/workitementitydata.py +7 -0
  1696. office365/sharepoint/contentcenter/machinelearning/workitems/item.py +12 -0
  1697. office365/sharepoint/contentcenter/sp_machine_learning_column_autofill_prompt_suggestion_data.py +25 -0
  1698. office365/sharepoint/contentcenter/sp_machine_learning_column_autofill_prompt_suggestion_result.py +16 -0
  1699. office365/sharepoint/contentcenter/sp_machine_learning_publications_result.py +38 -0
  1700. office365/sharepoint/contentcenter/syntex_models_landing_info.py +24 -0
  1701. office365/sharepoint/contentcenter/template_api.py +195 -0
  1702. office365/sharepoint/contenttypes/collection.py +54 -17
  1703. office365/sharepoint/contenttypes/content_type.py +146 -5
  1704. office365/sharepoint/contenttypes/content_type_id.py +4 -0
  1705. office365/sharepoint/contenttypes/fieldlinks/collection.py +1 -2
  1706. office365/sharepoint/contenttypes/fieldlinks/field_link.py +20 -2
  1707. office365/sharepoint/directory/conditional_access_policy.py +5 -0
  1708. office365/sharepoint/directory/group_and_user_status.py +9 -0
  1709. office365/sharepoint/directory/groups/group.py +203 -6
  1710. office365/sharepoint/directory/link.py +20 -0
  1711. office365/sharepoint/directory/members_info.py +15 -0
  1712. office365/sharepoint/directory/multi_conditional_access_policy.py +5 -0
  1713. office365/sharepoint/directory/my_groups_result.py +14 -0
  1714. office365/sharepoint/directory/named_location_policy.py +15 -0
  1715. office365/sharepoint/directory/provider/provider.py +141 -0
  1716. office365/sharepoint/directory/session.py +69 -0
  1717. office365/sharepoint/directory/tenant.py +37 -0
  1718. office365/sharepoint/directory/users/user.py +343 -1
  1719. office365/sharepoint/document_location.py +14 -0
  1720. office365/sharepoint/documentmanagement/document_set.py +4 -0
  1721. office365/sharepoint/dynamiccontent/updateservice.py +207 -1
  1722. office365/sharepoint/edgeworth_settings_apis.py +7 -0
  1723. office365/sharepoint/engage_distribution_info.py +17 -0
  1724. office365/sharepoint/entity.py +94 -4
  1725. office365/sharepoint/entity_collection.py +86 -1
  1726. office365/sharepoint/esign/digitallegacy.py +371 -1
  1727. office365/sharepoint/eventreceivers/definition.py +26 -0
  1728. office365/sharepoint/exceptions.py +2 -2
  1729. office365/sharepoint/features/definitions/definition.py +8 -0
  1730. office365/sharepoint/fields/calculated.py +22 -0
  1731. office365/sharepoint/fields/choice.py +9 -0
  1732. office365/sharepoint/fields/collection.py +61 -7
  1733. office365/sharepoint/fields/field.py +109 -1
  1734. office365/sharepoint/fields/lookup.py +23 -0
  1735. office365/sharepoint/fields/multi_choice.py +7 -0
  1736. office365/sharepoint/fields/multi_line_text.py +12 -0
  1737. office365/sharepoint/fields/number.py +27 -0
  1738. office365/sharepoint/fields/rating_scale.py +27 -0
  1739. office365/sharepoint/fields/user.py +12 -0
  1740. office365/sharepoint/files/checked_out_file.py +15 -0
  1741. office365/sharepoint/files/collection.py +269 -38
  1742. office365/sharepoint/files/download_operation.py +466 -0
  1743. office365/sharepoint/files/file.py +648 -20
  1744. office365/sharepoint/files/file_url_virus_status_update.py +9 -0
  1745. office365/sharepoint/files/publish/status.py +43 -0
  1746. office365/sharepoint/files/recent_file_collection.py +41 -0
  1747. office365/sharepoint/files/versions/collection.py +1 -1
  1748. office365/sharepoint/files/versions/event.py +13 -0
  1749. office365/sharepoint/flows/synchronization_result.py +13 -1
  1750. office365/sharepoint/folders/coloring.py +29 -0
  1751. office365/sharepoint/folders/folder.py +173 -4
  1752. office365/sharepoint/fontpackages/font_package.py +69 -0
  1753. office365/sharepoint/fontpackages/oob_settings.py +47 -0
  1754. office365/sharepoint/forms/form.py +8 -0
  1755. office365/sharepoint/glutamate_controller.py +44 -0
  1756. office365/sharepoint/htmlpages/html_page.py +5 -0
  1757. office365/sharepoint/item_reference.py +17 -0
  1758. office365/sharepoint/largeoperation/operation.py +26 -1
  1759. office365/sharepoint/likes/user_entity.py +7 -0
  1760. office365/sharepoint/list_creation_information.py +18 -0
  1761. office365/sharepoint/listhome/favorite_lists.py +130 -0
  1762. office365/sharepoint/listhome/favoritelistssubstrate.py +106 -1
  1763. office365/sharepoint/listhome/item2.py +7 -0
  1764. office365/sharepoint/listhome/itemcollection.py +99 -1
  1765. office365/sharepoint/listhome/nucleus.py +15 -1
  1766. office365/sharepoint/listhome/pinneditems.py +127 -1
  1767. office365/sharepoint/listitems/collection.py +28 -0
  1768. office365/sharepoint/listitems/favoriteitemcollection.py +20 -1
  1769. office365/sharepoint/listitems/listitem.py +529 -27
  1770. office365/sharepoint/listitems/relateditemmanager.py +231 -1
  1771. office365/sharepoint/listitems/versions/version.py +9 -0
  1772. office365/sharepoint/lists/apihubconnector.py +18 -1
  1773. office365/sharepoint/lists/batch_convert_asset_result.py +19 -0
  1774. office365/sharepoint/lists/batch_convert_classic_wiki_page_input.py +15 -0
  1775. office365/sharepoint/lists/batch_convert_item_result.py +21 -1
  1776. office365/sharepoint/lists/bloom_filter.py +33 -0
  1777. office365/sharepoint/lists/list.py +1255 -31
  1778. office365/sharepoint/lists/list_forms_api.py +106 -1
  1779. office365/sharepoint/lists/playlist.py +33 -1
  1780. office365/sharepoint/lists/recentlist.py +7 -0
  1781. office365/sharepoint/lists/recentlistcollection.py +76 -1
  1782. office365/sharepoint/lists/recentlistproxy.py +31 -1
  1783. office365/sharepoint/lists/recommendationcollection.py +71 -1
  1784. office365/sharepoint/lists/smart_wiki_library_utility.py +102 -0
  1785. office365/sharepoint/lists/smart_wiki_metadata_update_parameters.py +18 -0
  1786. office365/sharepoint/lists/templates/template.py +58 -0
  1787. office365/sharepoint/lists/version_policy_manager.py +14 -0
  1788. office365/sharepoint/migrationcenter/agentgroupentity.py +24 -1
  1789. office365/sharepoint/migrationcenter/common/task.py +8 -1
  1790. office365/sharepoint/migrationcenter/common/task_entity_data.py +231 -0
  1791. office365/sharepoint/migrationcenter/common/teams/channel_resource.py +7 -0
  1792. office365/sharepoint/migrationcenter/common/teams/team_resource.py +8 -0
  1793. office365/sharepoint/migrationcenter/common/url_parser.py +37 -1
  1794. office365/sharepoint/migrationcenter/credentialentitydata.py +7 -0
  1795. office365/sharepoint/migrationcenter/device.py +7 -0
  1796. office365/sharepoint/migrationcenter/deviceentitydata.py +8 -0
  1797. office365/sharepoint/migrationcenter/service/credential.py +21 -0
  1798. office365/sharepoint/migrationcenter/service/services.py +77 -0
  1799. office365/sharepoint/mount/folder_info.py +34 -0
  1800. office365/sharepoint/mount/onedriveitemcollection.py +26 -1
  1801. office365/sharepoint/mount/placesinformation.py +18 -1
  1802. office365/sharepoint/multigeo/service/allowed_data_location.py +7 -0
  1803. office365/sharepoint/multigeo/service/cross_geo_user_placement_job.py +7 -0
  1804. office365/sharepoint/multigeo/service/df_deprecation_job.py +18 -0
  1805. office365/sharepoint/multigeo/service/geo_tenant_instance_information.py +7 -0
  1806. office365/sharepoint/multigeo/service/job_entity_data.py +21 -0
  1807. office365/sharepoint/multigeo/service/microfeedpostdefinitionmanager.py +91 -0
  1808. office365/sharepoint/multigeo/service/move_job_entity_data.py +111 -0
  1809. office365/sharepoint/multigeo/service/move_site_ro_state.py +18 -0
  1810. office365/sharepoint/multigeo/service/multigeoservicesbeta.py +55 -13
  1811. office365/sharepoint/multigeo/service/sbsitemovejob.py +26 -0
  1812. office365/sharepoint/multigeo/service/site_move_job_entity_data.py +75 -0
  1813. office365/sharepoint/multigeo/service/storage_quota.py +49 -0
  1814. office365/sharepoint/multigeo/service/tenant_information.py +40 -0
  1815. office365/sharepoint/multigeo/service/unified_group.py +35 -0
  1816. office365/sharepoint/multigeo/service/user_personal_site_location.py +30 -0
  1817. office365/sharepoint/multigeo/service/usermovejob.py +11 -0
  1818. office365/sharepoint/navigation/home_site_navigation_settings.py +6 -1
  1819. office365/sharepoint/navigation/nodes/creationinformation.py +2 -0
  1820. office365/sharepoint/navigation/nodes/node.py +22 -1
  1821. office365/sharepoint/navigation/quickaccessitemcollection.py +37 -1
  1822. office365/sharepoint/news/ngspdatacollection.py +24 -1
  1823. office365/sharepoint/ngsp_ai_settings.py +32 -0
  1824. office365/sharepoint/oauth/token_response.py +56 -0
  1825. office365/sharepoint/onboarding/job_entity_data.py +14 -0
  1826. office365/sharepoint/onboarding/tenantrename/job.py +7 -0
  1827. office365/sharepoint/onboarding/tenantrename/jobentitydata.py +10 -0
  1828. office365/sharepoint/permissions/irm/effective_settings.py +78 -0
  1829. office365/sharepoint/permissions/irm/file_settings.py +47 -0
  1830. office365/sharepoint/permissions/irm/settings.py +16 -0
  1831. office365/sharepoint/permissions/roles/definitions/definition.py +12 -0
  1832. office365/sharepoint/permissions/securable_object.py +42 -0
  1833. office365/sharepoint/portal/groups/site_manager.py +28 -4
  1834. office365/sharepoint/portal/home/page_context.py +4 -0
  1835. office365/sharepoint/portal/home/service_context.py +57 -0
  1836. office365/sharepoint/portal/organization_news.py +37 -0
  1837. office365/sharepoint/portal/theme_manager.py +17 -0
  1838. office365/sharepoint/portal/userprofiles/sharedwithme/document.py +267 -0
  1839. office365/sharepoint/portal/userprofiles/sharedwithme/item_collection.py +147 -0
  1840. office365/sharepoint/portal/webcontrols/recentdocument.py +7 -0
  1841. office365/sharepoint/principal/groups/group.py +11 -1
  1842. office365/sharepoint/publishing/amplify/clientamplifyanywhereresults.py +1 -1
  1843. office365/sharepoint/publishing/amplify/historyresult.py +1 -1
  1844. office365/sharepoint/publishing/amplify/result.py +1 -1
  1845. office365/sharepoint/publishing/announcements/data.py +2 -2
  1846. office365/sharepoint/publishing/approval/request.py +7 -0
  1847. office365/sharepoint/publishing/campaign/campaign.py +9 -0
  1848. office365/sharepoint/publishing/channelannouncement.py +3 -3
  1849. office365/sharepoint/publishing/customproperties/base.py +19 -0
  1850. office365/sharepoint/publishing/customproperties/bigint.py +9 -0
  1851. office365/sharepoint/publishing/diagnostics/page_result.py +2 -2
  1852. office365/sharepoint/publishing/embed_data_v1.py +7 -0
  1853. office365/sharepoint/publishing/highlights_info.py +14 -0
  1854. office365/sharepoint/publishing/pagecopywithassets/response.py +79 -0
  1855. office365/sharepoint/publishing/pages/_3d/__init__.py +0 -0
  1856. office365/sharepoint/publishing/pages/campaign/publication.py +104 -16
  1857. office365/sharepoint/publishing/pages/canvas.py +81 -0
  1858. office365/sharepoint/publishing/pages/collection.py +39 -5
  1859. office365/sharepoint/publishing/pages/metadata.py +119 -0
  1860. office365/sharepoint/publishing/pages/move_response.py +64 -0
  1861. office365/sharepoint/publishing/pages/page.py +6 -0
  1862. office365/sharepoint/publishing/pages/reposts/metadata.py +30 -0
  1863. office365/sharepoint/publishing/person/magazineuserprofile.py +3 -3
  1864. office365/sharepoint/publishing/personmagazine/userprofiledata.py +1 -1
  1865. office365/sharepoint/publishing/point/site_status.py +25 -0
  1866. office365/sharepoint/publishing/point/user.py +7 -0
  1867. office365/sharepoint/publishing/profiledata.py +1 -1
  1868. office365/sharepoint/publishing/profilefullproperties.py +3 -3
  1869. office365/sharepoint/publishing/profilevieweditpolicies.py +24 -24
  1870. office365/sharepoint/publishing/publicationmetadata.py +2 -2
  1871. office365/sharepoint/publishing/ruleerrordetails.py +1 -1
  1872. office365/sharepoint/publishing/ruleresult.py +1 -1
  1873. office365/sharepoint/publishing/search.py +8 -0
  1874. office365/sharepoint/publishing/sitepagstreamdata.py +1 -1
  1875. office365/sharepoint/publishing/spotlightchannel.py +19 -1
  1876. office365/sharepoint/publishing/spotlightvideo.py +19 -1
  1877. office365/sharepoint/publishing/spstatusresponse.py +1 -1
  1878. office365/sharepoint/publishing/statusresponse.py +2 -4
  1879. office365/sharepoint/publishing/user_info.py +8 -0
  1880. office365/sharepoint/publishing/validations/prepublishvalidationserrorcodesforvivaengage.py +1 -1
  1881. office365/sharepoint/publishing/video/channel.py +7 -0
  1882. office365/sharepoint/publishing/video/item.py +11 -2
  1883. office365/sharepoint/publishing/video/service_manager.py +66 -2
  1884. office365/sharepoint/pushnotifications/subscriber.py +7 -0
  1885. office365/sharepoint/recyclebin/item.py +16 -0
  1886. office365/sharepoint/recyclebin/item_collection.py +1 -1
  1887. office365/sharepoint/request.py +67 -8
  1888. office365/sharepoint/request_context.py +41 -2
  1889. office365/sharepoint/role_definition_creation_information.py +14 -0
  1890. office365/sharepoint/search/relevant_results.py +7 -0
  1891. office365/sharepoint/search/result.py +6 -0
  1892. office365/sharepoint/search/searchresultitemcollection.py +86 -1
  1893. office365/sharepoint/search/semantic/enqueueproxy.py +68 -1
  1894. office365/sharepoint/search/semantic/itemcollection.py +35 -1
  1895. office365/sharepoint/search/service.py +7 -0
  1896. office365/sharepoint/search/simple_data_row.py +8 -0
  1897. office365/sharepoint/securestore/target_application.py +17 -0
  1898. office365/sharepoint/securestore/target_application_field.py +19 -0
  1899. office365/sharepoint/sharing/information.py +299 -1
  1900. office365/sharepoint/sharing/internal/sharing_restrictions.py +30 -0
  1901. office365/sharepoint/sharing/object_sharing_information.py +35 -0
  1902. office365/sharepoint/sharing/object_sharing_information_user.py +67 -0
  1903. office365/sharepoint/sharing/object_sharing_settings.py +143 -0
  1904. office365/sharepoint/sharing/permission_information.py +22 -0
  1905. office365/sharepoint/sharing/picker_settings.py +24 -0
  1906. office365/sharepoint/sharing/principal_info.py +25 -0
  1907. office365/sharepoint/sharing/shared_document_info.py +161 -0
  1908. office365/sharepoint/sharing/sharepoint_sharing_settings.py +85 -0
  1909. office365/sharepoint/sitedesigns/design_package_menu_contents.py +20 -0
  1910. office365/sharepoint/sitedesigns/principal.py +19 -0
  1911. office365/sharepoint/sitedesigns/run.py +43 -0
  1912. office365/sharepoint/sitedesigns/userfirstrun.py +53 -0
  1913. office365/sharepoint/sites/home/site.py +279 -0
  1914. office365/sharepoint/sites/language_collection.py +18 -0
  1915. office365/sharepoint/sites/migration/job_status.py +15 -0
  1916. office365/sharepoint/sites/site.py +8 -2
  1917. office365/sharepoint/sites/team_site_data.py +45 -0
  1918. office365/sharepoint/sites/version_policy_manager.py +35 -0
  1919. office365/sharepoint/smartcache/item.py +106 -0
  1920. office365/sharepoint/social/feed/manager.py +208 -1
  1921. office365/sharepoint/social/following/manager.py +82 -1
  1922. office365/sharepoint/social/following/rest_manager.py +186 -0
  1923. office365/sharepoint/social/rest_actor.py +25 -0
  1924. office365/sharepoint/social/rest_thread.py +39 -1
  1925. office365/sharepoint/sp_playlist_subscriber.py +12 -0
  1926. office365/sharepoint/taxonomy/create_xml_parameters.py +5 -5
  1927. office365/sharepoint/taxonomy/field.py +20 -19
  1928. office365/sharepoint/taxonomy/field_value_col.py +21 -0
  1929. office365/sharepoint/taxonomy/groups/collection.py +19 -2
  1930. office365/sharepoint/teams/channel_manager.py +272 -0
  1931. office365/sharepoint/tenant/administration/app_service_principal_public.py +46 -0
  1932. office365/sharepoint/tenant/administration/audit/data.py +4 -1
  1933. office365/sharepoint/tenant/administration/consumer_quota_info_api.py +7 -0
  1934. office365/sharepoint/tenant/administration/container/filterorder.py +1 -1
  1935. office365/sharepoint/tenant/administration/container/properties.py +1 -1
  1936. office365/sharepoint/tenant/administration/container/type/configurationproperties.py +1 -1
  1937. office365/sharepoint/tenant/administration/copilot/reportrow.py +1 -1
  1938. office365/sharepoint/tenant/administration/dataset_metadata_request_info.py +4 -1
  1939. office365/sharepoint/tenant/administration/dataset_metadata_response.py +9 -2
  1940. office365/sharepoint/tenant/administration/endpoints.py +5 -0
  1941. office365/sharepoint/tenant/administration/groups/group.py +130 -0
  1942. office365/sharepoint/tenant/administration/hubsites/hub_site.py +62 -0
  1943. office365/sharepoint/tenant/administration/hubsites/properties.py +53 -0
  1944. office365/sharepoint/tenant/administration/internal/aad/permission_grant.py +39 -0
  1945. office365/sharepoint/tenant/administration/internal/appservice/permission_request.py +7 -0
  1946. office365/sharepoint/tenant/administration/internal/appservice/principal.py +87 -2
  1947. office365/sharepoint/tenant/administration/licensed_onedrives_summary_metrics.py +16 -0
  1948. office365/sharepoint/tenant/administration/malware/file.py +35 -0
  1949. office365/sharepoint/tenant/administration/onedrives_count_and_size.py +15 -0
  1950. office365/sharepoint/tenant/administration/onedrives_summary_metrics.py +23 -0
  1951. office365/sharepoint/tenant/administration/onedrives_summary_metrics_by_unlicensed_reason.py +25 -0
  1952. office365/sharepoint/tenant/administration/owner_not_in_entra_onedrives_summary_metrics.py +22 -0
  1953. office365/sharepoint/tenant/administration/policies/content_security_configuration.py +67 -0
  1954. office365/sharepoint/tenant/administration/settings/service.py +240 -0
  1955. office365/sharepoint/tenant/administration/siteadministratorsfieldsdata.py +1 -1
  1956. office365/sharepoint/tenant/administration/sites/properties.py +21 -2
  1957. office365/sharepoint/tenant/administration/spcontenteventscustomemailproperty.py +1 -1
  1958. office365/sharepoint/tenant/administration/spdatagovernanceinsightresponse.py +6 -6
  1959. office365/sharepoint/tenant/administration/spofileversionfiletypepolicysettings.py +1 -1
  1960. office365/sharepoint/tenant/administration/spom365agentinsightsm365agentsonsitesdetails.py +4 -0
  1961. office365/sharepoint/tenant/administration/storage_usage_metrics_snapshot.py +8 -1
  1962. office365/sharepoint/tenant/administration/syntex/applicationproperties.py +19 -3
  1963. office365/sharepoint/tenant/administration/syntex/getmodifiedlistresponse.py +1 -1
  1964. office365/sharepoint/tenant/administration/tenant.py +5931 -34
  1965. office365/sharepoint/tenant/administration/theme_properties.py +24 -0
  1966. office365/sharepoint/tenant/administration/unlicensed_onedrives_summary_metrics.py +20 -0
  1967. office365/sharepoint/tenant/apps/instance.py +20 -0
  1968. office365/sharepoint/tenant/apps/user_experience_state.py +25 -0
  1969. office365/sharepoint/tenant/insights/reports/metadata.py +8 -0
  1970. office365/sharepoint/tenant/insights/reports/report.py +23 -0
  1971. office365/sharepoint/tenant/management/externalusers/results/remove.py +13 -0
  1972. office365/sharepoint/tenant/management/externalusers/results/session_revocation.py +9 -0
  1973. office365/sharepoint/tenant/management/office365_tenant.py +99 -8
  1974. office365/sharepoint/tenantadministration/sp_data_governance_insight_rest_api_client.py +475 -0
  1975. office365/sharepoint/tenantadministration/sp_data_governance_sar_rest_api_client.py +88 -0
  1976. office365/sharepoint/tenantadministration/spo_copilot_agent_insights_rest_api_client.py +170 -0
  1977. office365/sharepoint/tenantadministration/spo_file_archive_policy_info.py +4 -1
  1978. office365/sharepoint/tenantadministration/spo_restricted_content_discoverability_client.py +90 -0
  1979. office365/sharepoint/tenantadministration/spom365_agent_insights_rest_api_client.py +166 -0
  1980. office365/sharepoint/tenantadministration/spom365_agent_insights_top20_agents_details.py +11 -0
  1981. office365/sharepoint/tenantmanagement/group_creation_params.py +32 -0
  1982. office365/sharepoint/token.py +7 -0
  1983. office365/sharepoint/user_ai_tokens.py +56 -0
  1984. office365/sharepoint/user_photos.py +32 -0
  1985. office365/sharepoint/usercustomactions/action.py +109 -1
  1986. office365/sharepoint/userprofiles/followed_content.py +164 -0
  1987. office365/sharepoint/userprofiles/my_site_links.py +22 -0
  1988. office365/sharepoint/userprofiles/people_manager.py +68 -0
  1989. office365/sharepoint/userprofiles/peoplecollection.py +40 -1
  1990. office365/sharepoint/userprofiles/person_properties.py +22 -0
  1991. office365/sharepoint/userprofiles/personal_cache.py +246 -1
  1992. office365/sharepoint/userprofiles/personallistsproxy.py +33 -1
  1993. office365/sharepoint/userprofiles/profile_loader.py +16 -0
  1994. office365/sharepoint/userprofiles/user_profile.py +42 -0
  1995. office365/sharepoint/utilities/file_handler_wopi_properties.py +24 -0
  1996. office365/sharepoint/utilities/move_copy_util.py +44 -3
  1997. office365/sharepoint/utilities/upload_status.py +8 -0
  1998. office365/sharepoint/views/collection.py +135 -14
  1999. office365/sharepoint/views/field_collection.py +3 -0
  2000. office365/sharepoint/views/view.py +113 -0
  2001. office365/sharepoint/viva/app_configuration.py +34 -1
  2002. office365/sharepoint/viva/employee_engagement.py +149 -1
  2003. office365/sharepoint/viva/employee_experience_controller.py +97 -1
  2004. office365/sharepoint/viva/knowledgehub.py +37 -1
  2005. office365/sharepoint/viva/resource_get_endpoint.py +15 -0
  2006. office365/sharepoint/viva/visualizationappsynchronizationresult.py +18 -1
  2007. office365/sharepoint/webhooks/subscription.py +2 -1
  2008. office365/sharepoint/webhooks/subscription_information.py +2 -1
  2009. office365/sharepoint/webparts/client/webpart.py +15 -0
  2010. office365/sharepoint/webparts/webpart.py +12 -0
  2011. office365/sharepoint/webs/apptile.py +12 -0
  2012. office365/sharepoint/webs/dataleakage_prevention_status_info.py +24 -0
  2013. office365/sharepoint/webs/information.py +7 -0
  2014. office365/sharepoint/webs/modernize_homepage_result.py +7 -0
  2015. office365/sharepoint/webs/multilingual_settings.py +19 -1
  2016. office365/sharepoint/webs/regional_settings.py +27 -0
  2017. office365/sharepoint/webs/remote_web.py +19 -0
  2018. office365/sharepoint/webs/templates/template.py +7 -0
  2019. office365/sharepoint/webs/theme_info.py +14 -0
  2020. office365/sharepoint/webs/time_zone.py +15 -0
  2021. office365/sharepoint/webs/web.py +1167 -0
  2022. office365/sharepoint/workflow/associations/association.py +5 -0
  2023. office365/sharepoint/workflow/instances/instance.py +41 -0
  2024. office365/sharepoint/workflow/sp_workflow_task.py +618 -0
  2025. office365/sharepoint/workflow/template.py +8 -0
  2026. office365/subscriptions/change_notification.py +17 -0
  2027. office365/subscriptions/change_notification_collection.py +4 -0
  2028. office365/subscriptions/changetype.py +4 -0
  2029. office365/subscriptions/collection.py +10 -6
  2030. office365/subscriptions/subscription.py +57 -1
  2031. office365/teams/admin_root.py +35 -0
  2032. office365/teams/administration/number_assignment.py +74 -1
  2033. office365/teams/administration/telephone_number_long_running_operation.py +26 -3
  2034. office365/teams/administration/telephone_number_management_root.py +20 -1
  2035. office365/teams/administration/user_configuration.py +17 -3
  2036. office365/teams/app_settings.py +21 -0
  2037. office365/teams/apps/app.py +11 -0
  2038. office365/teams/apps/definition.py +38 -0
  2039. office365/teams/apps/installation.py +18 -0
  2040. office365/teams/assignment_type.py +13 -0
  2041. office365/teams/channels/channel.py +59 -0
  2042. office365/teams/channels/collection.py +62 -2
  2043. office365/teams/channels/layout_type.py +13 -0
  2044. office365/teams/channels/shared_team_info.py +6 -0
  2045. office365/teams/chats/chat.py +40 -1
  2046. office365/teams/chats/collection.py +11 -3
  2047. office365/teams/chats/deleted.py +6 -0
  2048. office365/teams/chats/messages/info.py +42 -0
  2049. office365/teams/chats/messages/message.py +39 -1
  2050. office365/teams/chats/pinned_chat_message_info.py +22 -0
  2051. office365/teams/chats/targeted_chat_message.py +21 -0
  2052. office365/teams/collection.py +76 -11
  2053. office365/teams/community_privacy.py +13 -0
  2054. office365/teams/info.py +11 -0
  2055. office365/teams/members/aad_user_conversation.py +16 -0
  2056. office365/teams/members/conversation.py +17 -0
  2057. office365/teams/members/conversation_collection.py +19 -7
  2058. office365/teams/migration_mode.py +13 -0
  2059. office365/teams/operations/async_operation.py +102 -1
  2060. office365/teams/operations/asyncoperationtype.py +10 -15
  2061. office365/teams/policy_assignment.py +23 -0
  2062. office365/teams/schedule/change_request.py +47 -1
  2063. office365/teams/schedule/day_note.py +34 -0
  2064. office365/teams/schedule/groups/group.py +16 -0
  2065. office365/teams/schedule/open_shift.py +38 -0
  2066. office365/teams/schedule/schedule.py +165 -0
  2067. office365/teams/schedule/shifts/open/change_request.py +13 -0
  2068. office365/teams/schedule/shifts/shift.py +23 -0
  2069. office365/teams/schedule/swap_shifts_change_request.py +16 -0
  2070. office365/teams/schedule/time_card.py +74 -0
  2071. office365/teams/schedule/time_off.py +38 -0
  2072. office365/teams/schedule/time_off_reason.py +35 -0
  2073. office365/teams/schedule/time_off_request.py +27 -0
  2074. office365/teams/tabs/tab.py +18 -0
  2075. office365/teams/team.py +2 -2
  2076. office365/teams/teamwork/shiftmanagement/user_solution_root.py +22 -0
  2077. office365/teams/teamwork/shiftmanagement/working_time_schedule.py +9 -0
  2078. office365/teams/teamwork/tags/member.py +16 -0
  2079. office365/teams/telephone_number_long_running_operation_details.py +19 -0
  2080. office365/teams/viva/community.py +42 -1
  2081. office365/teams/viva/employee_experience.py +34 -0
  2082. office365/teams/viva/employee_experience_user.py +19 -0
  2083. office365/teams/viva/engagement/conversation.py +33 -1
  2084. office365/teams/viva/engagement/conversation_message.py +112 -0
  2085. office365/teams/viva/engagement/conversation_message_reaction.py +41 -0
  2086. office365/teams/viva/learning/content.py +111 -0
  2087. office365/teams/viva/learning/courses/activity.py +49 -0
  2088. office365/teams/viva/learning/courses/self_initiated.py +14 -0
  2089. office365/teams/viva/learning/provider.py +60 -0
  2090. office365/teams/work.py +43 -1
  2091. office365/teams/workforce_integration.py +69 -0
  2092. office365/teams/workforce_integration_encryption.py +18 -0
  2093. office365/todo/attachments/base.py +11 -0
  2094. office365/todo/attachments/session.py +4 -0
  2095. office365/todo/attachments/task_file.py +2 -5
  2096. office365/todo/attachments/type.py +7 -10
  2097. office365/todo/linked_resource.py +6 -0
  2098. office365/todo/tasks/collection.py +45 -0
  2099. office365/todo/tasks/lists/collection.py +1 -0
  2100. office365/todo/tasks/task.py +1 -1
  2101. office365_rest_python_client-3.3.0.dist-info/METADATA +635 -0
  2102. office365_rest_python_client-3.3.0.dist-info/RECORD +6789 -0
  2103. office365/directory/identitygovernance/workflow.py +0 -106
  2104. office365/directory/identitygovernance/workflow_setting.py +0 -15
  2105. office365/directory/identitygovernance/workflow_version.py +0 -16
  2106. office365/directory/security/account.py +0 -14
  2107. office365/directory/security/ai_agent_evidence.py +0 -16
  2108. office365/directory/security/alerts/alert.py +0 -205
  2109. office365/directory/security/alerts/blob_container_evidence.py +0 -17
  2110. office365/directory/security/alerts/blob_evidence.py +0 -21
  2111. office365/directory/security/alerts/collection.py +0 -49
  2112. office365/directory/security/alerts/evidence.py +0 -28
  2113. office365/directory/security/alerts/file_security_state.py +0 -5
  2114. office365/directory/security/alerts/history_state.py +0 -16
  2115. office365/directory/security/alerts/malware_evidence.py +0 -22
  2116. office365/directory/security/alerts/process_evidence.py +0 -27
  2117. office365/directory/security/alerts/sas_token_evidence.py +0 -26
  2118. office365/directory/security/analyzed_message_evidence.py +0 -34
  2119. office365/directory/security/article.py +0 -56
  2120. office365/directory/security/attacksimulations/automation.py +0 -30
  2121. office365/directory/security/attacksimulations/automation_run.py +0 -12
  2122. office365/directory/security/attacksimulations/event.py +0 -5
  2123. office365/directory/security/attacksimulations/landing_page.py +0 -5
  2124. office365/directory/security/attacksimulations/operations/operation.py +0 -5
  2125. office365/directory/security/attacksimulations/repeat_offender.py +0 -17
  2126. office365/directory/security/attacksimulations/reports/overview.py +0 -5
  2127. office365/directory/security/attacksimulations/reports/report.py +0 -25
  2128. office365/directory/security/attacksimulations/root.py +0 -67
  2129. office365/directory/security/attacksimulations/simulation.py +0 -23
  2130. office365/directory/security/attacksimulations/user.py +0 -16
  2131. office365/directory/security/attacksimulations/user_coverage.py +0 -10
  2132. office365/directory/security/attacksimulations/users/details.py +0 -20
  2133. office365/directory/security/attribute_definition.py +0 -37
  2134. office365/directory/security/attribute_mapping.py +0 -23
  2135. office365/directory/security/attribute_mapping_source.py +0 -24
  2136. office365/directory/security/audit/audit_data.py +0 -15
  2137. office365/directory/security/audit/whois_record.py +0 -22
  2138. office365/directory/security/cases/case.py +0 -48
  2139. office365/directory/security/cases/ediscovery.py +0 -99
  2140. office365/directory/security/cases/operation.py +0 -10
  2141. office365/directory/security/cases/root.py +0 -21
  2142. office365/directory/security/category_template.py +0 -9
  2143. office365/directory/security/cloud_application_evidence.py +0 -20
  2144. office365/directory/security/cloud_logon_session_evidence.py +0 -24
  2145. office365/directory/security/collaboration/analyzed_email.py +0 -133
  2146. office365/directory/security/collaboration/analyzed_email_attachment.py +0 -22
  2147. office365/directory/security/collaboration/analyzed_email_delivery_detail.py +0 -15
  2148. office365/directory/security/collaboration/analyzed_email_url.py +0 -18
  2149. office365/directory/security/collaboration/collaboration_root.py +0 -22
  2150. office365/directory/security/compromise_indicator.py +0 -14
  2151. office365/directory/security/container_evidence.py +0 -23
  2152. office365/directory/security/container_image_evidence.py +0 -17
  2153. office365/directory/security/cvss_summary.py +0 -15
  2154. office365/directory/security/data_source_container.py +0 -44
  2155. office365/directory/security/datasecurity/classifcation_error_base.py +0 -18
  2156. office365/directory/security/datasecurity/classification_error.py +0 -12
  2157. office365/directory/security/datasecurity/compute_rights_and_inheritance_result.py +0 -17
  2158. office365/directory/security/datasecurity/governance.py +0 -17
  2159. office365/directory/security/datasecurity/policy_scope_base.py +0 -26
  2160. office365/directory/security/datasecurity/policy_tenant_scope.py +0 -15
  2161. office365/directory/security/datasecurity/policy_tip_action.py +0 -16
  2162. office365/directory/security/datasecurity/process_content_batch_request.py +0 -17
  2163. office365/directory/security/datasecurity/process_content_request.py +0 -24
  2164. office365/directory/security/datasecurity/process_content_response.py +0 -24
  2165. office365/directory/security/datasecurity/process_content_responses.py +0 -16
  2166. office365/directory/security/datasecurity/process_file_metadata.py +0 -16
  2167. office365/directory/security/datasecurity/sensitivity_label.py +0 -31
  2168. office365/directory/security/detonation_details.py +0 -34
  2169. office365/directory/security/device_evidence.py +0 -47
  2170. office365/directory/security/dns_evidence.py +0 -19
  2171. office365/directory/security/ediscovery/case_member.py +0 -21
  2172. office365/directory/security/ediscovery/custodian.py +0 -22
  2173. office365/directory/security/ediscovery/ediscovery_case_settings.py +0 -21
  2174. office365/directory/security/ediscovery/ediscovery_estimate_operation.py +0 -54
  2175. office365/directory/security/ediscovery/ediscovery_export_operation.py +0 -50
  2176. office365/directory/security/ediscovery/ediscovery_index_operation.py +0 -7
  2177. office365/directory/security/ediscovery/ediscovery_review_tag.py +0 -34
  2178. office365/directory/security/ediscovery/noncustodial_data_source.py +0 -18
  2179. office365/directory/security/ediscovery/operations/add_to_review_set.py +0 -52
  2180. office365/directory/security/ediscovery/operations/hold.py +0 -7
  2181. office365/directory/security/ediscovery/operations/hold_policy_sync.py +0 -9
  2182. office365/directory/security/ediscovery/operations/purge_data.py +0 -9
  2183. office365/directory/security/ediscovery/operations/search_export.py +0 -62
  2184. office365/directory/security/ediscovery/operations/tag.py +0 -7
  2185. office365/directory/security/ediscovery/review_set.py +0 -22
  2186. office365/directory/security/ediscovery/review_set_query.py +0 -7
  2187. office365/directory/security/ediscovery/search.py +0 -56
  2188. office365/directory/security/event_propagation_result.py +0 -18
  2189. office365/directory/security/event_query.py +0 -14
  2190. office365/directory/security/expression_input_object.py +0 -20
  2191. office365/directory/security/file_evidence.py +0 -18
  2192. office365/directory/security/file_hash.py +0 -16
  2193. office365/directory/security/file_hash_evidence.py +0 -16
  2194. office365/directory/security/file_plan_applied_category.py +0 -15
  2195. office365/directory/security/filter.py +0 -22
  2196. office365/directory/security/filter_clause.py +0 -17
  2197. office365/directory/security/filter_group.py +0 -17
  2198. office365/directory/security/formatted_content.py +0 -16
  2199. office365/directory/security/google_cloud_resource_evidence.py +0 -21
  2200. office365/directory/security/health_issue.py +0 -81
  2201. office365/directory/security/hunting_query_results.py +0 -24
  2202. office365/directory/security/identity_accounts.py +0 -36
  2203. office365/directory/security/incidents/incident.py +0 -59
  2204. office365/directory/security/indicator.py +0 -16
  2205. office365/directory/security/invoke_action_result.py +0 -15
  2206. office365/directory/security/io_t_device_evidence.py +0 -46
  2207. office365/directory/security/ip_address.py +0 -26
  2208. office365/directory/security/ip_evidence.py +0 -19
  2209. office365/directory/security/kubernetes_cluster_evidence.py +0 -20
  2210. office365/directory/security/kubernetes_controller_evidence.py +0 -19
  2211. office365/directory/security/kubernetes_namespace_evidence.py +0 -18
  2212. office365/directory/security/kubernetes_pod_evidence.py +0 -35
  2213. office365/directory/security/kubernetes_secret_evidence.py +0 -17
  2214. office365/directory/security/kubernetes_service_account_evidence.py +0 -16
  2215. office365/directory/security/kubernetes_service_evidence.py +0 -29
  2216. office365/directory/security/kubernetes_service_port.py +0 -20
  2217. office365/directory/security/labels/retention/collection.py +0 -68
  2218. office365/directory/security/labels/retention/duration_in_days.py +0 -16
  2219. office365/directory/security/labels/retention/fileplan_descriptor.py +0 -52
  2220. office365/directory/security/labels/retention/label.py +0 -158
  2221. office365/directory/security/labels/root.py +0 -83
  2222. office365/directory/security/mailbox_configuration_evidence.py +0 -19
  2223. office365/directory/security/mailbox_evidence.py +0 -18
  2224. office365/directory/security/network_connection_evidence.py +0 -18
  2225. office365/directory/security/nic_evidence.py +0 -18
  2226. office365/directory/security/object_definition.py +0 -25
  2227. office365/directory/security/object_mapping.py +0 -26
  2228. office365/directory/security/parse_expression_response.py +0 -19
  2229. office365/directory/security/passive_dns_record.py +0 -47
  2230. office365/directory/security/processing_error.py +0 -15
  2231. office365/directory/security/retention_event.py +0 -63
  2232. office365/directory/security/retention_event_status.py +0 -15
  2233. office365/directory/security/scorecontrol/profile.py +0 -23
  2234. office365/directory/security/search/data_source.py +0 -34
  2235. office365/directory/security/security.py +0 -157
  2236. office365/directory/security/sensor.py +0 -72
  2237. office365/directory/security/sensor_candidate_activation_configuration.py +0 -9
  2238. office365/directory/security/sensor_settings.py +0 -20
  2239. office365/directory/security/service_principal_evidence.py +0 -18
  2240. office365/directory/security/settings_container.py +0 -19
  2241. office365/directory/security/ssl_certificate.py +0 -59
  2242. office365/directory/security/string_key_attribute_mapping_source_value_pair.py +0 -16
  2243. office365/directory/security/subdomain.py +0 -26
  2244. office365/directory/security/subjectrightsrequests/identity.py +0 -21
  2245. office365/directory/security/subjectrightsrequests/request.py +0 -6
  2246. office365/directory/security/teams_message_evidence.py +0 -42
  2247. office365/directory/security/threatintelligence/host.py +0 -138
  2248. office365/directory/security/threatintelligence/host_component.py +0 -46
  2249. office365/directory/security/threatintelligence/host_cookie.py +0 -41
  2250. office365/directory/security/threatintelligence/host_logon_session_evidence.py +0 -21
  2251. office365/directory/security/threatintelligence/host_pair.py +0 -41
  2252. office365/directory/security/threatintelligence/host_port.py +0 -57
  2253. office365/directory/security/threatintelligence/host_port_component.py +0 -19
  2254. office365/directory/security/threatintelligence/host_reputation.py +0 -16
  2255. office365/directory/security/threatintelligence/host_reputation_rule.py +0 -16
  2256. office365/directory/security/threatintelligence/host_ssl_certificate.py +0 -43
  2257. office365/directory/security/threatintelligence/host_tracker.py +0 -41
  2258. office365/directory/security/threatintelligence/profile.py +0 -56
  2259. office365/directory/security/threatintelligence/threat_intelligence.py +0 -193
  2260. office365/directory/security/timeline_event.py +0 -19
  2261. office365/directory/security/triggers/root.py +0 -9
  2262. office365/directory/security/triggertypes/collection.py +0 -9
  2263. office365/directory/security/triggertypes/root.py +0 -24
  2264. office365/directory/security/unified_group_source.py +0 -22
  2265. office365/directory/security/user_account.py +0 -27
  2266. office365/directory/security/user_evidence.py +0 -17
  2267. office365/directory/security/user_source.py +0 -27
  2268. office365/directory/security/vm_metadata.py +0 -16
  2269. office365/directory/security/vulnerability.py +0 -74
  2270. office365/directory/security/whois_base_record.py +0 -59
  2271. office365/externalconnectors/binary_operator.py +0 -12
  2272. office365/externalconnectors/connection_operation_status.py +0 -15
  2273. office365/externalconnectors/connection_state.py +0 -15
  2274. office365/externalconnectors/external_activity_type.py +0 -15
  2275. office365/externalconnectors/external_item_content_type.py +0 -13
  2276. office365/intune/brand.py +0 -21
  2277. office365/intune/instance_resource_access.py +0 -19
  2278. office365/intune/provisioned_plan.py +0 -27
  2279. office365/onenote/operations/operation.py +0 -35
  2280. office365/partners/billing/billing_period.py +0 -13
  2281. office365/partners/billing/billing_reconciliation.py +0 -26
  2282. office365/sharepoint/contentcenter/machinelearning/publications/result.py +0 -9
  2283. office365/sharepoint/customactions/action.py +0 -36
  2284. office365_rest_python_client-3.2.0.dist-info/METADATA +0 -605
  2285. office365_rest_python_client-3.2.0.dist-info/RECORD +0 -5743
  2286. /office365/{directory/security → common}/__init__.py +0 -0
  2287. /office365/{intune → directory/applications}/resource_permission.py +0 -0
  2288. /office365/directory/identitygovernance/{workflow_subject.py → workflow/subject.py} +0 -0
  2289. /office365/{directory/security/alerts → intune/devices/configurations/macos}/__init__.py +0 -0
  2290. /office365/{directory/security/attacksimulations → intune/devices/configurations/windows/_10}/__init__.py +0 -0
  2291. /office365/intune/devices/configurations/windows/{10 → _10}/editiontype.py +0 -0
  2292. /office365/intune/{enrollment_state.py → devices/management/enrollment_state.py} +0 -0
  2293. /office365/{directory/security/attacksimulations/operations → intune/devices/management/mobileapps/macos}/__init__.py +0 -0
  2294. /office365/intune/{package_status.py → devices/management/package_status.py} +0 -0
  2295. /office365/intune/{process_integrity_level.py → devices/management/process_integrity_level.py} +0 -0
  2296. /office365/{directory/security/attacksimulations/reports → planner/internal}/__init__.py +0 -0
  2297. /office365/{directory/security/attacksimulations/users → planner/internal/paths}/__init__.py +0 -0
  2298. /office365/runtime/{decorators.py → deprecated.py} +0 -0
  2299. /office365/{directory/security/audit → security}/__init__.py +0 -0
  2300. /office365/{directory/security → security}/a365_ai_inference_call.py +0 -0
  2301. /office365/{directory/security → security}/a365_ai_invoke_agent.py +0 -0
  2302. /office365/{directory/security → security}/a365_ai_run_summary.py +0 -0
  2303. /office365/{directory/security → security}/a365_span_outputs.py +0 -0
  2304. /office365/{directory/security → security}/action.py +0 -0
  2305. /office365/{directory/security → security}/aed_audit_record.py +0 -0
  2306. /office365/{directory/security → security}/agent_admin_activity_record.py +0 -0
  2307. /office365/{directory/security → security}/agent_setting_admin_activity.py +0 -0
  2308. /office365/{directory/security → security}/ai_agent_platform.py +0 -0
  2309. /office365/{directory/security → security}/aip_discover.py +0 -0
  2310. /office365/{directory/security → security}/aip_file_deleted.py +0 -0
  2311. /office365/{directory/security → security}/aip_heart_beat.py +0 -0
  2312. /office365/{directory/security → security}/aip_protection_action_log_request.py +0 -0
  2313. /office365/{directory/security → security}/aip_scanner_discover_event.py +0 -0
  2314. /office365/{directory/security → security}/aip_sensitivity_label_action_log_request.py +0 -0
  2315. /office365/{directory/security → security}/air_admin_action_investigation_data.py +0 -0
  2316. /office365/{directory/security → security}/air_investigation_data.py +0 -0
  2317. /office365/{directory/security/cases → security/alerts}/__init__.py +0 -0
  2318. /office365/{directory/security → security}/alerts/active_directory_domain_evidence.py +0 -0
  2319. /office365/{directory/security → security}/alerts/antispamteamsdirection.py +0 -0
  2320. /office365/{directory/security → security}/alerts/azure_resource_evidence.py +0 -0
  2321. /office365/{directory/security → security}/alerts/classification.py +0 -0
  2322. /office365/{directory/security → security}/alerts/comment.py +0 -0
  2323. /office365/{directory/security → security}/alerts/connection_direction.py +0 -0
  2324. /office365/{directory/security → security}/alerts/connectionstatus.py +0 -0
  2325. /office365/{directory/security → security}/alerts/defenderavstatus.py +0 -0
  2326. /office365/{directory/security → security}/alerts/detectionsource.py +0 -0
  2327. /office365/{directory/security → security}/alerts/detectionstatus.py +0 -0
  2328. /office365/{directory/security → security}/alerts/determination.py +0 -0
  2329. /office365/{directory/security → security}/alerts/emailrole.py +0 -0
  2330. /office365/{directory/security → security}/alerts/evidenceremediationstatus.py +0 -0
  2331. /office365/{directory/security → security}/alerts/evidencerole.py +0 -0
  2332. /office365/{directory/security → security}/alerts/evidenceverdict.py +0 -0
  2333. /office365/{directory/security → security}/alerts/feedback.py +0 -0
  2334. /office365/{directory/security → security}/alerts/googlecloudlocationtype.py +0 -0
  2335. /office365/{directory/security → security}/alerts/iotdeviceimportancetype.py +0 -0
  2336. /office365/{directory/security → security}/alerts/kubernetesplatform.py +0 -0
  2337. /office365/{directory/security → security}/alerts/kubernetesservicetype.py +0 -0
  2338. /office365/{directory/security → security}/alerts/logontype.py +0 -0
  2339. /office365/{directory/security → security}/alerts/registryhive.py +0 -0
  2340. /office365/{directory/security → security}/alerts/registryoperation.py +0 -0
  2341. /office365/{directory/security → security}/alerts/registryvaluetype.py +0 -0
  2342. /office365/{directory/security → security}/alerts/securitynetworkprotocol.py +0 -0
  2343. /office365/{directory/security → security}/alerts/securityresourcetype.py +0 -0
  2344. /office365/{directory/security → security}/alerts/severity.py +0 -0
  2345. /office365/{directory/security → security}/alerts/status.py +0 -0
  2346. /office365/{directory/security → security}/alerts/useraccountsecuritytype.py +0 -0
  2347. /office365/{directory/security → security}/amazon_resource_evidence.py +0 -0
  2348. /office365/{directory/security → security}/antispam_directionality.py +0 -0
  2349. /office365/{directory/security → security}/artifact.py +0 -0
  2350. /office365/{directory/security/collaboration → security/attacksimulations}/__init__.py +0 -0
  2351. /office365/{directory/security → security}/attacksimulations/account_target_content_type.py +0 -0
  2352. /office365/{directory/security → security}/attacksimulations/clicksource.py +0 -0
  2353. /office365/{directory/security → security}/attacksimulations/endusernotificationpreference.py +0 -0
  2354. /office365/{directory/security → security}/attacksimulations/endusernotificationsettingtype.py +0 -0
  2355. /office365/{directory/security → security}/attacksimulations/endusernotificationtype.py +0 -0
  2356. /office365/{directory/security → security}/attacksimulations/notificationdeliveryfrequency.py +0 -0
  2357. /office365/{directory/security → security}/attacksimulations/notificationdeliverypreference.py +0 -0
  2358. /office365/{directory/security → security}/attacksimulations/oauthappscope.py +0 -0
  2359. /office365/{directory/security/datasecurity → security/attacksimulations/operations}/__init__.py +0 -0
  2360. /office365/{directory/security → security}/attacksimulations/operations/type.py +0 -0
  2361. /office365/{directory/security → security}/attacksimulations/payloadbrand.py +0 -0
  2362. /office365/{directory/security → security}/attacksimulations/payloadcomplexity.py +0 -0
  2363. /office365/{directory/security → security}/attacksimulations/payloaddeliveryplatform.py +0 -0
  2364. /office365/{directory/security → security}/attacksimulations/payloadindustry.py +0 -0
  2365. /office365/{directory/security → security}/attacksimulations/payloadtheme.py +0 -0
  2366. /office365/{directory/security/ediscovery → security/attacksimulations/reports}/__init__.py +0 -0
  2367. /office365/{directory/security → security}/attacksimulations/simulationattacktechnique.py +0 -0
  2368. /office365/{directory/security → security}/attacksimulations/simulationattacktype.py +0 -0
  2369. /office365/{directory/security → security}/attacksimulations/simulationautomationrunstatus.py +0 -0
  2370. /office365/{directory/security → security}/attacksimulations/simulationautomationstatus.py +0 -0
  2371. /office365/{directory/security → security}/attacksimulations/simulationcontentsource.py +0 -0
  2372. /office365/{directory/security → security}/attacksimulations/simulationcontentstatus.py +0 -0
  2373. /office365/{directory/security → security}/attacksimulations/simulationstatus.py +0 -0
  2374. /office365/{directory/security → security}/attacksimulations/targettedusertype.py +0 -0
  2375. /office365/{directory/security → security}/attacksimulations/trainingassignedto.py +0 -0
  2376. /office365/{directory/security → security}/attacksimulations/trainingavailabilitystatus.py +0 -0
  2377. /office365/{directory/security → security}/attacksimulations/trainingcompletionduration.py +0 -0
  2378. /office365/{directory/security → security}/attacksimulations/trainingsettingtype.py +0 -0
  2379. /office365/{directory/security → security}/attacksimulations/trainingstatus.py +0 -0
  2380. /office365/{directory/security → security}/attacksimulations/trainingtype.py +0 -0
  2381. /office365/{directory/security/ediscovery/operations → security/attacksimulations/users}/__init__.py +0 -0
  2382. /office365/{directory/security → security}/attribute_definition_metadata_entry.py +0 -0
  2383. /office365/{directory/security → security}/attribute_mapping_parameter_schema.py +0 -0
  2384. /office365/{directory/security/healthissues → security/audit}/__init__.py +0 -0
  2385. /office365/{directory/security → security}/audit/a365_ai_execute_tool.py +0 -0
  2386. /office365/{directory/security → security}/audit/aad_risk_detection_audit_record.py +0 -0
  2387. /office365/{directory/security → security}/audit/ai_app_interaction_audit_record.py +0 -0
  2388. /office365/{directory/security → security}/audit/ai_execute_tool_audit_record.py +0 -0
  2389. /office365/{directory/security → security}/audit/ai_interactions_change_notification_audit_record.py +0 -0
  2390. /office365/{directory/security → security}/audit/ai_interactions_export_audit_record.py +0 -0
  2391. /office365/{directory/security → security}/audit/ai_interactions_subscription_audit_record.py +0 -0
  2392. /office365/{directory/security → security}/audit/ai_invoke_agent_audit_record.py +0 -0
  2393. /office365/{directory/security → security}/audit/ai_span_outputs_audit_record.py +0 -0
  2394. /office365/{directory/security → security}/audit/air_manual_investigation_data.py +0 -0
  2395. /office365/{directory/security → security}/audit/alert_submission_audit_record.py +0 -0
  2396. /office365/{directory/security → security}/audit/alert_submission_result_detail_audit_record.py +0 -0
  2397. /office365/{directory/security → security}/audit/attack_sim_admin_audit_record.py +0 -0
  2398. /office365/{directory/security → security}/audit/attack_sim_audit_record.py +0 -0
  2399. /office365/{directory/security → security}/audit/audit_config_audit_record.py +0 -0
  2400. /office365/{directory/security → security}/audit/audit_log_query_status.py +0 -0
  2401. /office365/{directory/security → security}/audit/audit_record_type_dictionary.py +0 -0
  2402. /office365/{directory/security → security}/audit/audit_search_audit_record.py +0 -0
  2403. /office365/{directory/security → security}/audit/azfw_application_rule_aggregation_event_record.py +0 -0
  2404. /office365/{directory/security → security}/audit/azfw_dns_query_event_record.py +0 -0
  2405. /office365/{directory/security → security}/audit/azfw_network_rule_event_record.py +0 -0
  2406. /office365/{directory/security → security}/audit/azure_active_directory_account_logon_audit_record.py +0 -0
  2407. /office365/{directory/security → security}/audit/azure_active_directory_audit_record.py +0 -0
  2408. /office365/{directory/security → security}/audit/azure_active_directory_sts_logon_audit_record.py +0 -0
  2409. /office365/{directory/security → security}/audit/azure_ai_search_audit_record.py +0 -0
  2410. /office365/{directory/security → security}/audit/campaign_audit_record.py +0 -0
  2411. /office365/{directory/security → security}/audit/ccrai_policy_violation_record.py +0 -0
  2412. /office365/{directory/security → security}/audit/cdp_classifier_health_record.py +0 -0
  2413. /office365/{directory/security → security}/audit/cdp_cold_crawl_status_record.py +0 -0
  2414. /office365/{directory/security → security}/audit/cdp_consumption_resource_record.py +0 -0
  2415. /office365/{directory/security → security}/audit/cdp_content_explorer_aggregate_record.py +0 -0
  2416. /office365/{directory/security → security}/audit/cdp_dlp_sensitive_endpoint_audit_record.py +0 -0
  2417. /office365/{directory/security → security}/audit/cdp_log_record.py +0 -0
  2418. /office365/{directory/security → security}/audit/cdp_ocr_billing_record.py +0 -0
  2419. /office365/{directory/security → security}/audit/cdp_resource_scope_change_event_record.py +0 -0
  2420. /office365/{directory/security → security}/audit/cdpdlmai_interaction_insights_record.py +0 -0
  2421. /office365/{directory/security → security}/audit/cloud_update_device_config_audit_record.py +0 -0
  2422. /office365/{directory/security → security}/audit/cloud_update_profile_config_audit_record.py +0 -0
  2423. /office365/{directory/security → security}/audit/cloud_update_tenant_config_audit_record.py +0 -0
  2424. /office365/{directory/security → security}/audit/compliance_connector_audit_record.py +0 -0
  2425. /office365/{directory/security → security}/audit/compliance_dlm_exchange_audit_record.py +0 -0
  2426. /office365/{directory/security → security}/audit/compliance_dlm_share_point_audit_record.py +0 -0
  2427. /office365/{directory/security → security}/audit/compliance_dlp_applications_audit_record.py +0 -0
  2428. /office365/{directory/security → security}/audit/compliance_dlp_applications_classification_audit_record.py +0 -0
  2429. /office365/{directory/security → security}/audit/compliance_dlp_endpoint_audit_record.py +0 -0
  2430. /office365/{directory/security → security}/audit/compliance_dlp_endpoint_discovery_audit_record.py +0 -0
  2431. /office365/{directory/security → security}/audit/compliance_dlp_enforcement_audit_record.py +0 -0
  2432. /office365/{directory/security → security}/audit/compliance_dlp_exchange_audit_record.py +0 -0
  2433. /office365/{directory/security → security}/audit/compliance_dlp_exchange_classification_audit_record.py +0 -0
  2434. /office365/{directory/security → security}/audit/compliance_dlp_exchange_classification_cdp_record.py +0 -0
  2435. /office365/{directory/security → security}/audit/compliance_dlp_exchange_discovery_audit_record.py +0 -0
  2436. /office365/{directory/security → security}/audit/compliance_dlp_share_point_audit_record.py +0 -0
  2437. /office365/{directory/security → security}/audit/compliance_dlp_share_point_classification_audit_record.py +0 -0
  2438. /office365/{directory/security → security}/audit/compliance_dlp_share_point_classification_cdp_record.py +0 -0
  2439. /office365/{directory/security → security}/audit/compliance_dlp_share_point_classification_extended_audit_record.py +0 -0
  2440. /office365/{directory/security → security}/audit/compliance_exchange_ocr_audit_record.py +0 -0
  2441. /office365/{directory/security → security}/audit/compliance_manager_action_record.py +0 -0
  2442. /office365/{directory/security → security}/audit/compliance_policy_grading_share_point_audit_record.py +0 -0
  2443. /office365/{directory/security → security}/audit/compliance_settings_change_audit_record.py +0 -0
  2444. /office365/{directory/security → security}/audit/compliance_sit_grading_share_point_audit_record.py +0 -0
  2445. /office365/{directory/security → security}/audit/compliance_supervision_exchange_audit_record.py +0 -0
  2446. /office365/{directory/security → security}/audit/connected_ai_app_interaction_audit_record.py +0 -0
  2447. /office365/{directory/security → security}/audit/consumption_resource_audit_record.py +0 -0
  2448. /office365/{directory/security → security}/audit/content_store_metadata_record.py +0 -0
  2449. /office365/{directory/security → security}/audit/copilot_agent_management_audit_record.py +0 -0
  2450. /office365/{directory/security → security}/audit/copilot_for_security_logging_audit_record.py +0 -0
  2451. /office365/{directory/security → security}/audit/copilot_for_security_trigger_audit_record.py +0 -0
  2452. /office365/{directory/security → security}/audit/copilot_interaction_audit_record.py +0 -0
  2453. /office365/{directory/security → security}/audit/copilot_plugin_setting_audit_record.py +0 -0
  2454. /office365/{directory/security → security}/audit/copilot_prompt_book_setting_audit_record.py +0 -0
  2455. /office365/{directory/security → security}/audit/copilot_session_sharing_audit_record.py +0 -0
  2456. /office365/{directory/security → security}/audit/copilot_setting_audit_record.py +0 -0
  2457. /office365/{directory/security → security}/audit/copilot_workspace_setting_audit_record.py +0 -0
  2458. /office365/{directory/security → security}/audit/core_reporting_settings_audit_record.py +0 -0
  2459. /office365/{directory/security → security}/audit/critical_asset_management_classification_record.py +0 -0
  2460. /office365/{directory/security → security}/audit/crm_entity_operation_audit_record.py +0 -0
  2461. /office365/{directory/security → security}/audit/cross_tenant_access_policy_audit_record.py +0 -0
  2462. /office365/{directory/security → security}/audit/device_discovery_settings_authenticated_scans_record.py +0 -0
  2463. /office365/{directory/security → security}/audit/device_discovery_settings_exclusion_record.py +0 -0
  2464. /office365/{directory/security → security}/audit/device_discovery_settings_record.py +0 -0
  2465. /office365/{directory/security → security}/audit/discovery_audit_record.py +0 -0
  2466. /office365/{directory/security → security}/audit/dlp_endpoint_audit_record.py +0 -0
  2467. /office365/{directory/security → security}/audit/dlp_import_result_audit_record.py +0 -0
  2468. /office365/{directory/security → security}/audit/dragon_copilot_access_record.py +0 -0
  2469. /office365/{directory/security → security}/audit/dragon_copilot_admin_record.py +0 -0
  2470. /office365/{directory/security → security}/audit/dragon_copilot_clinical_data_record.py +0 -0
  2471. /office365/{directory/security → security}/audit/dragon_copilot_session_record.py +0 -0
  2472. /office365/{directory/security → security}/audit/dynamics365_business_central_audit_record.py +0 -0
  2473. /office365/{directory/security → security}/audit/ehr_connector_audit_base_record.py +0 -0
  2474. /office365/{directory/security → security}/audit/eop_submission_feed_entity_audit_record.py +0 -0
  2475. /office365/{directory/security → security}/audit/exchange_admin_audit_record.py +0 -0
  2476. /office365/{directory/security → security}/audit/exchange_aggregated_mailbox_audit_record.py +0 -0
  2477. /office365/{directory/security → security}/audit/exchange_aggregated_operation_record.py +0 -0
  2478. /office365/{directory/security → security}/audit/exchange_mailbox_audit_group_record.py +0 -0
  2479. /office365/{directory/security → security}/audit/exchange_mailbox_audit_record.py +0 -0
  2480. /office365/{directory/security → security}/audit/hosted_rpa_audit_record.py +0 -0
  2481. /office365/{directory/security → security}/audit/hr_signal_audit_record.py +0 -0
  2482. /office365/{directory/security → security}/audit/hygiene_event_record.py +0 -0
  2483. /office365/{directory/security → security}/audit/information_barrier_policy_application_audit_record.py +0 -0
  2484. /office365/{directory/security → security}/audit/information_worker_protection_audit_record.py +0 -0
  2485. /office365/{directory/security → security}/audit/insider_risk_scoped_user_insights_record.py +0 -0
  2486. /office365/{directory/security → security}/audit/insider_risk_scoped_users_record.py +0 -0
  2487. /office365/{directory/security → security}/audit/microsoft_stream_audit_record.py +0 -0
  2488. /office365/{directory/security → security}/audit/microsoft_teams_admin_audit_record.py +0 -0
  2489. /office365/{directory/security → security}/audit/microsoft_teams_analytics_audit_record.py +0 -0
  2490. /office365/{directory/security → security}/audit/microsoft_teams_audit_record.py +0 -0
  2491. /office365/{directory/security → security}/audit/microsoft_teams_device_audit_record.py +0 -0
  2492. /office365/{directory/security → security}/audit/microsoft_teams_retention_label_action_audit_record.py +0 -0
  2493. /office365/{directory/security → security}/audit/microsoft_teams_sensitivity_label_action_audit_record.py +0 -0
  2494. /office365/{directory/security → security}/audit/microsoft_teams_shifts_audit_record.py +0 -0
  2495. /office365/{directory/security → security}/audit/microsoft_teams_user_concern_audit_record.py +0 -0
  2496. /office365/{directory/security → security}/audit/multi_tenant_organization_join_request_transition_details.py +0 -0
  2497. /office365/{directory/security → security}/audit/multi_tenant_organization_member_transition_details.py +0 -0
  2498. /office365/{directory/security → security}/audit/whois_domain_status.py +0 -0
  2499. /office365/{directory/security → security}/audit/whois_history_record.py +0 -0
  2500. /office365/{directory/security → security}/authority_template.py +0 -0
  2501. /office365/{directory/security → security}/auto_auditing_configuration.py +0 -0
  2502. /office365/{directory/security → security}/autonomous_system.py +0 -0
  2503. /office365/{directory/security/incidents → security/cases}/__init__.py +0 -0
  2504. /office365/{directory/security → security}/cases/action.py +0 -0
  2505. /office365/{directory/security → security}/cases/additionaldataoptions.py +0 -0
  2506. /office365/{directory/security → security}/cases/additionaloptions.py +0 -0
  2507. /office365/{directory/security → security}/cases/exportcriteria.py +0 -0
  2508. /office365/{directory/security → security}/cases/exportfilestructure.py +0 -0
  2509. /office365/{directory/security → security}/cases/exportformat.py +0 -0
  2510. /office365/{directory/security → security}/cases/exportlocation.py +0 -0
  2511. /office365/{directory/security → security}/cases/exportoptions.py +0 -0
  2512. /office365/{directory/security → security}/cases/itemstoinclude.py +0 -0
  2513. /office365/{directory/security → security}/cases/operationstatus.py +0 -0
  2514. /office365/{directory/security → security}/cases/reviewsetsettings.py +0 -0
  2515. /office365/{directory/security → security}/cases/statisticsoptions.py +0 -0
  2516. /office365/{directory/security → security}/cases/status.py +0 -0
  2517. /office365/{directory/security → security}/cases/type.py +0 -0
  2518. /office365/{directory/security → security}/childselectability.py +0 -0
  2519. /office365/{directory/security → security}/citation_template.py +0 -0
  2520. /office365/{directory/security → security}/cloud_logon_request_evidence.py +0 -0
  2521. /office365/{directory/security → security}/cloudattachmentversion.py +0 -0
  2522. /office365/{directory/security/labels → security/collaboration}/__init__.py +0 -0
  2523. /office365/{directory/security → security}/collaboration/analyzed_email_authentication_detail.py +0 -0
  2524. /office365/{directory/security → security}/collaboration/analyzed_email_dlp_rule_info.py +0 -0
  2525. /office365/{directory/security → security}/collaboration/analyzed_email_exchange_transport_rule_info.py +0 -0
  2526. /office365/{directory/security → security}/collaboration/analyzed_email_recipient_detail.py +0 -0
  2527. /office365/{directory/security → security}/collaboration/analyzed_email_sender_detail.py +0 -0
  2528. /office365/{directory/security → security}/container_filter.py +0 -0
  2529. /office365/{directory/security → security}/container_registry_evidence.py +0 -0
  2530. /office365/{directory/security → security}/containerportprotocol.py +0 -0
  2531. /office365/{directory/security → security}/contentformat.py +0 -0
  2532. /office365/{directory/security → security}/contentprocessingerrortype.py +0 -0
  2533. /office365/{directory/security → security}/correlation_reason.py +0 -0
  2534. /office365/{directory/security → security}/cortana_briefing_audit_record.py +0 -0
  2535. /office365/{directory/security → security}/custom_security_attribute_comparison_operator.py +0 -0
  2536. /office365/{directory/security → security}/customer_key_service_encryption_audit_record.py +0 -0
  2537. /office365/{directory/security → security}/data_center_security_cmdlet_audit_record.py +0 -0
  2538. /office365/{directory/security → security}/data_governance_audit_record.py +0 -0
  2539. /office365/{directory/security → security}/data_insights_rest_api_audit_record.py +0 -0
  2540. /office365/{directory/security → security}/data_lake_export_operation_audit_record.py +0 -0
  2541. /office365/{directory/security → security}/data_set.py +0 -0
  2542. /office365/{directory/security → security}/data_share_operation_audit_record.py +0 -0
  2543. /office365/{directory/security/labels/retention → security/datasecurity}/__init__.py +0 -0
  2544. /office365/{directory/security → security}/datasecurity/classification_inner_error.py +0 -0
  2545. /office365/{directory/security → security}/datasecurity/custom_metadata_dictionary.py +0 -0
  2546. /office365/{directory/security → security}/datasecurity/dlp_action_info.py +0 -0
  2547. /office365/{directory/security → security}/datasecurity/integrated_application_metadata.py +0 -0
  2548. /office365/{directory/security/data_security_investigation_audit_record.py → security/datasecurity/investigation_audit_record.py} +0 -0
  2549. /office365/{directory/security → security}/datasecurity/policy_binding.py +0 -0
  2550. /office365/{directory/security → security}/datasecurity/policy_location.py +0 -0
  2551. /office365/{directory/security → security}/datasecurity/policy_location_application.py +0 -0
  2552. /office365/{directory/security → security}/datasecurity/policy_location_domain.py +0 -0
  2553. /office365/{directory/security → security}/datasecurity/policy_location_url.py +0 -0
  2554. /office365/{directory/security → security}/datasecurity/policy_user_scope.py +0 -0
  2555. /office365/{directory/security → security}/datasecurity/process_conversation_metadata.py +0 -0
  2556. /office365/{directory/security → security}/datasecurity/protected_content.py +0 -0
  2557. /office365/{directory/security → security}/default_audit_data.py +0 -0
  2558. /office365/{directory/security → security}/defender_case_management_audit_record.py +0 -0
  2559. /office365/{directory/security → security}/defender_preview_features_record.py +0 -0
  2560. /office365/{directory/security → security}/defender_security_for_ai_configuration_audit_record.py +0 -0
  2561. /office365/{directory/security → security}/delivery_action.py +0 -0
  2562. /office365/{directory/security → security}/delivery_location.py +0 -0
  2563. /office365/{directory/security → security}/department_template.py +0 -0
  2564. /office365/{directory/security → security}/deploy_feature_activity_record.py +0 -0
  2565. /office365/{directory/security → security}/deployment_access_key_type.py +0 -0
  2566. /office365/{directory/security → security}/detonation_behaviour_details.py +0 -0
  2567. /office365/{directory/security → security}/detonation_chain.py +0 -0
  2568. /office365/{directory/security → security}/detonation_observables.py +0 -0
  2569. /office365/{directory/security → security}/dictionary.py +0 -0
  2570. /office365/{directory/security → security}/dlp_sensitive_information_type_rule_package_cmdlet_record.py +0 -0
  2571. /office365/{directory/security → security}/dynamic_column_value.py +0 -0
  2572. /office365/{directory/security/scorecontrol → security/ediscovery}/__init__.py +0 -0
  2573. /office365/{directory/security/search → security/ediscovery/operations}/__init__.py +0 -0
  2574. /office365/{directory/security → security}/email_sender.py +0 -0
  2575. /office365/{directory/security → security}/event_source.py +0 -0
  2576. /office365/{directory/security → security}/export_file_metadata.py +0 -0
  2577. /office365/{directory/security → security}/fabric_audit_record.py +0 -0
  2578. /office365/{directory/security → security}/fabric_policy_record.py +0 -0
  2579. /office365/{directory/security → security}/file_details.py +0 -0
  2580. /office365/{directory/security → security}/file_plan_authority.py +0 -0
  2581. /office365/{directory/security → security}/file_plan_citation.py +0 -0
  2582. /office365/{directory/security → security}/file_plan_department.py +0 -0
  2583. /office365/{directory/security → security}/file_plan_descriptor_base.py +0 -0
  2584. /office365/{directory/security → security}/file_plan_descriptor_template.py +0 -0
  2585. /office365/{directory/security → security}/file_plan_reference.py +0 -0
  2586. /office365/{directory/security → security}/file_plan_reference_template.py +0 -0
  2587. /office365/{directory/security → security}/file_plan_subcategory.py +0 -0
  2588. /office365/{directory/security → security}/filehashalgorithm.py +0 -0
  2589. /office365/{directory/security → security}/filter_operand.py +0 -0
  2590. /office365/{directory/security → security}/geo_location.py +0 -0
  2591. /office365/{directory/security → security}/git_hub_organization_evidence.py +0 -0
  2592. /office365/{directory/security → security}/git_hub_repo_evidence.py +0 -0
  2593. /office365/{directory/security → security}/git_hub_user_evidence.py +0 -0
  2594. /office365/{directory/security → security}/group_filter.py +0 -0
  2595. /office365/{directory/security → security}/healthcare_signal_record.py +0 -0
  2596. /office365/{directory/security/securescores → security/healthissues}/__init__.py +0 -0
  2597. /office365/{directory/security → security}/healthissues/deploymentstatus.py +0 -0
  2598. /office365/{directory/security → security}/healthissues/sensorhealthstatus.py +0 -0
  2599. /office365/{directory/security → security}/healthissues/sensortype.py +0 -0
  2600. /office365/{directory/security → security}/healthissues/severity.py +0 -0
  2601. /office365/{directory/security → security}/healthissues/sourcetype.py +0 -0
  2602. /office365/{directory/security → security}/healthissues/status.py +0 -0
  2603. /office365/{directory/security → security}/healthissues/type.py +0 -0
  2604. /office365/{directory/security → security}/hunting_row_result.py +0 -0
  2605. /office365/{directory/security/subjectrightsrequests → security/incidents}/__init__.py +0 -0
  2606. /office365/{directory/security → security}/incidents/status.py +0 -0
  2607. /office365/{directory/security → security}/indicator_source.py +0 -0
  2608. /office365/{directory/security → security}/integrated_apps_app_admin_activity_audit_record.py +0 -0
  2609. /office365/{directory/security → security}/integrated_apps_app_settings_admin_activity_audit_record.py +0 -0
  2610. /office365/{directory/security → security}/intelligence_profile_indicator.py +0 -0
  2611. /office365/{directory/security → security}/intelligence_profile_kind.py +0 -0
  2612. /office365/{directory/security → security}/investigation_state.py +0 -0
  2613. /office365/{directory/security → security}/irm_activity_audit_trail_record.py +0 -0
  2614. /office365/{directory/security → security}/irm_user_defined_detection_record.py +0 -0
  2615. /office365/{directory/security → security}/kaizala_audit_record.py +0 -0
  2616. /office365/{directory/security/threatintelligence → security/labels}/__init__.py +0 -0
  2617. /office365/{directory/security/label_analytics_aggregate_audit_record.py → security/labels/analytics_aggregate_audit_record.py} +0 -0
  2618. /office365/{directory/security/label_content_explorer_audit_record.py → security/labels/content_explorer_audit_record.py} +0 -0
  2619. /office365/{directory/security/threatintelligence/hosts → security/labels/retention}/__init__.py +0 -0
  2620. /office365/{directory/security → security}/labels/retention/actionafterretentionperiod.py +0 -0
  2621. /office365/{directory/security → security}/labels/retention/behaviorduringretentionperiod.py +0 -0
  2622. /office365/{directory/security → security}/labels/retention/defaultrecordbehavior.py +0 -0
  2623. /office365/{directory/security → security}/labels/retention/disposition_review_stage.py +0 -0
  2624. /office365/{directory/security → security}/labels/retention/duration.py +0 -0
  2625. /office365/{directory/security → security}/labels/retention/trigger.py +0 -0
  2626. /office365/{directory/security → security}/large_content_metadata_audit_record.py +0 -0
  2627. /office365/{directory/security → security}/logged_on_user.py +0 -0
  2628. /office365/{directory/security → security}/m365_compliance_connector_audit_record.py +0 -0
  2629. /office365/{directory/security → security}/m365_search_sections_record.py +0 -0
  2630. /office365/{directory/security → security}/m365daad_audit_record.py +0 -0
  2631. /office365/{directory/security → security}/m365odsp_asset_metadata_record.py +0 -0
  2632. /office365/{directory/security → security}/mail_cluster_evidence.py +0 -0
  2633. /office365/{directory/security → security}/mail_submission_data.py +0 -0
  2634. /office365/{directory/security → security}/mailbox_configuration_type.py +0 -0
  2635. /office365/{directory/security → security}/managed_services_audit_record.py +0 -0
  2636. /office365/{directory/security → security}/managed_tenants_audit_record.py +0 -0
  2637. /office365/{directory/security → security}/mapg_alerts_audit_record.py +0 -0
  2638. /office365/{directory/security → security}/mapg_onboard_audit_record.py +0 -0
  2639. /office365/{directory/security → security}/mapg_policy_audit_record.py +0 -0
  2640. /office365/{directory/security → security}/mapg_remediation_audit_record.py +0 -0
  2641. /office365/{directory/security → security}/mcas_alerts_audit_record.py +0 -0
  2642. /office365/{directory/security → security}/mda_audit_record.py +0 -0
  2643. /office365/{directory/security → security}/mda_data_security_signal_record.py +0 -0
  2644. /office365/{directory/security → security}/mdash_audit_record.py +0 -0
  2645. /office365/{directory/security → security}/mdatp_audit_record.py +0 -0
  2646. /office365/{directory/security → security}/mdc_events_record.py +0 -0
  2647. /office365/{directory/security → security}/mdi_audit_record.py +0 -0
  2648. /office365/{directory/security → security}/merge_response.py +0 -0
  2649. /office365/{directory/security → security}/mesh_worlds_audit_record.py +0 -0
  2650. /office365/{directory/security → security}/microsoft365_backup_backup_item_audit_record.py +0 -0
  2651. /office365/{directory/security → security}/microsoft365_backup_backup_policy_audit_record.py +0 -0
  2652. /office365/{directory/security → security}/microsoft365_backup_granular_browse_task_audit_record.py +0 -0
  2653. /office365/{directory/security → security}/microsoft365_backup_restore_item_audit_record.py +0 -0
  2654. /office365/{directory/security → security}/microsoft365_backup_restore_task_audit_record.py +0 -0
  2655. /office365/{directory/security → security}/microsoft365_copilot_scheduled_prompt_audit_record.py +0 -0
  2656. /office365/{directory/security → security}/microsoft_defender_experts_xdr_audit_record.py +0 -0
  2657. /office365/{directory/security → security}/microsoft_flow_audit_record.py +0 -0
  2658. /office365/{directory/security → security}/microsoft_forms_audit_record.py +0 -0
  2659. /office365/{directory/security → security}/microsoft_graph_data_connect_consent.py +0 -0
  2660. /office365/{directory/security → security}/microsoft_graph_data_connect_operation.py +0 -0
  2661. /office365/{directory/security → security}/microsoft_purview_data_catalog_operation_record.py +0 -0
  2662. /office365/{directory/security → security}/microsoft_purview_data_map_operation_record.py +0 -0
  2663. /office365/{directory/security → security}/microsoft_purview_metadata_policy_operation_record.py +0 -0
  2664. /office365/{directory/security → security}/microsoft_purview_policy_operation_record.py +0 -0
  2665. /office365/{directory/security → security}/microsoft_purview_privacy_audit_event.py +0 -0
  2666. /office365/{directory/security → security}/microsoft_purview_unified_catalog_operation_record.py +0 -0
  2667. /office365/{directory/security → security}/mip_auto_label_exchange_item_audit_record.py +0 -0
  2668. /office365/{directory/security → security}/mip_auto_label_progress_feedback_audit_record.py +0 -0
  2669. /office365/{directory/security → security}/mip_auto_label_share_point_item_audit_record.py +0 -0
  2670. /office365/{directory/security → security}/mip_auto_label_share_point_policy_location_audit_record.py +0 -0
  2671. /office365/{directory/security → security}/mip_auto_label_simulation_share_point_completion_record.py +0 -0
  2672. /office365/{directory/security → security}/mip_auto_label_simulation_share_point_progress_record.py +0 -0
  2673. /office365/{directory/security → security}/mip_auto_label_simulation_statistics_record.py +0 -0
  2674. /office365/{directory/security → security}/mip_exact_data_match_audit_record.py +0 -0
  2675. /office365/{directory/security → security}/mip_label_analytics_audit_record.py +0 -0
  2676. /office365/{directory/security → security}/mip_label_audit_record.py +0 -0
  2677. /office365/{directory/security → security}/mos_agent_info_record.py +0 -0
  2678. /office365/{directory/security → security}/mos_agent_info_record_v2.py +0 -0
  2679. /office365/{directory/security → security}/ms365d_custom_detection_audit_record.py +0 -0
  2680. /office365/{directory/security → security}/ms365d_incident_audit_record.py +0 -0
  2681. /office365/{directory/security → security}/ms365d_suppression_rule_audit_record.py +0 -0
  2682. /office365/{directory/security → security}/msde_custom_collection_audit_record.py +0 -0
  2683. /office365/{directory/security → security}/msde_general_settings_audit_record.py +0 -0
  2684. /office365/{directory/security → security}/msde_indicators_settings_audit_record.py +0 -0
  2685. /office365/{directory/security → security}/msde_response_actions_audit_record.py +0 -0
  2686. /office365/{directory/security → security}/msde_roles_settings_audit_record.py +0 -0
  2687. /office365/{directory/security → security}/msp_vector_search_content_metadata_audit_record.py +0 -0
  2688. /office365/{directory/security → security}/mstic_nation_state_notification_record.py +0 -0
  2689. /office365/{directory/security → security}/multi_stage_disposition_audit_record.py +0 -0
  2690. /office365/{directory/security → security}/my_analytics_settings_audit_record.py +0 -0
  2691. /office365/{directory/security → security}/network_adapter.py +0 -0
  2692. /office365/{directory/security → security}/noisy_alert_policy_audit_record.py +0 -0
  2693. /office365/{directory/security → security}/oauth_application_evidence.py +0 -0
  2694. /office365/{directory/security → security}/object_definition_metadata_entry.py +0 -0
  2695. /office365/{directory/security → security}/object_mapping_metadata_entry.py +0 -0
  2696. /office365/{directory/security → security}/ocr_settings.py +0 -0
  2697. /office365/{directory/security → security}/office_native_audit_record.py +0 -0
  2698. /office365/{directory/security → security}/ome_portal_audit_record.py +0 -0
  2699. /office365/{directory/security → security}/on_demand_share_point_classification_audit_record.py +0 -0
  2700. /office365/{directory/security → security}/on_premises_file_share_scanner_dlp_audit_record.py +0 -0
  2701. /office365/{directory/security → security}/on_premises_share_point_scanner_dlp_audit_record.py +0 -0
  2702. /office365/{directory/security → security}/onboarding_status.py +0 -0
  2703. /office365/{directory/security → security}/one_drive_audit_record.py +0 -0
  2704. /office365/{directory/security → security}/organizational_data_in_m365_audit_record.py +0 -0
  2705. /office365/{directory/security → security}/outlook_copilot_automation_audit_record.py +0 -0
  2706. /office365/{directory/security → security}/owa_get_access_token_for_resource_audit_record.py +0 -0
  2707. /office365/{directory/security → security}/p4_ai_risk_score_record.py +0 -0
  2708. /office365/{directory/security → security}/p4ai_assessment_category_record.py +0 -0
  2709. /office365/{directory/security → security}/p4ai_assessment_fabric_scanner_record.py +0 -0
  2710. /office365/{directory/security → security}/p4ai_assessment_location_result_record.py +0 -0
  2711. /office365/{directory/security → security}/p4ai_assessment_record.py +0 -0
  2712. /office365/{directory/security → security}/people_admin_settings_audit_record.py +0 -0
  2713. /office365/{directory/security → security}/physical_badging_signal_audit_record.py +0 -0
  2714. /office365/{directory/security → security}/places_directory_audit_record.py +0 -0
  2715. /office365/{directory/security → security}/planner_chat_message_audit_record.py +0 -0
  2716. /office365/{directory/security → security}/planner_chat_message_list_audit_record.py +0 -0
  2717. /office365/{directory/security → security}/planner_copy_plan_audit_record.py +0 -0
  2718. /office365/{directory/security → security}/planner_goal_audit_record.py +0 -0
  2719. /office365/{directory/security → security}/planner_goal_list_audit_record.py +0 -0
  2720. /office365/{directory/security → security}/planner_plan_audit_record.py +0 -0
  2721. /office365/{directory/security → security}/planner_plan_list_audit_record.py +0 -0
  2722. /office365/{directory/security → security}/planner_plan_sensitivity_label_audit_record.py +0 -0
  2723. /office365/{directory/security → security}/planner_roster_audit_record.py +0 -0
  2724. /office365/{directory/security → security}/planner_roster_sensitivity_label_audit_record.py +0 -0
  2725. /office365/{directory/security → security}/planner_task_audit_record.py +0 -0
  2726. /office365/{directory/security → security}/planner_task_list_audit_record.py +0 -0
  2727. /office365/{directory/security → security}/planner_tenant_settings_audit_record.py +0 -0
  2728. /office365/{directory/security → security}/policy_config_change_audit_record.py +0 -0
  2729. /office365/{directory/security → security}/policypivotproperty.py +0 -0
  2730. /office365/{directory/security → security}/power_apps_audit_app_record.py +0 -0
  2731. /office365/{directory/security → security}/power_apps_audit_plan_record.py +0 -0
  2732. /office365/{directory/security → security}/power_apps_audit_resource_record.py +0 -0
  2733. /office365/{directory/security → security}/power_bi_audit_record.py +0 -0
  2734. /office365/{directory/security → security}/power_bi_dlp_audit_record.py +0 -0
  2735. /office365/{directory/security → security}/power_pages_site_audit_record.py +0 -0
  2736. /office365/{directory/security → security}/power_platform_admin_dlp_audit_record.py +0 -0
  2737. /office365/{directory/security → security}/power_platform_admin_environment_audit_record.py +0 -0
  2738. /office365/{directory/security → security}/power_platform_administrator_activity_record.py +0 -0
  2739. /office365/{directory/security → security}/power_platform_lockbox_resource_access_request_audit_record.py +0 -0
  2740. /office365/{directory/security → security}/power_platform_lockbox_resource_command_audit_record.py +0 -0
  2741. /office365/{directory/security → security}/power_platform_service_activity_audit_record.py +0 -0
  2742. /office365/{directory/security → security}/power_platform_tenant_isolation_record.py +0 -0
  2743. /office365/{directory/security → security}/priva_privacy_assessment_operation_record.py +0 -0
  2744. /office365/{directory/security → security}/priva_privacy_consent_operation_record.py +0 -0
  2745. /office365/{directory/security → security}/privacy_data_match_audit_record.py +0 -0
  2746. /office365/{directory/security → security}/privacy_data_minimization_record.py +0 -0
  2747. /office365/{directory/security → security}/privacy_digest_email_record.py +0 -0
  2748. /office365/{directory/security → security}/privacy_open_access_audit_record.py +0 -0
  2749. /office365/{directory/security → security}/privacy_portal_audit_record.py +0 -0
  2750. /office365/{directory/security → security}/privacy_remediation_action_record.py +0 -0
  2751. /office365/{directory/security → security}/privacy_remediation_record.py +0 -0
  2752. /office365/{directory/security → security}/project_audit_record.py +0 -0
  2753. /office365/{directory/security → security}/project_for_the_web_assigned_to_me_settings_audit_record.py +0 -0
  2754. /office365/{directory/security → security}/project_for_the_web_project_audit_record.py +0 -0
  2755. /office365/{directory/security → security}/project_for_the_web_project_settings_audit_record.py +0 -0
  2756. /office365/{directory/security → security}/project_for_the_web_roadmap_audit_record.py +0 -0
  2757. /office365/{directory/security → security}/project_for_the_web_roadmap_item_audit_record.py +0 -0
  2758. /office365/{directory/security → security}/project_for_the_web_roadmap_settings_audit_record.py +0 -0
  2759. /office365/{directory/security → security}/project_for_the_web_task_audit_record.py +0 -0
  2760. /office365/{directory/security → security}/protectionscopestate.py +0 -0
  2761. /office365/{directory/security → security}/protocol_type.py +0 -0
  2762. /office365/{directory/security → security}/public_error_response.py +0 -0
  2763. /office365/{directory/security → security}/public_folder_audit_record.py +0 -0
  2764. /office365/{directory/security → security}/purgeareas.py +0 -0
  2765. /office365/{directory/security → security}/purgetype.py +0 -0
  2766. /office365/{directory/security → security}/purview_insider_risk_alerts_record.py +0 -0
  2767. /office365/{directory/security → security}/purview_insider_risk_cases_record.py +0 -0
  2768. /office365/{directory/security → security}/purview_mc_recommendation_record.py +0 -0
  2769. /office365/{directory/security → security}/purview_posture_agent_audit_record.py +0 -0
  2770. /office365/{directory/security → security}/quarantine_audit_record.py +0 -0
  2771. /office365/{directory/security → security}/query_type.py +0 -0
  2772. /office365/{directory/security → security}/recipient_type.py +0 -0
  2773. /office365/{directory/security → security}/records_management_audit_record.py +0 -0
  2774. /office365/{directory/security → security}/redundancy_detection_settings.py +0 -0
  2775. /office365/{directory/security → security}/referenced_object.py +0 -0
  2776. /office365/{directory/security → security}/registry_key_evidence.py +0 -0
  2777. /office365/{directory/security → security}/registry_value_evidence.py +0 -0
  2778. /office365/{directory/security → security}/remediation_action.py +0 -0
  2779. /office365/{directory/security → security}/remediation_severity.py +0 -0
  2780. /office365/{directory/security → security}/report_file_metadata.py +0 -0
  2781. /office365/{directory/security → security}/report_submission.py +0 -0
  2782. /office365/{directory/security → security}/report_submission_result_detail.py +0 -0
  2783. /office365/{directory/security → security}/resource_access_event.py +0 -0
  2784. /office365/{directory/security → security}/restricted_mode_audit_record.py +0 -0
  2785. /office365/{directory/security → security}/restrictionaction.py +0 -0
  2786. /office365/{directory/security → security}/retention_duration_forever.py +0 -0
  2787. /office365/{directory/security → security}/retention_policy_audit_record.py +0 -0
  2788. /office365/{directory/security → security}/riskscore.py +0 -0
  2789. /office365/{directory/security → security}/rti_operations_agent_audit_record.py +0 -0
  2790. /office365/{directory/security → security}/sbp_configuration_event_record.py +0 -0
  2791. /office365/{directory/security → security}/sbp_usage_event_record.py +0 -0
  2792. /office365/{directory/security → security}/score_evidence.py +0 -0
  2793. /office365/{directory/security → security}/score_platform_generic_audit_record.py +0 -0
  2794. /office365/{directory/security/threatintelligence/whoisrecords → security/scorecontrol}/__init__.py +0 -0
  2795. /office365/{directory/security → security}/script_run_audit_record.py +0 -0
  2796. /office365/{directory/security/triggers → security/search}/__init__.py +0 -0
  2797. /office365/{directory/security/search_audit_record.py → security/search/audit_record.py} +0 -0
  2798. /office365/{directory/security → security}/search/datasourcecontainerstatus.py +0 -0
  2799. /office365/{directory/security → security}/search/datasourceholdstatus.py +0 -0
  2800. /office365/{directory/security → security}/search/datasourcescopes.py +0 -0
  2801. /office365/{directory/security → security}/search/documentversion.py +0 -0
  2802. /office365/{directory/security → security}/search.py +0 -0
  2803. /office365/{directory/security/triggertypes → security/securescores}/__init__.py +0 -0
  2804. /office365/{directory/security → security}/security_compliance_alert_record.py +0 -0
  2805. /office365/{directory/security → security}/security_compliance_center_eop_cmdlet_audit_record.py +0 -0
  2806. /office365/{directory/security → security}/security_compliance_insights_audit_record.py +0 -0
  2807. /office365/{directory/security → security}/security_compliance_rbac_audit_record.py +0 -0
  2808. /office365/{directory/security → security}/security_compliance_user_change_audit_record.py +0 -0
  2809. /office365/{directory/security → security}/security_copilot_identity_management_audit_record.py +0 -0
  2810. /office365/{directory/security → security}/security_development_lifecycle_ai_log_audit_record.py +0 -0
  2811. /office365/{directory/security → security}/security_group_evidence.py +0 -0
  2812. /office365/{directory/security → security}/sensitive_info_remediation_agent_data_record.py +0 -0
  2813. /office365/{directory/security → security}/sensitivity_label_action_audit_record.py +0 -0
  2814. /office365/{directory/security → security}/sensitivity_label_policy_match_audit_record.py +0 -0
  2815. /office365/{directory/security → security}/sensitivity_labeled_file_action_audit_record.py +0 -0
  2816. /office365/{directory/security → security}/sensor_candidate.py +0 -0
  2817. /office365/{directory/security → security}/sensor_candidate_activation_mode.py +0 -0
  2818. /office365/{directory/security → security}/sensor_deployment_package.py +0 -0
  2819. /office365/{directory/security → security}/sentinel_ai_tool_audit_record.py +0 -0
  2820. /office365/{directory/security → security}/sentinel_graph_audit_record.py +0 -0
  2821. /office365/{directory/security → security}/sentinel_job_audit_record.py +0 -0
  2822. /office365/{directory/security → security}/sentinel_kql_on_lake_audit_record.py +0 -0
  2823. /office365/{directory/security → security}/sentinel_lake_data_onboarding_audit_record.py +0 -0
  2824. /office365/{directory/security → security}/sentinel_lake_encryption_audit_record.py +0 -0
  2825. /office365/{directory/security → security}/sentinel_lake_onboarding_audit_record.py +0 -0
  2826. /office365/{directory/security → security}/sentinel_notebook_on_lake_audit_record.py +0 -0
  2827. /office365/{directory/security → security}/sentinel_package_audit_record.py +0 -0
  2828. /office365/{directory/security → security}/service_principal_type.py +0 -0
  2829. /office365/{directory/security → security}/service_source.py +0 -0
  2830. /office365/{directory/security → security}/share_point_app_permission_operation_audit_record.py +0 -0
  2831. /office365/{directory/security → security}/share_point_audit_record.py +0 -0
  2832. /office365/{directory/security → security}/share_point_comment_operation_audit_record.py +0 -0
  2833. /office365/{directory/security → security}/share_point_content_security_policy_audit_record.py +0 -0
  2834. /office365/{directory/security → security}/share_point_content_type_operation_audit_record.py +0 -0
  2835. /office365/{directory/security → security}/share_point_e_signature_audit_record.py +0 -0
  2836. /office365/{directory/security → security}/share_point_field_operation_audit_record.py +0 -0
  2837. /office365/{directory/security → security}/share_point_file_operation_audit_record.py +0 -0
  2838. /office365/{directory/security → security}/share_point_list_item_operation_audit_record.py +0 -0
  2839. /office365/{directory/security → security}/share_point_list_operation_audit_record.py +0 -0
  2840. /office365/{directory/security → security}/share_point_sharing_operation_audit_record.py +0 -0
  2841. /office365/{directory/security → security}/single_property_schema.py +0 -0
  2842. /office365/{directory/security → security}/site_source.py +0 -0
  2843. /office365/{directory/security → security}/skype_for_business_cmdlets_audit_record.py +0 -0
  2844. /office365/{directory/security → security}/skype_for_business_pstn_usage_audit_record.py +0 -0
  2845. /office365/{directory/security → security}/skype_for_business_users_blocked_audit_record.py +0 -0
  2846. /office365/{directory/security → security}/sonar_detonation_content_metadata.py +0 -0
  2847. /office365/{directory/security → security}/sonar_detonation_entity_audit_record.py +0 -0
  2848. /office365/{directory/security → security}/sonar_file_detonation_entity_audit_record.py +0 -0
  2849. /office365/{directory/security → security}/sonar_submission_entity_audit_record.py +0 -0
  2850. /office365/{directory/security → security}/sonar_url_detonation_entity_audit_record.py +0 -0
  2851. /office365/{directory/security → security}/spark_core_custom_live_pool_record.py +0 -0
  2852. /office365/{directory/security → security}/stream.py +0 -0
  2853. /office365/{directory/security → security}/string_key_long_value_pair.py +0 -0
  2854. /office365/{directory/security → security}/string_key_object_value_pair.py +0 -0
  2855. /office365/{directory/security → security}/string_key_string_value_pair.py +0 -0
  2856. /office365/{directory/security → security}/string_value_dictionary.py +0 -0
  2857. /office365/{directory/security → security}/subcategory_template.py +0 -0
  2858. /office365/{intune/devices/configurations/windows/10 → security/subjectrightsrequests}/__init__.py +0 -0
  2859. /office365/{directory/security → security}/subjectrightsrequests/all_mailbox_location.py +0 -0
  2860. /office365/{directory/security → security}/subjectrightsrequests/all_site_location.py +0 -0
  2861. /office365/{directory/security → security}/subjectrightsrequests/data_subject.py +0 -0
  2862. /office365/{directory/security → security}/subjectrightsrequests/detail.py +0 -0
  2863. /office365/{directory/security → security}/subjectrightsrequests/enumerated_mailbox_location.py +0 -0
  2864. /office365/{directory/security → security}/subjectrightsrequests/enumerated_site_location.py +0 -0
  2865. /office365/{directory/security → security}/subjectrightsrequests/history.py +0 -0
  2866. /office365/{directory/security → security}/subjectrightsrequests/mailbox_location.py +0 -0
  2867. /office365/{directory/security → security}/subjectrightsrequests/site_location.py +0 -0
  2868. /office365/{directory/security → security}/subjectrightsrequests/stage_detail.py +0 -0
  2869. /office365/{directory/security → security}/submission_entity_audit_record.py +0 -0
  2870. /office365/{directory/security → security}/submission_mail_evidence.py +0 -0
  2871. /office365/{directory/security → security}/supervisory_review_day_x_insights_audit_record.py +0 -0
  2872. /office365/{directory/security → security}/synthetic_probe_audit_record.py +0 -0
  2873. /office365/{directory/security → security}/tag.py +0 -0
  2874. /office365/{directory/security → security}/team_copilot_interaction_audit_record.py +0 -0
  2875. /office365/{directory/security → security}/teams_delivery_location.py +0 -0
  2876. /office365/{directory/security → security}/teams_easy_approvals_audit_record.py +0 -0
  2877. /office365/{directory/security → security}/teams_eval_data_hub_admin_operation_audit_record.py +0 -0
  2878. /office365/{directory/security → security}/teams_eval_data_hub_data_access_audit_record.py +0 -0
  2879. /office365/{directory/security → security}/teams_eval_data_hub_permission_change_audit_record.py +0 -0
  2880. /office365/{directory/security → security}/teams_healthcare_audit_record.py +0 -0
  2881. /office365/{directory/security → security}/teams_message_delivery_action.py +0 -0
  2882. /office365/{directory/security → security}/teams_updates_audit_record.py +0 -0
  2883. /office365/{directory/security → security}/tenant_allow_block_list_audit_record.py +0 -0
  2884. /office365/{directory/security → security}/threat_detection_detail.py +0 -0
  2885. /office365/{directory/security → security}/threat_finder_audit_record.py +0 -0
  2886. /office365/{directory/security → security}/threat_type.py +0 -0
  2887. /office365/{sharepoint/publishing/pages/3d → security/threatintelligence}/__init__.py +0 -0
  2888. /office365/{directory/security → security}/threatintelligence/article_indicator.py +0 -0
  2889. /office365/{directory/security/threat_intelligence_atp_content_data.py → security/threatintelligence/atp_content_data.py} +0 -0
  2890. /office365/{directory/security/threat_intelligence_export_audit_record.py → security/threatintelligence/export_audit_record.py} +0 -0
  2891. /office365/{directory/security → security}/threatintelligence/host_port_banner.py +0 -0
  2892. /office365/{directory/security → security}/threatintelligence/host_port_protocol.py +0 -0
  2893. /office365/{directory/security → security}/threatintelligence/host_port_status.py +0 -0
  2894. /office365/{directory/security → security}/threatintelligence/host_reputation_classification.py +0 -0
  2895. /office365/{directory/security → security}/threatintelligence/host_reputation_rule_severity.py +0 -0
  2896. /office365/{directory/security → security}/threatintelligence/hostname.py +0 -0
  2897. /office365/{directory/security/threat_intelligence_mail_data.py → security/threatintelligence/mail_data.py} +0 -0
  2898. /office365/{directory/security/threat_intelligence_object_audit_record.py → security/threatintelligence/object_audit_record.py} +0 -0
  2899. /office365/{directory/security/threat_intelligence_url_click_data.py → security/threatintelligence/url_click_data.py} +0 -0
  2900. /office365/{directory/security → security}/timeline_event_type.py +0 -0
  2901. /office365/{directory/security → security}/todo_audit_record.py +0 -0
  2902. /office365/{directory/security → security}/topic_modeling_settings.py +0 -0
  2903. /office365/{directory/security → security}/trainable_classifier_audit_record.py +0 -0
  2904. /office365/{directory/security → security}/triggers/eventpropagationstatus.py +0 -0
  2905. /office365/{directory/security → security}/triggers/eventstatustype.py +0 -0
  2906. /office365/{directory/security → security}/triggertypes/event_type.py +0 -0
  2907. /office365/{directory/security → security}/uam_operation_audit_record.py +0 -0
  2908. /office365/{directory/security → security}/unclassified_artifact.py +0 -0
  2909. /office365/{directory/security → security}/unified_group_audit_record.py +0 -0
  2910. /office365/{directory/security → security}/unified_simulation_matched_item_audit_record.py +0 -0
  2911. /office365/{directory/security → security}/unified_simulation_summary_audit_record.py +0 -0
  2912. /office365/{directory/security → security}/universal_print_management_audit_record.py +0 -0
  2913. /office365/{directory/security → security}/universal_print_print_job_audit_record.py +0 -0
  2914. /office365/{directory/security → security}/urbac_assignment_audit_record.py +0 -0
  2915. /office365/{directory/security → security}/urbac_enable_state_audit_record.py +0 -0
  2916. /office365/{directory/security → security}/urbac_role_audit_record.py +0 -0
  2917. /office365/{directory/security → security}/url_evidence.py +0 -0
  2918. /office365/{directory/security → security}/user_training_audit_record.py +0 -0
  2919. /office365/{directory/security → security}/useractivitytype.py +0 -0
  2920. /office365/{directory/security → security}/useractivitytypes.py +0 -0
  2921. /office365/{directory/security → security}/userscopetype.py +0 -0
  2922. /office365/{directory/security → security}/usx_workspace_onboarding_audit_record.py +0 -0
  2923. /office365/{directory/security → security}/verdict_category.py +0 -0
  2924. /office365/{directory/security → security}/vfam_create_policy_audit_record.py +0 -0
  2925. /office365/{directory/security → security}/vfam_delete_policy_audit_record.py +0 -0
  2926. /office365/{directory/security → security}/vfam_update_policy_audit_record.py +0 -0
  2927. /office365/{directory/security → security}/viva_amplify_audit_record.py +0 -0
  2928. /office365/{directory/security → security}/viva_engage_events_audit_record.py +0 -0
  2929. /office365/{directory/security → security}/viva_engage_network_association_audit_record.py +0 -0
  2930. /office365/{directory/security → security}/viva_engage_segment_audit_record.py +0 -0
  2931. /office365/{directory/security → security}/viva_glint_advanced_configuration_audit_record.py +0 -0
  2932. /office365/{directory/security → security}/viva_glint_agentic_campaign_audit_record.py +0 -0
  2933. /office365/{directory/security → security}/viva_glint_feedback_program_audit_record.py +0 -0
  2934. /office365/{directory/security → security}/viva_glint_organizational_data_audit_record.py +0 -0
  2935. /office365/{directory/security → security}/viva_glint_pulse_program_audit_record.py +0 -0
  2936. /office365/{directory/security → security}/viva_glint_pulse_program_respondent_rate_audit_record.py +0 -0
  2937. /office365/{directory/security → security}/viva_glint_question_audit_record.py +0 -0
  2938. /office365/{directory/security → security}/viva_glint_role_audit_record.py +0 -0
  2939. /office365/{directory/security → security}/viva_glint_rubicon_audit_record.py +0 -0
  2940. /office365/{directory/security → security}/viva_glint_support_access_audit_record.py +0 -0
  2941. /office365/{directory/security → security}/viva_glint_system_audit_record.py +0 -0
  2942. /office365/{directory/security → security}/viva_glint_user_audit_record.py +0 -0
  2943. /office365/{directory/security → security}/viva_glint_user_group_audit_record.py +0 -0
  2944. /office365/{directory/security → security}/viva_goals_audit_record.py +0 -0
  2945. /office365/{directory/security → security}/viva_learning_admin_audit_record.py +0 -0
  2946. /office365/{directory/security → security}/viva_learning_audit_record.py +0 -0
  2947. /office365/{directory/security → security}/viva_pulse_admin_audit_record.py +0 -0
  2948. /office365/{directory/security → security}/viva_pulse_organizer_audit_record.py +0 -0
  2949. /office365/{directory/security → security}/viva_pulse_report_audit_record.py +0 -0
  2950. /office365/{directory/security → security}/viva_pulse_response_audit_record.py +0 -0
  2951. /office365/{directory/security → security}/vm_cloud_provider.py +0 -0
  2952. /office365/{directory/security → security}/vulnerability_component.py +0 -0
  2953. /office365/{directory/security → security}/vulnerability_severity.py +0 -0
  2954. /office365/{directory/security → security}/wdatp_alerts_audit_record.py +0 -0
  2955. /office365/{directory/security → security}/web_content_filtering_audit_record.py +0 -0
  2956. /office365/{directory/security → security}/windows365_customer_lockbox_audit_record.py +0 -0
  2957. /office365/{directory/security → security}/workplace_analytics_audit_record.py +0 -0
  2958. /office365/{directory/security → security}/yammer_audit_record.py +0 -0
  2959. /office365/{directory/security → security}/yammer_user_hiding_audit_record.py +0 -0
  2960. /office365/sharepoint/publishing/pages/{3d → _3d}/page.py +0 -0
  2961. /office365/sharepoint/publishing/pages/{3d → _3d}/types.py +0 -0
  2962. /office365/{intune/print → todo/tasks}/taskstatus.py +0 -0
  2963. {office365_rest_python_client-3.2.0.dist-info → office365_rest_python_client-3.3.0.dist-info}/WHEEL +0 -0
@@ -12,20 +12,33 @@ from __future__ import annotations
12
12
 
13
13
  import hashlib
14
14
  import io
15
+ import json
15
16
  from collections.abc import Callable
16
- from datetime import timezone
17
- from typing import TYPE_CHECKING, cast
17
+ from datetime import datetime, timezone
18
+ from typing import TYPE_CHECKING, Any, cast
19
+
20
+ from typing_extensions import TypeAlias
18
21
 
19
22
  from office365.migration.adapters import MigrationProgress
20
- from office365.migration.base import MigrationItem, PermissionEntry
23
+ from office365.migration.base import (
24
+ MigrationItem,
25
+ PermissionEntry,
26
+ Principal,
27
+ conflict_to_on_conflict,
28
+ normalize_puid,
29
+ )
30
+ from office365.migration.sharepoint.structure import normalize_guid
21
31
  from office365.migration.sharepoint.transfer import Failure
22
- from office365.runtime.converters.json_file import record_to_json
32
+ from office365.runtime.converters.records import records_from_items
23
33
  from office365.runtime.converters.scalars import iso_or_none, parse_datetime, parse_int
24
34
  from office365.runtime.limits import DEFAULT_BATCH_SIZE
25
35
  from office365.runtime.operations import emit_progress
26
36
  from office365.sharepoint.fields.builtin_field_name import SYSTEM_FIELD_NAMES
37
+ from office365.sharepoint.fields.user_value import FieldUserValue
38
+ from office365.sharepoint.principal.type import PrincipalType
27
39
 
28
40
  if TYPE_CHECKING:
41
+ from office365.migration.map import ContentTypeMap
29
42
  from office365.sharepoint.files.file import File
30
43
  from office365.sharepoint.folders.folder import Folder
31
44
  from office365.sharepoint.lists.list import List as SPList
@@ -33,24 +46,496 @@ if TYPE_CHECKING:
33
46
 
34
47
  _TAXONOMY_FIELD_TYPES = {"TaxonomyFieldType", "TaxonomyFieldTypeMulti"}
35
48
 
49
+
50
+ def _cloned_context(obj):
51
+ """A fresh ``ClientContext`` clone for ``obj`` (same auth + transport, own queue)."""
52
+ ctx = obj.context
53
+ return ctx.clone(ctx.base_url)
54
+
55
+
56
+ def _cloned_list(sp_list: "SPList", context=None) -> "SPList":
57
+ """The same list, addressed by id and loaded on ``context`` (or a clone of it)."""
58
+ list_id = sp_list.id
59
+ if not list_id:
60
+ raise ValueError("cannot rebind a list that was never loaded")
61
+ ctx = context if context is not None else _cloned_context(sp_list)
62
+ return ctx.web.lists.get_by_id(list_id).get().execute_query()
63
+
64
+
65
+ def _cloned_folder(folder: "Folder", context=None) -> "Folder":
66
+ """The same folder, addressed by server-relative path and loaded on ``context``."""
67
+ server_relative_url = folder.server_relative_url
68
+ if not server_relative_url:
69
+ raise ValueError("cannot rebind a folder that was never loaded")
70
+ ctx = context if context is not None else _cloned_context(folder)
71
+ return ctx.web.get_folder_by_server_relative_path(server_relative_url).get().execute_query()
72
+
73
+
74
+ #: The target's typed columns: ``(lookup: name -> referenced list id, user names,
75
+ #: multi-lookup: name -> referenced list id, multi-user names, taxonomy:
76
+ #: name -> (allows many, hidden note field internal name), multi-choice names)``.
77
+ _TypedColumns: TypeAlias = (
78
+ "tuple[dict[str, str], set[str], dict[str, str], set[str], dict[str, tuple[bool, str | None]], set[str]]"
79
+ )
80
+
36
81
  # Always-projected system metadata (reliable author/editor identity).
37
- _METADATA_SELECT = ["AuthorId", "EditorId"]
82
+ _METADATA_SELECT = ["AuthorId", "EditorId", "HasUniqueRoleAssignments"]
83
+ #: Projected system metadata that is read-only and must not be written back.
84
+ _NON_WRITABLE_SELECT = frozenset({"HasUniqueRoleAssignments"})
85
+
86
+ #: Navigation properties a ``*`` read returns that carry no scalar content and can
87
+ #: never be written back; they are dropped silently. A genuine typed value we
88
+ #: cannot remap (a lookup/person column) is still surfaced as a warning.
89
+ _NAVIGATION_SELECT = frozenset(
90
+ {
91
+ "ParentList",
92
+ "ContentType",
93
+ "Folder",
94
+ "File",
95
+ "FieldValuesAsHtml",
96
+ "FieldValuesAsText",
97
+ "FieldValuesForEdit",
98
+ }
99
+ )
100
+
101
+ #: Marker column added to a migrated list recording each item's source id. It makes
102
+ #: a re-run idempotent and lets post-run verification pair source and target items.
103
+ DEFAULT_LIST_IDENTITY_FIELD = "MigrationSourceId"
104
+ # Authorship has its own fidelity path (``apply_authorship``); the built-in Author/
105
+ # Editor companion columns are neither content nor Person values to remap.
106
+ _PERSON_EXCLUDED = frozenset({"Author", "Editor"})
107
+
108
+ #: Content-approval state: record key (OData escaped form) -> ``ValidateUpdateListItem``
109
+ #: field name. It is ordinary record content, but a *system update* re-approves the
110
+ #: item, so every metadata write that restores timestamps/authorship would silently
111
+ #: discard it; the captured state is therefore folded into that same write
112
+ #: (live-probed: both fields are accepted, and no version is created).
113
+ _APPROVAL_FIELDS = {
114
+ "OData__ModerationStatus": "_ModerationStatus",
115
+ "OData__ModerationComments": "_ModerationComments",
116
+ }
117
+
118
+ #: Approval status of an approved item (0 = draft/pending, 1 = rejected, 2 = approved).
119
+ #: A freshly uploaded document is approved, so only the other states are worth writing.
120
+ _APPROVED_STATUS = "2"
121
+
122
+ #: Moderation columns of a list item. They only exist on a list whose
123
+ #: ``EnableModeration`` is on, so selecting them unconditionally fails outright.
124
+ _MODERATION_FIELDS = ("OData__ModerationStatus", "OData__ModerationComments")
125
+
126
+ #: How many paths a per-run warning names before it summarizes the rest.
127
+ _WARNING_SAMPLE = 5
128
+
129
+ #: Multi-value typed columns. Their ``<name>Id`` companion is a collection of ids
130
+ #: that a ``*`` projection omits (LookupMulti entirely; UserMulti unreliably), so the
131
+ #: source selects them explicitly and the target writes ``{"results": [...]}``.
132
+ _MULTI_VALUE_TYPES = frozenset({"LookupMulti", "UserMulti"})
133
+
134
+ #: Typed columns whose values are remapped (lookup item ids, person logins).
135
+ _TYPED_VALUE_TYPES = frozenset({"Lookup", "LookupMulti", "User", "UserMulti"})
136
+
137
+ #: Properties the source column scans need. Selecting them explicitly keeps the read
138
+ #: independent of whatever ``$select`` a previous caller left on the shared
139
+ #: ``fields`` collection — ``List.fields`` survives a ``capture_list_structure``
140
+ #: read (which selects a different, narrower set), and a field payload that omits
141
+ #: ``AllowMultipleValues`` makes a multi-value column look single-value.
142
+ _SOURCE_FIELD_SELECT = [
143
+ "Id",
144
+ "InternalName",
145
+ "TypeAsString",
146
+ "SchemaXml",
147
+ "Hidden",
148
+ "ReadOnlyField",
149
+ "TextField",
150
+ "AllowMultipleValues",
151
+ ]
152
+
153
+
154
+ #: Choice column types; a multi-choice column stores a collection of strings.
155
+ _CHOICE_TYPES = frozenset({"Choice", "MultiChoice"})
156
+
157
+ #: Column types whose value is a small REST object instead of a scalar.
158
+ _VALUE_OBJECT_TYPES = frozenset({"URL"})
159
+
160
+
161
+ def _allows_multiple_values(field: Any) -> bool:
162
+ """Whether a Lookup/Person column holds many values.
163
+
164
+ ``TypeAsString`` is not authoritative here. A column created from a
165
+ ``Type="Lookup" Mult="TRUE"`` (or ``Type="User" Mult="TRUE"``) schema keeps that
166
+ spelling, so SharePoint reports the *single-value* type and only
167
+ ``AllowMultipleValues`` tells the truth (live-verified). Classifying such a
168
+ column as single-value left its collection of ids unremapped: a multi-lookup
169
+ kept source item ids and a multi-person kept source site-user ids on the target,
170
+ and verification could not see the difference.
171
+ """
172
+ if getattr(field, "type_as_string", None) in _MULTI_VALUE_TYPES:
173
+ return True
174
+ return field.properties.get("AllowMultipleValues") is True
175
+
176
+
177
+ def _is_multi_choice_column(field: Any) -> bool:
178
+ """Whether a Choice column stores a collection of values.
179
+
180
+ ``TypeAsString == "MultiChoice"`` is the only reliable signal. Live-probed: a
181
+ column created from a ``Type="Choice" Mult="TRUE"`` schema reads back as
182
+ ``"Choice"``, stores one value in a single ``nvarchar`` slot (a scalar write
183
+ round-trips, an array is rejected) and does not project ``AllowMultipleValues``
184
+ at all — its schema ``Mult`` flag is misleading, so it is deliberately not used.
185
+ """
186
+ field_type = getattr(field, "type_as_string", None)
187
+ if field_type == "MultiChoice":
188
+ return True
189
+ return field_type in _CHOICE_TYPES and _allows_multiple_values(field)
190
+
191
+
192
+ def _is_value_object_column(field: Any) -> bool:
193
+ """Whether a column holds a REST value object rather than a scalar.
194
+
195
+ A Hyperlink column is the common case: a read returns
196
+ ``{"Url": ..., "Description": ...}``, which a scalar-only record projection
197
+ dropped (a plain string is accepted on write but then makes every subsequent
198
+ *read* of the column fail, live-probed).
199
+ """
200
+ return getattr(field, "type_as_string", None) in _VALUE_OBJECT_TYPES
201
+
202
+
203
+ def _scalar_mapping(value: Any) -> dict | None:
204
+ """A value object as a plain mapping with scalar members, or ``None``.
205
+
206
+ Only a shallow mapping of scalars is accepted, so a navigation property (a
207
+ ``ClientObject``) or a typed payload carrying a nested ``__metadata`` is never
208
+ mistaken for a column value.
209
+ """
210
+ if not isinstance(value, dict) or not value:
211
+ return None
212
+ if any(item is not None and not isinstance(item, (str, int, float, bool)) for item in value.values()):
213
+ return None
214
+ return dict(value)
215
+
216
+
217
+ #: Record columns excluded from the content checksum: server-assigned identity
218
+ #: (``Id``/``GUID``), facets verified separately (timestamps, authorship,
219
+ #: permissions), values the target remaps (``ContentTypeId``), and bookkeeping
220
+ #: (version label, server-derived embed URLs, the navigation ``ParentList``). What
221
+ #: remains is the item's *content* columns, which a faithful copy preserves.
222
+ _CONTENT_EXCLUDED = frozenset(
223
+ {
224
+ "Id",
225
+ "ID",
226
+ "GUID",
227
+ "FileSystemObjectType",
228
+ "Created",
229
+ "Modified",
230
+ "AuthorId",
231
+ "EditorId",
232
+ "HasUniqueRoleAssignments",
233
+ "ContentTypeId",
234
+ "OData__UIVersionString",
235
+ "_UIVersionString",
236
+ "ServerRedirectedEmbedUri",
237
+ "ServerRedirectedEmbedUrl",
238
+ "ParentList",
239
+ "AttachmentFiles",
240
+ }
241
+ )
242
+
243
+ #: Field types whose value is server-derived: a computed column (a link filename, a
244
+ #: doc icon) is a view of the item's identity, not metadata a folder carries.
245
+ _FOLDER_COMPUTED_TYPES = frozenset({"Attachments", "Computed", "File", "WorkflowStatus"})
246
+
247
+
248
+ def _folder_value_columns(fields: list[Any]) -> set[str]:
249
+ """The columns a folder's own metadata can live in (writable, non-system).
250
+
251
+ A read of a folder also returns computed and read-only columns (link filenames,
252
+ compliance flags, version labels); writing those is rejected and comparing them
253
+ would only echo the folder's own name back.
254
+ """
255
+ return {
256
+ field.internal_name
257
+ for field in fields
258
+ if field.internal_name
259
+ # An underscore-prefixed internal name is SharePoint's own storage column; it
260
+ # is not reliably projectable (the same rule the safe fallback projection uses).
261
+ and not field.internal_name.startswith("_")
262
+ and not field.hidden
263
+ and not field.read_only_field
264
+ and field.type_as_string not in _FOLDER_COMPUTED_TYPES
265
+ and field.internal_name not in _FOLDER_RECORD_EXCLUDED
266
+ }
267
+
268
+
269
+ #: Folder (document library) record columns that never travel to the target: the
270
+ #: server-assigned identity and path columns, system metadata (restored by the
271
+ #: fidelity steps), and the folder-specific bookkeeping a read projects. A
272
+ #: ``ContentTypeId`` is kept — a document set's content type is part of what makes
273
+ #: the folder what it is.
274
+ _FOLDER_RECORD_EXCLUDED = (_CONTENT_EXCLUDED - {"ContentTypeId"}) | {
275
+ "FileRef",
276
+ "FileDirRef",
277
+ "FileLeafRef",
278
+ "FSObjType",
279
+ "UniqueId",
280
+ "SortBehavior",
281
+ "SMTotalFileStreamSize",
282
+ "SMTotalFileCount",
283
+ }
284
+
285
+
286
+ def _content_checksum(record: dict, extra_excluded: "frozenset[str]" = frozenset()) -> str:
287
+ """MD5 of an item's content columns in a stable, order-independent form.
288
+
289
+ The list source and target both use this so record content can be reconciled:
290
+ only scalar, multi-value and value-object columns are hashed (navigation
291
+ properties and excluded keys are dropped) and keys are sorted, so the two sides
292
+ agree regardless of property iteration order. A null column is treated as
293
+ absent: the source drops an empty multi-value column while the target reads it
294
+ back as ``None``, and both mean "no value".
295
+ """
296
+ content = {}
297
+ for key, value in record.items():
298
+ if key in _CONTENT_EXCLUDED or key in extra_excluded or value is None:
299
+ continue
300
+ if isinstance(value, (str, int, float, bool, list, tuple)):
301
+ content[key] = value
302
+ continue
303
+ mapping = _scalar_mapping(value)
304
+ if mapping is not None:
305
+ content[key] = mapping
306
+ payload = json.dumps(content, sort_keys=True, default=str)
307
+ return hashlib.md5(payload.encode("utf-8")).hexdigest()
308
+
309
+
310
+ def _folder_record_values(record: dict) -> dict:
311
+ """The column values of a folder item that travel to (or compare with) the target.
312
+
313
+ The projection already narrows the read to the folder's own writable columns; here
314
+ only the identity/system columns are dropped, and an empty value is treated as
315
+ absent on both sides — a source read reports ``""`` where a target read reports
316
+ ``None``, and both mean "no value".
317
+ """
318
+ return {
319
+ key: value for key, value in record.items() if key not in _FOLDER_RECORD_EXCLUDED and value not in (None, "")
320
+ }
321
+
322
+
323
+ def _id_list(value: Any) -> list[Any] | None:
324
+ """Normalize a multi-value ``...Id`` collection into a plain list of ids.
325
+
326
+ A live read returns the collection as a positional mapping (``{0: 1, 1: 2}``);
327
+ a payload this client built may hold ``{"results": [...]}``. Both reduce to the
328
+ same list, which is the form the checksum and the remap work with.
329
+ """
330
+ if isinstance(value, dict):
331
+ if "results" in value:
332
+ value = value["results"]
333
+ else:
334
+ try:
335
+ return [value[i] for i in range(len(value))]
336
+ except (KeyError, TypeError):
337
+ return list(value.values())
338
+ if isinstance(value, (list, tuple)):
339
+ return list(value)
340
+ return None
341
+
342
+
343
+ def _multi_values(value: Any) -> list[Any] | None:
344
+ """A multi-value cell as a plain list of its values, or ``None`` when empty.
345
+
346
+ Accepts every shape a live read or a built payload produces: a positional
347
+ mapping (``{0: "red", 1: "green"}``), a ``{"results": [...]}`` wrapper, a bare
348
+ list, and — because a collection cell may hold exactly one selection — a bare
349
+ scalar.
350
+ """
351
+ if value is None:
352
+ return None
353
+ if isinstance(value, (str, int, float, bool)):
354
+ return [value]
355
+ return _id_list(value)
356
+
357
+
358
+ def _taxonomy_terms(value: Any) -> list[dict[str, str]]:
359
+ """Normalize a taxonomy value to portable ``{"TermGuid": ...}`` terms.
360
+
361
+ A live read shapes a single value as a dict (``{"Label", "TermGuid", "WssId"}``)
362
+ and a multi-value one as a positional mapping (``{0: {...}, 1: {...}}``); a
363
+ payload this client built holds a bare list. All reduce to a list of terms.
364
+ Only ``TermGuid`` is carried: a per-site ``WssId`` is meaningless elsewhere and
365
+ ``Label`` is not authoritative (a read may return the WssId string, and the
366
+ server resolves a write by term id regardless of the label).
367
+ """
368
+ if value is None:
369
+ return []
370
+ if isinstance(value, dict):
371
+ if "TermGuid" in value:
372
+ items: list[Any] = [value]
373
+ elif "results" in value and isinstance(value["results"], (list, tuple)):
374
+ items = list(value["results"])
375
+ else:
376
+ try:
377
+ items = [value[i] for i in range(len(value))]
378
+ except (KeyError, TypeError):
379
+ items = list(value.values())
380
+ elif isinstance(value, (list, tuple)):
381
+ items = list(value)
382
+ else:
383
+ return []
384
+ terms: list[dict[str, str]] = []
385
+ for item in items:
386
+ if isinstance(item, dict):
387
+ guid = item.get("TermGuid")
388
+ else:
389
+ guid = getattr(item, "TermGuid", None)
390
+ if guid:
391
+ terms.append({"TermGuid": str(guid)})
392
+ return terms
393
+
394
+
395
+ def _is_taxonomy_value(value: Any) -> bool:
396
+ """Whether a projected value is a taxonomy term (single dict or multi mapping/list)."""
397
+ return isinstance(value, (dict, list, tuple)) and bool(_taxonomy_terms(value))
398
+
399
+
400
+ def _taxonomy_guids(value: Any) -> list[str]:
401
+ return [term["TermGuid"] for term in _taxonomy_terms(value)]
402
+
403
+
404
+ def _canonicalize_taxonomy_record(record: dict) -> dict:
405
+ """Replace taxonomy values with a sorted list of term ids for comparison.
406
+
407
+ ``Label`` is cosmetic and ``WssId`` is site-local, so neither belongs in the
408
+ content checksum; the term id is the portable identity. Sorting also keeps the
409
+ checksum independent of the order a read happens to return the terms in.
410
+ """
411
+ if not any(_is_taxonomy_value(value) for value in record.values()):
412
+ return record
413
+ canonical = dict(record)
414
+ for key, value in record.items():
415
+ if _is_taxonomy_value(value):
416
+ canonical[key] = sorted(_taxonomy_guids(value))
417
+ return canonical
418
+
419
+
420
+ def _collection_payload(ids: list) -> dict:
421
+ """The JSON shape SharePoint accepts when *creating* a multi-value column.
422
+
423
+ Live-probed: a bare array is rejected on create
424
+ (``InvalidClientQueryException``: "a 'PrimitiveValue' or 'StartObject' node was
425
+ expected") while ``{"results": [...]}`` is accepted. A MERGE *update* is the
426
+ opposite — it accepts the bare array and rejects ``{"results": [...]}`` — so
427
+ only the create path goes through this helper.
428
+ """
429
+ return {"results": ids}
38
430
 
39
431
 
40
- def _sp_timestamp(value: str | None) -> str | None:
41
- """Normalize an ISO-8601 timestamp to the ``...Z`` UTC form SharePoint expects.
432
+ def _sp_utc_timestamp(value: str | None) -> str | None:
433
+ """Normalize a timestamp to the UTC form a *list item* honors.
42
434
 
43
- ``ValidateUpdateListItem`` (``datesInUTC=True``) rejects the ``+00:00`` offset
44
- form the migration items carry, so convert to a naive UTC ``Z`` string.
435
+ Live-probed: ``ValidateUpdateListItem`` on a plain list item parses an
436
+ ISO-8601 value with a trailing ``Z`` as UTC (and ``datesInUTC=True`` keeps it
437
+ that way), so this form round-trips the exact instant.
45
438
  """
46
439
  if not value:
47
440
  return None
48
441
  parsed = parse_datetime(value)
49
442
  if parsed is None:
50
443
  return value
51
- if parsed.tzinfo is not None:
52
- parsed = parsed.astimezone(timezone.utc).replace(tzinfo=None)
53
- return parsed.strftime("%Y-%m-%dT%H:%M:%SZ")
444
+ if parsed.tzinfo is None:
445
+ parsed = parsed.replace(tzinfo=timezone.utc)
446
+ return parsed.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
447
+
448
+
449
+ def _sp_local_timestamp(value: datetime | str | None) -> str | None:
450
+ """Format a *web-local* value as the date form a document item accepts.
451
+
452
+ Live-probed: document-library items ignore ``datesInUTC`` and parse every date
453
+ string in the web's locale, rejecting a trailing ``Z`` (``...must specify a
454
+ valid date within the range of 1/1/1900 and 12/31/8900.``). The space-separated
455
+ ``YYYY-MM-DD HH:MM:SS`` form is accepted and interpreted as local wall-clock,
456
+ so callers must pass the instant already converted to web-local time (see
457
+ ``SharePointLibraryTarget._local_timestamp``).
458
+ """
459
+ if value is None:
460
+ return None
461
+ if isinstance(value, datetime):
462
+ return value.strftime("%Y-%m-%d %H:%M:%S")
463
+ return str(value)
464
+
465
+
466
+ def _report_rejected_fields(result, warnings: list[str], label: str) -> None:
467
+ """Warn about ``ValidateUpdateListItem`` fields the server rejected.
468
+
469
+ A rejected field does not raise over HTTP — the response carries a per-field
470
+ ``HasException``/``ErrorMessage`` — and worse, SharePoint rolls the whole batch
471
+ back, so a sibling ``Created``/``Modified`` write is discarded too. Surfacing it
472
+ keeps that invisible loss honest (and testable).
473
+ """
474
+ values = getattr(result, "value", None) or []
475
+ for value in values:
476
+ if getattr(value, "HasException", False):
477
+ warnings.append(f"{label}: {value.FieldName} rejected ({value.ErrorMessage})")
478
+
479
+
480
+ def _moderation_state(properties: dict) -> dict:
481
+ """The approval fields of a list item, in ``ValidateUpdateListItem`` form.
482
+
483
+ Only present ones are returned: an empty ``_ModerationComments`` normalizes to
484
+ ``None`` server-side, so a blank comment is not carried.
485
+ """
486
+ status = properties.get("OData__ModerationStatus")
487
+ state: dict = {}
488
+ if status is not None:
489
+ state["_ModerationStatus"] = str(status)
490
+ comments = properties.get("OData__ModerationComments")
491
+ if comments:
492
+ state["_ModerationComments"] = str(comments)
493
+ return state
494
+
495
+
496
+ def _version_sort_key(label: str) -> tuple[int, ...]:
497
+ """Sort key for a version label like ``"2.0"`` (numeric parts, non-numeric last)."""
498
+ try:
499
+ return tuple(int(part) for part in str(label).split("."))
500
+ except ValueError:
501
+ return (0,)
502
+
503
+
504
+ #: Library-relative paths SharePoint owns and recreates on the target (its forms
505
+ #: folder and the folder rendering file). Migrating them collides server-side and
506
+ #: clobbers system pages client-side, so the source adapter skips them.
507
+ _LIBRARY_SYSTEM_PATHS = ("Forms", "desktop.ini")
508
+
509
+ #: SiteAssets is the one library that also holds per-site infrastructure — the
510
+ #: site icon and the auto-provisioned OneNote folder.
511
+ _SITE_ASSETS_URL_SUFFIX = "/SiteAssets"
512
+
513
+
514
+ def _site_assets_scaffolding(folder) -> tuple[str, ...]:
515
+ """SiteAssets-only SharePoint-owned entries for this web (empty otherwise).
516
+
517
+ Every modern site provisions ``SiteAssets/<web title> Notebook``; migrating
518
+ it (or reporting it) yields a phantom missing/extra pair because the target's
519
+ folder carries the *target* web's name. The site icon is site branding whose
520
+ bytes are meaningless on another site. Both are skipped on each side.
521
+ """
522
+ url = (folder.server_relative_url or "").rstrip("/")
523
+ if not url.endswith(_SITE_ASSETS_URL_SUFFIX):
524
+ return ()
525
+ web = folder.context.web
526
+ if not web.properties.get("Title"):
527
+ web.ensure_properties(["Title"]).execute_query()
528
+ title = web.title
529
+ names = ["__siteIcon__.png"]
530
+ if title:
531
+ names.append(f"{title} Notebook")
532
+ return tuple(names)
533
+
534
+
535
+ def _is_system_path(rel: str, extra: tuple[str, ...] = ()) -> bool:
536
+ """Whether a library-relative path is SharePoint-owned scaffolding, not user content."""
537
+ entries = (*_LIBRARY_SYSTEM_PATHS, *extra)
538
+ return any(rel == entry or rel.startswith(entry + "/") for entry in entries)
54
539
 
55
540
 
56
541
  def is_taxonomy_validation(exc: Exception) -> bool:
@@ -78,13 +563,367 @@ def taxonomy_internal_names(fields) -> list[str]:
78
563
  return list(dict.fromkeys(names))
79
564
 
80
565
 
566
+ def _perm_mask(role_definition: Any) -> str | None:
567
+ """The decimal ``PermMask`` of a role definition (``BasePermissions`` High/Low)."""
568
+ base = getattr(role_definition, "base_permissions", None)
569
+ if base is None:
570
+ return None
571
+ try:
572
+ return str((int(base.High) << 32) | int(base.Low))
573
+ except (TypeError, ValueError):
574
+ return None
575
+
576
+
577
+ def _is_user_principal(member: Any) -> bool:
578
+ """Whether a role-assignment member is a user (not a group)."""
579
+ ptype = getattr(member, "principal_type", None)
580
+ if ptype is None:
581
+ return False
582
+ if ptype == PrincipalType.User:
583
+ return True
584
+ # ``PrincipalType`` is a plain ``Enum`` (not ``IntEnum``); accept a raw int too.
585
+ return getattr(ptype, "value", ptype) == PrincipalType.User.value
586
+
587
+
588
+ def _is_system_principal(user: Any) -> bool:
589
+ """Whether a resolved user is a tenant system/app account.
590
+
591
+ The Migration API cannot set ``Author``/``Editor`` to a system account, and a
592
+ ``ValidateUpdateListItem`` batch containing one is rejected wholesale — which
593
+ would silently discard the ``Created``/``Modified`` restoration we send in the
594
+ same call. Items created by the migration app itself are authored by such an
595
+ account, so setting it is a no-op anyway; skipping it keeps the timestamp write
596
+ intact.
597
+ """
598
+ login = (getattr(user, "login_name", None) or "").lower()
599
+ return "app@sharepoint" in login or "sharepoint\\system" in login
600
+
601
+
602
+ def _has_unique_role_assignments(securable, *, refresh: bool = False) -> bool:
603
+ """Whether a securable has unique (broken-inheritance) role assignments.
604
+
605
+ SharePoint REST returns *inherited* role assignments too, so reading them
606
+ verbatim would break inheritance on every item and replicate the parent ACL.
607
+ Only items with unique assignments are read/applied (and compared by the
608
+ verifier); inherited items stay inherited on the target.
609
+
610
+ A plain entity read does not return the flag, so an object loaded *before* its
611
+ ACL changed keeps a stale value. The target side passes ``refresh=True``: this
612
+ value decides whether an ACL is applied or compared at all, so a stale
613
+ ``False`` would break inheritance on an item that is already unique (wiping the
614
+ assignments it was supposed to keep) and a stale ``False`` read back after a
615
+ write would report a permission mismatch for an ACL that did land.
616
+ """
617
+ unique = getattr(securable, "has_unique_role_assignments", None)
618
+ if unique is not None and not refresh:
619
+ return bool(unique)
620
+ options = getattr(securable, "query_options", None)
621
+ previous = list(getattr(options, "select", None) or [])
622
+ try:
623
+ securable.select(["HasUniqueRoleAssignments"]).get().execute_query()
624
+ except Exception: # noqa: BLE001 — an unreadable flag is treated as "not unique"
625
+ return False
626
+ finally:
627
+ if options is not None:
628
+ # The projection lives on the object's query options: leaving it on
629
+ # would narrow every later read of the same item.
630
+ options.select = previous
631
+ return bool(securable.properties.get("HasUniqueRoleAssignments"))
632
+
633
+
634
+ def _replay_role_assignments(
635
+ securable,
636
+ principals: "_TargetPrincipals",
637
+ permissions: list[PermissionEntry],
638
+ warnings: list[str],
639
+ label: str,
640
+ ) -> None:
641
+ """Replay an item's unique ACLs onto a target item: additive, never destructive.
642
+
643
+ Every principal is resolved on the target first (users via ``EnsureUser``,
644
+ groups by name, default groups by role kind). Inheritance is broken only when
645
+ the target item still inherits *and* at least one assignment resolved: an item
646
+ whose every principal is unresolvable (a cross-tenant account, an app
647
+ principal) would otherwise end up with an **empty** ACL — inheritance broken,
648
+ nobody granted — which is worse than leaving it inheriting, so that case is
649
+ reported and the item is left alone.
650
+
651
+ A re-run therefore adds only what is missing and never removes an assignment
652
+ the source does not grant. The caller executes the queued requests.
653
+ """
654
+ resolved: list[tuple[Any, Any]] = []
655
+ for entry in permissions:
656
+ principal = principals.principal(entry.principal_name)
657
+ if principal is None:
658
+ continue
659
+ for role_name in entry.roles:
660
+ role = principals.role(role_name)
661
+ if role is None:
662
+ warnings.append(f"permissions {label}: could not resolve role {role_name!r}")
663
+ continue
664
+ resolved.append((principal, role))
665
+ if not resolved:
666
+ warnings.append(
667
+ f"permissions for {label!r} were not restored: no assignment could be resolved on the "
668
+ "target, and breaking inheritance would leave the item with an empty ACL — it still inherits"
669
+ )
670
+ return
671
+ if not _has_unique_role_assignments(securable, refresh=True):
672
+ securable.break_role_inheritance(copy_role_assignments=False, clear_sub_scopes=False)
673
+ for principal, role in resolved:
674
+ securable.add_role_assignment(principal, role)
675
+
676
+
677
+ def _user_login(web, user_id: Any, cache: dict[int, str | None]) -> str | None:
678
+ """Resolve a target user id to its login name, cached."""
679
+ key = parse_int(user_id)
680
+ if key is None:
681
+ return None
682
+ if key in cache:
683
+ return cache[key]
684
+ login: str | None = None
685
+ try:
686
+ user = web.site_users.get_by_id(key).get().execute_query()
687
+ login = user.login_name or None
688
+ except Exception: # noqa: BLE001 — an unresolvable id yields no login
689
+ login = None
690
+ cache[key] = login
691
+ return login
692
+
693
+
694
+ class _MultiValueCreateTarget:
695
+ """An upsert target that reshapes multi-value columns only when creating.
696
+
697
+ A multi-value Lookup/Person column is created with the ``{"results": [...]}``
698
+ wire shape but updated with a bare array (live-probed), so ``keyed_queue`` needs
699
+ to reshape the creates and leave the updates alone. Everything else delegates.
700
+ """
701
+
702
+ def __init__(self, target: Any, reshape: Callable[[dict], dict]) -> None:
703
+ self._target = target
704
+ self._reshape = reshape
705
+
706
+ @property
707
+ def key_field(self) -> str:
708
+ return self._target.key_field
709
+
710
+ def ensure_key_field(self) -> None:
711
+ self._target.ensure_key_field()
712
+
713
+ def load_keys(self) -> Any:
714
+ return self._target.load_keys()
715
+
716
+ def create_records(self, records: list[dict]) -> None:
717
+ self._target.create_records([self._reshape(record) for record in records])
718
+
719
+ def update_record(self, item_id: Any, record: dict) -> None:
720
+ self._target.update_record(item_id, record)
721
+
722
+
723
+ class _DefaultGroups:
724
+ """Maps a web's associated default groups (Owners/Members/Visitors).
725
+
726
+ Default groups are titled after the site (``team Owners``), so a source and
727
+ target in the same tenant rarely share the exact name. These groups are used
728
+ both to resolve a source name onto the target *and* to normalize the name to a
729
+ canonical ``default:<Kind>`` token so the permission diff compares like with
730
+ like (a renamed site still verifies as preserved). Loaded once, lazily.
731
+ """
732
+
733
+ ATTRS = (
734
+ ("Owners", "associated_owner_group"),
735
+ ("Members", "associated_member_group"),
736
+ ("Visitors", "associated_visitor_group"),
737
+ )
738
+
739
+ def __init__(self, web) -> None:
740
+ self._web = web
741
+ self._by_kind: dict[str, Any] | None = None
742
+ self._by_title: dict[str, str] = {}
743
+
744
+ def _load(self) -> None:
745
+ if self._by_kind is not None:
746
+ return
747
+ self._by_kind = {}
748
+ for kind, attr in self.ATTRS:
749
+ group = getattr(self._web, attr, None)
750
+ if group is None:
751
+ continue
752
+ try:
753
+ group.get().execute_query()
754
+ except Exception: # noqa: BLE001 — an unreadable group is skipped
755
+ continue
756
+ self._by_kind[kind] = group
757
+ title = getattr(group, "title", None) or getattr(group, "login_name", None)
758
+ if title:
759
+ self._by_title[title] = kind
760
+
761
+ def group(self, kind: str):
762
+ self._load()
763
+ return self._by_kind.get(kind) if self._by_kind else None
764
+
765
+ def normalize(self, name: str) -> str:
766
+ self._load()
767
+ kind = self._by_title.get(name)
768
+ return f"default:{kind}" if kind else name
769
+
770
+
771
+ class _TargetPrincipals:
772
+ """Resolve source principals and role definitions on a target web (cached).
773
+
774
+ Users are ensured on the site (``EnsureUser``) so their numeric id can be used
775
+ for authorship and role assignments; groups are matched by name, and the
776
+ default SharePoint groups (Owners/Members/Visitors) fall back to the target
777
+ web's associated groups so a renamed site (``team`` -> ``team_out``) still
778
+ maps its role assignments. Anything that cannot be resolved is skipped with a
779
+ warning (same-tenant, best-effort).
780
+ """
781
+
782
+ def __init__(self, web, warnings: list[str]) -> None:
783
+ self._web = web
784
+ self._warnings = warnings
785
+ self._users: dict[str, Any] = {}
786
+ self._roles: "dict[str, RoleDefinition] | None" = None
787
+ self._defaults = _DefaultGroups(web)
788
+
789
+ @staticmethod
790
+ def _strip_claims(name: str) -> str:
791
+ return name.rsplit("|", 1)[-1] if name.startswith("i:0#") else name
792
+
793
+ def normalize(self, name: str) -> str:
794
+ """Canonical principal name (default groups collapse to ``default:<Kind>``)."""
795
+ return self._defaults.normalize(name)
796
+
797
+ def user(self, login: str | None):
798
+ """Ensure a user login on the target site and return its ``User`` object."""
799
+ if not login:
800
+ return None
801
+ login = self._strip_claims(login)
802
+ if login in self._users:
803
+ return self._users[login]
804
+ obj = None
805
+ try:
806
+ obj = self._web.ensure_user(login).execute_query()
807
+ except Exception as e: # noqa: BLE001 — an unresolvable principal is skipped
808
+ self._warnings.append(f"could not ensure target user {login!r}: {e}")
809
+ self._users[login] = obj
810
+ return obj
811
+
812
+ def principal(self, name: str):
813
+ """Resolve a source principal (user or group) to a target principal object."""
814
+ if not name:
815
+ return None
816
+ login = self._strip_claims(name)
817
+ if login.startswith("default:"):
818
+ return self._defaults.group(login.split(":", 1)[1])
819
+ if "@" in login:
820
+ return self.user(login)
821
+ group = None
822
+ try:
823
+ group = self._web.site_groups.get_by_name(login).get().execute_query()
824
+ except Exception: # noqa: BLE001 — fall back to the default groups below
825
+ group = None
826
+ if group is None:
827
+ kind = login.rsplit(" ", 1)[-1]
828
+ if kind in {"Owners", "Members", "Visitors"}:
829
+ group = self._defaults.group(kind)
830
+ if group is not None:
831
+ return group
832
+ self._warnings.append(f"could not resolve principal {name!r} on the target")
833
+ return None
834
+
835
+ def role(self, name: str) -> "RoleDefinition | None":
836
+ """Resolve (and cache) a target role definition by name."""
837
+ if self._roles is None:
838
+ loaded = self._web.role_definitions.get().execute_query()
839
+ self._roles = {rd.name: rd for rd in loaded if rd.name}
840
+ return self._roles.get(name)
841
+
842
+
843
+ def _read_role_assignments(
844
+ securable,
845
+ resolve: Callable[[Any], "Principal | None"] | None = None,
846
+ normalize: Callable[[str], str] | None = None,
847
+ ) -> list[PermissionEntry]:
848
+ """Read a securable object's role assignments into ``PermissionEntry``s.
849
+
850
+ One entry per principal (user or group), keyed by login name; the target uses
851
+ the same shape so the permission diff compares like with like. ``normalize``
852
+ (when supplied) canonicalizes a principal name — default groups collapse to
853
+ ``default:<Kind>`` so a renamed site still compares as preserved. Role names are
854
+ paired with their decimal ``PermMask`` (``role_masks``), and — when ``resolve``
855
+ is supplied — a user principal is enriched with its PUID/display name/email so
856
+ the server-side package target can emit a faithful ``UserGroup.xml`` entry.
857
+ """
858
+ assignments = securable.role_assignments.expand(["Member", "RoleDefinitionBindings"]).get().execute_query()
859
+ result: list[PermissionEntry] = []
860
+ for assignment in assignments:
861
+ member = assignment.member
862
+ name = member.login_name or member.user_principal_name
863
+ if not name:
864
+ continue
865
+ roles: list[str] = []
866
+ role_masks: dict[str, str] = {}
867
+ for role in assignment.role_definition_bindings:
868
+ if not role.name:
869
+ continue
870
+ roles.append(role.name)
871
+ mask = _perm_mask(role)
872
+ if mask is not None:
873
+ role_masks[role.name] = mask
874
+ entry = PermissionEntry(
875
+ principal_name=normalize(name) if normalize is not None else name,
876
+ roles=roles,
877
+ role_masks=role_masks,
878
+ )
879
+ if resolve is not None and _is_user_principal(member):
880
+ principal = resolve(getattr(member, "id", None))
881
+ if principal is not None:
882
+ entry.system_id = principal.system_id
883
+ entry.display_name = principal.display_name
884
+ entry.email = principal.email
885
+ result.append(entry)
886
+ return result
887
+
888
+
81
889
  class SharePointListSource:
82
890
  """Enumerates a SharePoint list's items into record migration items."""
83
891
 
892
+ # Marks this source as emitting list-item attachments as extra items (see DataSource).
893
+ supports_attachments = True
894
+
895
+ # Marks this source as annotating Person columns with a portable login name so the
896
+ # target can resolve them (see ``_annotate_person_logins``).
897
+ supports_typed_values = True
898
+
899
+ def rebind(self) -> "SharePointListSource":
900
+ """This source on its own cloned context, so another thread can drive it.
901
+
902
+ A context owns one pending-query queue: two adapters sharing it interleave
903
+ their deferred queries. Re-addressing the same list (by id) on a ``clone``
904
+ — which shares the authenticated transport — lets a session run several
905
+ list jobs in parallel over one connection pool. Read caches are per-run and
906
+ deliberately not carried over.
907
+ """
908
+ return SharePointListSource(_cloned_list(self._list), self._select)
909
+
84
910
  def __init__(self, source_list: "SPList", select: list[str] | None = None) -> None:
85
911
  self._list = source_list
86
912
  self._select = select
87
913
  self._records: dict[str, dict] = {}
914
+ self._items: dict[str, Any] = {}
915
+ self._attachments: dict[str, Any] = {}
916
+ self._users: dict[int, Principal | None] = {}
917
+ self._default_groups: _DefaultGroups | None = None
918
+ self._source_fields_cache: list[Any] | None = None
919
+ self._user_columns_cache: set[str] | None = None
920
+ self._multi_columns_cache: dict[str, str] | None = None
921
+ self._skipped_columns_cache: set[str] | None = None
922
+ self._taxonomy_columns_cache: dict[str, bool] | None = None
923
+ self._taxonomy_note_columns_cache: set[str] | None = None
924
+ self._multi_choice_columns_cache: set[str] | None = None
925
+ self._value_object_columns_cache: set[str] | None = None
926
+ self._folder_records_cache: dict[str, dict] | None = None
88
927
  self.warnings: list[str] = []
89
928
 
90
929
  def label(self) -> str:
@@ -112,12 +951,29 @@ class SharePointListSource:
112
951
  loaded = self._load_items(["Id", "Title"])
113
952
  return self._project(loaded, progress)
114
953
 
115
- def _load_items(self, select: list[str] | None):
954
+ def _load_items(self, select: list[str] | None, folder_only: bool = False):
116
955
  items = self._list.items
117
956
  # A full read projects ``*`` plus the system metadata so author/editor ids
118
- # are captured reliably; an explicit/safe projection is honored as-is.
119
- columns = ["*", *_METADATA_SELECT] if select is None else select
120
- return items.select(columns).get_all().execute_query()
957
+ # and the unique-ACL flag are captured in one query (``*`` omits them);
958
+ # an explicit/safe projection is honored as-is.
959
+ if select is None:
960
+ columns = ["*", *_METADATA_SELECT]
961
+ # A ``*`` projection also omits multi-value Lookup ids (and returns
962
+ # multi-person ids only inconsistently); select each ``<name>Id``
963
+ # explicitly so the values are not silently lost.
964
+ for name in self._multi_columns():
965
+ key = f"{name}Id"
966
+ if key not in columns:
967
+ columns.append(key)
968
+ # Expand attachments so they can be migrated alongside their item.
969
+ query = items.select(columns).expand(["AttachmentFiles"])
970
+ if folder_only:
971
+ query = query.filter("FSObjType eq 1")
972
+ return query.get_all().execute_query()
973
+ query = items.select(select)
974
+ if folder_only:
975
+ query = query.filter("FSObjType eq 1")
976
+ return query.get_all().execute_query()
121
977
 
122
978
  def _safe_select(self) -> list[str]:
123
979
  """Fallback projection: visible, non-system columns minus Managed Metadata.
@@ -142,34 +998,487 @@ class SharePointListSource:
142
998
  return columns
143
999
 
144
1000
  def _project(self, loaded, progress: MigrationProgress) -> list[MigrationItem]:
145
- from office365.runtime.converters.records import iter_records
146
-
147
- # Reuse the shared record projection (raw values, no JSON coercion) so the
148
- # migration payload matches the data pipeline's.
149
- records = iter_records(loaded, raw=True)
1001
+ records = self._project_records(loaded)
150
1002
  result: list[MigrationItem] = []
1003
+ pending_attachments: list[tuple[str, Any]] = []
151
1004
  for item, record in zip(loaded, records):
152
- self._records[str(item.id)] = record
1005
+ item_id = str(item.id)
1006
+ self._records[item_id] = record
1007
+ self._items[item_id] = item
1008
+ author_id = parse_int(record.get("AuthorId"))
1009
+ editor_id = parse_int(record.get("EditorId"))
1010
+ planned = MigrationItem(
1011
+ source_path=f"{self._list.title}/{item_id}",
1012
+ dest_path=item_id,
1013
+ item_type="record",
1014
+ modified=iso_or_none(record.get("Modified")),
1015
+ created=iso_or_none(record.get("Created")),
1016
+ author_id=author_id,
1017
+ editor_id=editor_id,
1018
+ )
1019
+ # Resolve the source author/editor once so an authorship-preserving
1020
+ # target can restore them by login name (ids are tenant-local).
1021
+ if author_id is not None:
1022
+ planned.author = self._principal_for(author_id)
1023
+ if editor_id is not None:
1024
+ planned.editor = self._principal_for(editor_id)
1025
+ result.append(planned)
1026
+ for attachment in item.properties.get("AttachmentFiles") or []:
1027
+ pending_attachments.append((item_id, attachment))
1028
+ emit_progress(progress, done=len(result), stage="planning", items=[item])
1029
+ # Attachments come after every record so the target can create all parent
1030
+ # items before it resolves and uploads their attachments.
1031
+ for item_id, attachment in pending_attachments:
1032
+ name = getattr(attachment, "file_name", None)
1033
+ if not name:
1034
+ self.warnings.append(f"skipped an attachment without a file name on item {item_id}")
1035
+ continue
1036
+ dest_path = f"{item_id}/{name}"
1037
+ self._attachments[dest_path] = attachment
153
1038
  result.append(
154
1039
  MigrationItem(
155
- source_path=f"{self._list.title}/{item.id}",
156
- dest_path=str(item.id),
157
- item_type="record",
158
- modified=iso_or_none(record.get("Modified")),
159
- created=iso_or_none(record.get("Created")),
160
- author_id=parse_int(record.get("AuthorId")),
161
- editor_id=parse_int(record.get("EditorId")),
1040
+ source_path=f"{self._list.title}/{dest_path}",
1041
+ dest_path=dest_path,
1042
+ item_type="attachment",
162
1043
  )
163
1044
  )
164
- emit_progress(progress, done=len(result), stage="planning", items=[item])
1045
+ emit_progress(progress, done=len(result), stage="planning", items=[attachment])
165
1046
  return result
166
1047
 
167
- def read(self, item: MigrationItem) -> dict:
1048
+ def _project_records(self, loaded, report_skipped: bool = True) -> list[dict]:
1049
+ # Project every loaded property — not the collection's literal ``select``.
1050
+ # Passing ``["*", ...]`` straight through emitted ``{"*": None, ...}``, so
1051
+ # migrated records carried no field values at all. ``AttachmentFiles`` is a
1052
+ # navigation property (objects, not a scalar column) and is excluded; the
1053
+ # target writes attachments separately.
1054
+ select = sorted(
1055
+ {key for item in loaded for key in item.properties if key != "AttachmentFiles"} - _NON_WRITABLE_SELECT
1056
+ )
1057
+ records = records_from_items(list(loaded), select, set(), raw=True)
1058
+ # A REST item create accepts scalar column values only. A full read also
1059
+ # returns navigation properties (``ParentList``) and typed values
1060
+ # (lookups/persons, e.g. ``PredecessorsId`` / ``AssignedTo``) whose JSON
1061
+ # objects carry no ``__metadata.type`` here, so the create batch is
1062
+ # rejected with ``InvalidClientQueryException``. Typed-value remapping is
1063
+ # not supported yet, so drop them and say so rather than fail the run.
1064
+ dropped: set[str] = set()
1065
+ multi = self._multi_columns()
1066
+ multi_keys = {f"{name}Id" for name in multi} | {f"{name}StringId" for name in multi}
1067
+ choices = self._multi_choice_columns()
1068
+ taxonomy = self._taxonomy_columns()
1069
+ value_objects = self._value_object_columns()
1070
+ for record in records:
1071
+ for key, value in list(record.items()):
1072
+ if key in multi_keys:
1073
+ continue # handled by ``_normalize_multi_values``
1074
+ if key in choices:
1075
+ continue # handled by ``_normalize_multi_choice_values``
1076
+ if key in taxonomy:
1077
+ continue # handled by ``_project_taxonomy_values``
1078
+ if key in value_objects and _scalar_mapping(value) is not None:
1079
+ record[key] = _scalar_mapping(value)
1080
+ continue # a value object (``{"Url", "Description"}``) is writable as-is
1081
+ if value is not None and not isinstance(value, (str, int, float, bool)):
1082
+ record.pop(key)
1083
+ if key not in _NAVIGATION_SELECT:
1084
+ dropped.add(key)
1085
+ if dropped:
1086
+ self.warnings.append(
1087
+ f"skipped non-scalar column(s) with no typed-value mapping: {', '.join(sorted(dropped))}"
1088
+ )
1089
+ # Structure provisioning skips hidden columns, so their typed values (and
1090
+ # the text companion) have nowhere to land on the target; drop both rather
1091
+ # than fail the create on an unknown column.
1092
+ skipped = self._skipped_columns()
1093
+ if skipped:
1094
+ skipped_keys = {f"{name}Id" for name in skipped} | {f"{name}StringId" for name in skipped}
1095
+ for record in records:
1096
+ for key in skipped_keys:
1097
+ record.pop(key, None)
1098
+ if report_skipped:
1099
+ self.warnings.append(
1100
+ f"skipped hidden typed column(s) not replicated by structure provisioning: "
1101
+ f"{', '.join(sorted(skipped))}"
1102
+ )
1103
+ self._project_taxonomy_values(records)
1104
+ self._normalize_multi_values(records)
1105
+ self._normalize_multi_choice_values(records)
1106
+ self._annotate_person_logins(records)
1107
+ return records
1108
+
1109
+ def folder_records(self) -> dict[str, dict]:
1110
+ """Column values of this list's folders, keyed by their server-relative URL.
1111
+
1112
+ A document library's folders are list items too: a document set or a managed
1113
+ folder carries its own column values, so a target that only created the empty
1114
+ folder would silently lose them. The values go through the same projection as
1115
+ an item's (typed values normalized, system and navigation columns dropped), so
1116
+ the target can write them onto the folder's underlying item. The read is
1117
+ narrowed to the folder's writable columns (see :meth:`folder_projection`): a
1118
+ ``*`` read also returns system and computed columns (compliance flags, link
1119
+ filenames, version labels) that an update would reject and that carry no
1120
+ portable value.
1121
+ """
1122
+ if self._folder_records_cache is None:
1123
+ records: dict[str, dict] = {}
1124
+ try:
1125
+ loaded = self._load_items(self.folder_projection(), folder_only=True)
1126
+ except Exception as e: # noqa: BLE001 — folder values are best-effort
1127
+ self.warnings.append(f"folder column values could not be read: {e}")
1128
+ loaded = []
1129
+ # The folder projection already excluded the columns an item record would
1130
+ # report as unmigratable, so those messages would describe nothing.
1131
+ for record in self._project_records(loaded, report_skipped=False) if loaded else []:
1132
+ url = record.get("FileRef")
1133
+ if not isinstance(url, str) or not url:
1134
+ continue
1135
+ values = _folder_record_values(record)
1136
+ if values:
1137
+ records[url.rstrip("/")] = values
1138
+ self._folder_records_cache = records
1139
+ return self._folder_records_cache
1140
+
1141
+ def folder_projection(self) -> list[str]:
1142
+ """The columns that carry a folder's own metadata, plus what typed values need.
1143
+
1144
+ A folder has no separate create payload: its values are read back as a record,
1145
+ written onto the folder's item and compared the same way — so the projection
1146
+ must include the companions the typed-value normalization reads (``<name>Id``
1147
+ for Lookup/Person values, the hidden note of a Managed Metadata column).
1148
+ """
1149
+ writable = _folder_value_columns(self._source_fields())
1150
+ # ``FileRef`` keys the record (it is the folder's identity, not a value).
1151
+ columns = ["ContentTypeId", "FileRef", *sorted(writable)]
1152
+ # Lookup and Person values are carried as item ids / logins under
1153
+ # ``<name>Id``; the column itself is carried in the record too.
1154
+ single_lookups = {
1155
+ field.internal_name
1156
+ for field in self._source_fields()
1157
+ if field.internal_name and field.type_as_string == "Lookup" and not _allows_multiple_values(field)
1158
+ }
1159
+ for name in sorted((single_lookups | self._user_columns() | set(self._multi_columns())) & writable):
1160
+ key = f"{name}Id"
1161
+ if key not in columns:
1162
+ columns.append(key)
1163
+ for note in self._taxonomy_note_columns():
1164
+ if note not in columns:
1165
+ columns.append(note)
1166
+ return columns
1167
+
1168
+ def _source_fields(self) -> list[Any]:
1169
+ """The source list's field definitions, read once and shared by every scan."""
1170
+ if self._source_fields_cache is None:
1171
+ self._source_fields_cache = list(self._list.fields.select(list(_SOURCE_FIELD_SELECT)).get().execute_query())
1172
+ return self._source_fields_cache
1173
+
1174
+ def _taxonomy_columns(self) -> dict[str, bool]:
1175
+ """The source's Managed Metadata columns (internal name -> allows many)."""
1176
+ if self._taxonomy_columns_cache is None:
1177
+ columns: dict[str, bool] = {}
1178
+ notes: set[str] = set()
1179
+ try:
1180
+ fields = self._source_fields()
1181
+ by_id = {
1182
+ normalize_guid(getattr(field, "id", None)): getattr(field, "internal_name", None) for field in fields
1183
+ }
1184
+ for field in fields:
1185
+ internal_name = getattr(field, "internal_name", None)
1186
+ if not internal_name:
1187
+ continue
1188
+ field_type = getattr(field, "type_as_string", None)
1189
+ schema = getattr(field, "schema_xml", None) or ""
1190
+ if field_type not in _TAXONOMY_FIELD_TYPES and "TermSetId" not in schema:
1191
+ continue
1192
+ columns[internal_name] = field_type == "TaxonomyFieldTypeMulti" or 'Mult="TRUE"' in schema
1193
+ note = by_id.get(normalize_guid(field.properties.get("TextField")))
1194
+ if note:
1195
+ notes.add(note)
1196
+ except Exception as e: # noqa: BLE001 — values stay un-remapped, never fatal
1197
+ self.warnings.append(f"could not read source columns for taxonomy remapping: {e}")
1198
+ self._taxonomy_columns_cache = columns
1199
+ self._taxonomy_note_columns_cache = notes
1200
+ return self._taxonomy_columns_cache
1201
+
1202
+ def _taxonomy_note_columns(self) -> set[str]:
1203
+ """The hidden note companions of the source's taxonomy columns."""
1204
+ self._taxonomy_columns()
1205
+ return self._taxonomy_note_columns_cache or set()
1206
+
1207
+ def _project_taxonomy_values(self, records: list[dict]) -> None:
1208
+ """Project taxonomy columns as bare ``[{"TermGuid": ...}]`` term lists.
1209
+
1210
+ The target recreates the column against the same term set and rebuilds the
1211
+ write payload, so only the portable term id travels. The hidden note
1212
+ companion (if a projection happens to include it) is dropped.
1213
+ """
1214
+ columns = self._taxonomy_columns()
1215
+ if not columns:
1216
+ return
1217
+ notes = self._taxonomy_note_columns()
1218
+ for record in records:
1219
+ for note in notes:
1220
+ record.pop(note, None)
1221
+ for name in columns:
1222
+ terms = _taxonomy_terms(record.get(name))
1223
+ if terms:
1224
+ record[name] = terms
1225
+ else:
1226
+ record.pop(name, None)
1227
+
1228
+ def _typed_columns(self) -> tuple[set[str], dict[str, str], set[str]]:
1229
+ """The source's Person and multi-value columns, read once.
1230
+
1231
+ Returns ``(single-value User columns, {multi-value column: field type},
1232
+ hidden typed columns)``. The field type tells a multi-lookup (remapped by
1233
+ item id) from a multi-person (remapped by login). Hidden typed columns are
1234
+ reported separately because structure provisioning does not replicate them.
1235
+ """
1236
+ if self._user_columns_cache is None or self._multi_columns_cache is None or self._skipped_columns_cache is None:
1237
+ users: set[str] = set()
1238
+ multi: dict[str, str] = {}
1239
+ skipped: set[str] = set()
1240
+ try:
1241
+ for field in self._source_fields():
1242
+ internal_name = getattr(field, "internal_name", None)
1243
+ if not internal_name or internal_name in _PERSON_EXCLUDED:
1244
+ continue
1245
+ field_type = getattr(field, "type_as_string", None)
1246
+ if field_type not in _TYPED_VALUE_TYPES:
1247
+ continue
1248
+ # Structure replay skips hidden columns, so the target never
1249
+ # gets them; projecting their values would fail the write.
1250
+ if getattr(field, "hidden", None) is True:
1251
+ skipped.add(internal_name)
1252
+ elif field_type.startswith("User"):
1253
+ if _allows_multiple_values(field):
1254
+ multi[internal_name] = "UserMulti"
1255
+ else:
1256
+ users.add(internal_name)
1257
+ elif _allows_multiple_values(field):
1258
+ multi[internal_name] = "LookupMulti"
1259
+ except Exception as e: # noqa: BLE001 — without field types ids stay raw
1260
+ self.warnings.append(f"could not read source columns for typed-value remapping: {e}")
1261
+ self._user_columns_cache = users
1262
+ self._multi_columns_cache = multi
1263
+ self._skipped_columns_cache = skipped
1264
+ return self._user_columns_cache, self._multi_columns_cache, self._skipped_columns_cache
1265
+
1266
+ def _user_columns(self) -> set[str]:
1267
+ """The source's single-value Person columns (internal names)."""
1268
+ return self._typed_columns()[0]
1269
+
1270
+ def _multi_columns(self) -> dict[str, str]:
1271
+ """The source's multi-value Lookup/Person columns (internal name -> field type)."""
1272
+ return self._typed_columns()[1]
1273
+
1274
+ def _skipped_columns(self) -> set[str]:
1275
+ """The source's hidden Person/multi-value columns (not replicated on the target)."""
1276
+ return self._typed_columns()[2]
1277
+
1278
+ def _multi_choice_columns(self) -> set[str]:
1279
+ """The source's multi-choice columns (internal names)."""
1280
+ if self._multi_choice_columns_cache is None:
1281
+ names: set[str] = set()
1282
+ try:
1283
+ names = {
1284
+ field.internal_name
1285
+ for field in self._source_fields()
1286
+ if getattr(field, "internal_name", None) and _is_multi_choice_column(field)
1287
+ }
1288
+ except Exception as e: # noqa: BLE001 — values stay un-normalized, never fatal
1289
+ self.warnings.append(f"could not read source columns for multi-choice values: {e}")
1290
+ self._multi_choice_columns_cache = names
1291
+ return self._multi_choice_columns_cache
1292
+
1293
+ def _value_object_columns(self) -> set[str]:
1294
+ """The source's columns that hold a REST value object (e.g. a hyperlink)."""
1295
+ if self._value_object_columns_cache is None:
1296
+ names: set[str] = set()
1297
+ try:
1298
+ names = {
1299
+ field.internal_name
1300
+ for field in self._source_fields()
1301
+ if getattr(field, "internal_name", None) and _is_value_object_column(field)
1302
+ }
1303
+ except Exception as e: # noqa: BLE001 — values are dropped, never fatal
1304
+ self.warnings.append(f"could not read source columns for value-object values: {e}")
1305
+ self._value_object_columns_cache = names
1306
+ return self._value_object_columns_cache
1307
+
1308
+ def _annotate_person_logins(self, records: list[dict]) -> None:
1309
+ """Replace each Person column's site-user id with the user's login name.
1310
+
1311
+ A site-user id is only meaningful inside its own site collection, so it is
1312
+ meaningless on the target. The login *is* portable, and the target resolves
1313
+ it back to (a possibly different) target site-user id with ``EnsureUser``.
1314
+ A person the source cannot resolve is dropped and reported rather than
1315
+ copied as a wrong id.
1316
+ """
1317
+ columns = self._user_columns()
1318
+ if not columns:
1319
+ return
1320
+ for record in records:
1321
+ for name in columns:
1322
+ # ``<name>StringId`` is a read-only text companion holding the user
1323
+ # id; leaving it in the payload makes REST ignore the remapped
1324
+ # ``<name>Id`` and keep the source id.
1325
+ record.pop(f"{name}StringId", None)
1326
+ key = f"{name}Id"
1327
+ user_id = parse_int(record.get(key))
1328
+ if user_id is None:
1329
+ continue
1330
+ principal = self._principal_for(user_id)
1331
+ login = principal.login_name if principal else None
1332
+ if login:
1333
+ record[key] = login
1334
+ else:
1335
+ record.pop(key, None)
1336
+ self.warnings.append(
1337
+ f"person column '{name}' value {user_id} could not be resolved to a login; left empty"
1338
+ )
1339
+
1340
+ def _normalize_multi_values(self, records: list[dict]) -> None:
1341
+ """Project each multi-value column's ids as a plain list the target can remap.
1342
+
1343
+ A multi-person column additionally carries the portable login names instead
1344
+ of the site-local ids, matching the single-value Person handling.
1345
+ """
1346
+ multi = self._multi_columns()
1347
+ if not multi:
1348
+ return
1349
+ for record in records:
1350
+ for name, field_type in multi.items():
1351
+ record.pop(f"{name}StringId", None)
1352
+ key = f"{name}Id"
1353
+ ids = _id_list(record.get(key))
1354
+ parsed: list[int] = []
1355
+ for value in ids or []:
1356
+ user_id = parse_int(value)
1357
+ if user_id is None:
1358
+ parsed = []
1359
+ break
1360
+ parsed.append(user_id)
1361
+ if not parsed:
1362
+ record.pop(key, None)
1363
+ continue
1364
+ if field_type == "UserMulti":
1365
+ logins: list[str] = []
1366
+ for user_id in parsed:
1367
+ principal = self._principal_for(user_id)
1368
+ login = principal.login_name if principal else None
1369
+ if login:
1370
+ logins.append(login)
1371
+ else:
1372
+ self.warnings.append(
1373
+ f"multi-person column '{name}' value {user_id} could not be resolved "
1374
+ "to a login; the value was left out"
1375
+ )
1376
+ if logins:
1377
+ record[key] = logins
1378
+ else:
1379
+ record.pop(key, None)
1380
+ else:
1381
+ record[key] = parsed
1382
+
1383
+ def _normalize_multi_choice_values(self, records: list[dict]) -> None:
1384
+ """Project each multi-choice column as a plain list of its selected values.
1385
+
1386
+ A live read returns the collection as a positional mapping
1387
+ (``{0: "red", 1: "green"}``); the source records it as a bare list, which is
1388
+ what the checksum compares and what the target wraps for a create. An empty
1389
+ selection is dropped so the column reads back empty rather than as an empty
1390
+ collection.
1391
+ """
1392
+ names = self._multi_choice_columns()
1393
+ if not names:
1394
+ return
1395
+ for record in records:
1396
+ for name in names:
1397
+ if name not in record:
1398
+ continue
1399
+ values = _multi_values(record.get(name))
1400
+ if values:
1401
+ record[name] = values
1402
+ else:
1403
+ record.pop(name, None)
1404
+
1405
+ def read(self, item: MigrationItem) -> object:
1406
+ if item.item_type == "attachment":
1407
+ return self._attachment_content(item)
168
1408
  return self._records.get(item.dest_path, {})
169
1409
 
1410
+ def _attachment_content(self, item: MigrationItem) -> bytes:
1411
+ attachment = self._attachments.get(item.dest_path)
1412
+ if attachment is None:
1413
+ raise FileNotFoundError(item.source_path)
1414
+ return cast(bytes, attachment.get_content().execute_query().value)
1415
+
170
1416
  def checksum(self, item: MigrationItem) -> str:
171
- payload = record_to_json(self._records.get(item.dest_path, {}))
172
- return hashlib.md5(payload.encode("utf-8")).hexdigest()
1417
+ if item.item_type == "attachment":
1418
+ return hashlib.md5(self._attachment_content(item)).hexdigest()
1419
+ return _content_checksum(_canonicalize_taxonomy_record(self._records.get(item.dest_path, {})))
1420
+
1421
+ def read_metadata(self, item: MigrationItem) -> dict | None:
1422
+ """Comparable attributes of the source item (for the post-run metadata diff)."""
1423
+ source_item = self._items.get(item.dest_path)
1424
+ if source_item is None:
1425
+ return None
1426
+ properties = source_item.properties
1427
+ return {
1428
+ "created": iso_or_none(properties.get("Created")),
1429
+ "modified": iso_or_none(properties.get("Modified")),
1430
+ }
1431
+
1432
+ def read_authorship(self, item: MigrationItem) -> tuple[str | None, str | None]:
1433
+ """The source author/editor login names (for the authorship diff)."""
1434
+ return (
1435
+ item.author.login_name if item.author else None,
1436
+ item.editor.login_name if item.editor else None,
1437
+ )
1438
+
1439
+ def _normalize_name(self, name: str) -> str:
1440
+ """Canonicalize a principal name (default groups -> ``default:<Kind>``)."""
1441
+ context = getattr(self._list, "context", None)
1442
+ web = getattr(context, "web", None) if context is not None else None
1443
+ if web is None:
1444
+ return name
1445
+ if self._default_groups is None:
1446
+ self._default_groups = _DefaultGroups(web)
1447
+ return self._default_groups.normalize(name)
1448
+
1449
+ def read_permissions(self, item: MigrationItem) -> list[PermissionEntry]:
1450
+ """Read the item's *unique* role assignments (best-effort ACL preservation).
1451
+
1452
+ Inherited items return ``[]`` — REST would otherwise report the parent's
1453
+ ACL, and copying it would break inheritance on every item.
1454
+ """
1455
+ source_item = self._items.get(item.dest_path)
1456
+ if source_item is None or not _has_unique_role_assignments(source_item):
1457
+ return []
1458
+ return _read_role_assignments(source_item, resolve=self._principal_for, normalize=self._normalize_name)
1459
+
1460
+ def _principal_for(self, user_id: Any) -> "Principal | None":
1461
+ """Resolve a site-user id to a :class:`Principal` (login + PUID), cached."""
1462
+ key = parse_int(user_id)
1463
+ if key is None:
1464
+ return None
1465
+ if key in self._users:
1466
+ return self._users[key]
1467
+ principal: "Principal | None" = None
1468
+ try:
1469
+ user = self._list.context.web.site_users.get_by_id(cast("str", key)).get().execute_query()
1470
+ user_id_info = user.properties.get("UserId")
1471
+ system_id = normalize_puid(getattr(user_id_info, "NameId", None))
1472
+ principal = Principal(
1473
+ login_name=user.login_name or "",
1474
+ system_id=system_id,
1475
+ display_name=user.title,
1476
+ email=user.properties.get("Email"),
1477
+ )
1478
+ except Exception as e: # noqa: BLE001 — a missing user degrades to no authorship
1479
+ self.warnings.append(f"user {key} could not be resolved: {e}")
1480
+ self._users[key] = principal
1481
+ return principal
173
1482
 
174
1483
  def close(self) -> None:
175
1484
  pass
@@ -180,34 +1489,132 @@ class SharePointListTarget:
180
1489
 
181
1490
  Pass ``key`` (one or more natural-key columns) to make the target idempotent:
182
1491
  a hash of those columns is stored in ``key_field`` and each payload is created
183
- or (``on_conflict="upsert"``) updated, so re-runs never duplicate. Without a
1492
+ or (``on_conflict="update"``) updated, so re-runs never duplicate. Without a
184
1493
  key, records are appended and idempotency relies on the checkpoint/manifest.
1494
+
1495
+ ``on_conflict`` may be passed explicitly; otherwise it is derived from
1496
+ ``MigrationOptions.conflict_resolution`` by :meth:`configure` (the runner calls
1497
+ it before writes): ``OVERWRITE`` → ``"update"``, anything else → ``"skip"``.
1498
+
1499
+ Every migrated item also gets a source-id marker (``identity_field``, default
1500
+ ``MigrationSourceId``): the default ``SKIP`` policy then recognizes items already
1501
+ on the target (so re-runs do not duplicate) and :meth:`list_paths` pairs source
1502
+ and target items for verification. Pass ``identity_field=None`` to opt out.
1503
+ List-item attachments are written after their parent records are flushed.
1504
+
1505
+ Pass ``content_type_map`` (a :class:`~office365.migration.map.ContentTypeMap`)
1506
+ when the target list's content types were recreated with fresh identifiers:
1507
+ every record's ``ContentTypeId`` is rewritten to the target id so items land
1508
+ under the recreated content type instead of the wrong (or a missing) one.
185
1509
  """
186
1510
 
1511
+ # Marks this target as accepting list-item attachments as extra items (see DataTarget).
1512
+ supports_attachments = True
1513
+
1514
+ # Marks this target as remapping Lookup values across migrated lists and Person
1515
+ # values by login (see ``_remap_typed_values``).
1516
+ supports_typed_values = True
1517
+
1518
+ # Marks this target as replaying a list item's approval state (``_ModerationStatus``
1519
+ # and its comments) in the metadata write, which a system update would otherwise
1520
+ # reset. Check-out and draft-version state are still not carried.
1521
+ supports_item_state = True
1522
+
1523
+ #: The single-item :meth:`write` only queues a create; a chunk must be flushed
1524
+ #: before it is durable. The runner therefore routes this target through the
1525
+ #: chunked path even at ``concurrency == 1``, so checkpoints never record
1526
+ #: ``DONE`` ahead of the actual write.
1527
+ deferred_writes = True
1528
+
1529
+ def rebind(self) -> "SharePointListTarget":
1530
+ """This target on its own cloned context (see :meth:`SharePointListSource.rebind`)."""
1531
+ return SharePointListTarget(
1532
+ _cloned_list(self._list),
1533
+ key=list(self._key_columns) or None,
1534
+ key_field=self._key_field,
1535
+ on_conflict=self._on_conflict_override,
1536
+ identity_field=self._identity_field,
1537
+ content_type_map=self._content_type_map,
1538
+ )
1539
+
187
1540
  def __init__(
188
1541
  self,
189
1542
  target_list: "SPList",
190
1543
  key: str | list[str] | None = None,
191
1544
  key_field: str = "MigrationKey",
192
- on_conflict: str = "skip",
1545
+ on_conflict: str | None = None,
1546
+ identity_field: str | None = DEFAULT_LIST_IDENTITY_FIELD,
1547
+ content_type_map: "ContentTypeMap | None" = None,
193
1548
  ) -> None:
194
1549
  self._list = target_list
195
1550
  self._key_columns = [key] if isinstance(key, str) else (list(key) if key else [])
196
1551
  self._key_field = key_field
197
- self._on_conflict = on_conflict
1552
+ self._on_conflict_override = on_conflict
1553
+ self._on_conflict = on_conflict or "skip"
198
1554
  self._target = None
199
1555
  self._existing: dict = {}
1556
+ self._identity_field = identity_field
1557
+ self._identity_ensured = False
1558
+ self._identity_index: dict | None = None
1559
+ self._identity_index_stale = False
1560
+ self._identity_index_max_id = 0
1561
+ self._attachment_path_cache: set[str] | None = None
1562
+ self._content_type_map = content_type_map
1563
+ self._login_cache: dict[int, str | None] = {}
1564
+ # Author/Editor values already written by ``apply_authorship`` but pending a
1565
+ # shared final write (see ``apply_timestamps``); avoids the ValidateUpdateListItem
1566
+ # call that restores timestamps silently resetting ``Editor`` to the caller.
1567
+ self._pending_authorship: dict[str, dict] = {}
1568
+ # Approval state read from the payload, replayed in the metadata write (a
1569
+ # system update approves the item); same pending-then-pop lifecycle as above.
1570
+ self._pending_approval: dict[str, dict[str, str]] = {}
1571
+ self.warnings: list[str] = []
1572
+ self._principals_cache: _TargetPrincipals | None = None
1573
+ # Lookup and Person columns carry an id that is only meaningful on the
1574
+ # source (list-local item id / site-user id). The target remaps each to its
1575
+ # own value on write and back to the source value on verification.
1576
+ self._columns_cache: _TypedColumns | None = None
1577
+ self._lookup_indexes: dict[str, tuple[dict[int, int], dict[int, int]]] = {}
1578
+ self._unresolved_lookups: set[tuple[str, int]] = set()
1579
+ self._unresolved_users: set[tuple[str, str]] = set()
1580
+ self._user_logins: dict[int, str] | None = None
1581
+
1582
+ @property
1583
+ def on_conflict(self) -> str:
1584
+ """The active record conflict policy (``"skip"`` or ``"update"``)."""
1585
+ return self._on_conflict
1586
+
1587
+ def configure(self, options) -> None:
1588
+ """Derive ``on_conflict`` from ``MigrationOptions.conflict_resolution``.
1589
+
1590
+ Called by :class:`MigrationRunner` before the run, so a single
1591
+ ``conflict_resolution`` drives both file and record targets. An
1592
+ ``on_conflict`` passed to the constructor is honored as-is.
1593
+ """
1594
+ if self._on_conflict_override is None:
1595
+ self._on_conflict = conflict_to_on_conflict(options.conflict_resolution)
200
1596
 
201
1597
  def label(self) -> str:
202
1598
  return f"list:{self._list.title}"
203
1599
 
204
1600
  def exists(self, item: MigrationItem) -> bool:
205
- # Keyed dedup happens in _queue (the key is derived from the payload, which
206
- # exists() doesn't receive); without a key, records are appended.
207
- return False
1601
+ # With the identity marker, both records and attachments are recognized on a
1602
+ # re-run, so the default ``SKIP`` policy is genuinely idempotent. Without it
1603
+ # (``identity_field=None``) nothing is recognized and records append.
1604
+ if not self._identity_field:
1605
+ return False
1606
+ self._ensure_identity()
1607
+ if not self._identity_field:
1608
+ return False
1609
+ if item.item_type == "attachment":
1610
+ return str(item.dest_path) in set(self._attachment_paths())
1611
+ return self._find_by_identity(item.dest_path) is not None
208
1612
 
209
1613
  def write(self, item: MigrationItem, payload: object) -> None:
210
- self._queue([cast(dict, payload)])
1614
+ if item.item_type == "attachment":
1615
+ self._write_attachment(item, payload)
1616
+ return
1617
+ self._queue([self._record_for(item, payload)])
211
1618
 
212
1619
  def write_many(
213
1620
  self,
@@ -219,19 +1626,67 @@ class SharePointListTarget:
219
1626
 
220
1627
  Flushing per chunk (instead of only at :meth:`commit`) keeps a large list
221
1628
  migration memory-bounded — the queued creates never accumulate for the
222
- whole run.
1629
+ whole run. Attachments in the chunk are written after its records are
1630
+ flushed, so their parent items already exist.
223
1631
 
224
1632
  Returns:
225
1633
  No failures (a batch failure raises; there are no partial successes).
226
1634
  """
227
- self._queue([cast(dict, payload) for payload in payloads])
228
- self._flush(len(payloads), concurrency)
1635
+ records: list[dict] = []
1636
+ attachments: list[tuple[MigrationItem, object]] = []
1637
+ for item, payload in zip(items, payloads):
1638
+ if item.item_type == "attachment":
1639
+ attachments.append((item, payload))
1640
+ else:
1641
+ records.append(self._record_for(item, payload))
1642
+ if records:
1643
+ self._queue(records)
1644
+ self._flush(len(records), concurrency)
1645
+ for item, payload in attachments:
1646
+ self._write_attachment(item, payload)
229
1647
  return []
230
1648
 
1649
+ def _record_for(self, item: MigrationItem, payload: object) -> dict:
1650
+ """The payload with the target content-type id and source-id marker applied."""
1651
+ record = dict(cast(dict, payload))
1652
+ if self._content_type_map:
1653
+ content_type_id = record.get("ContentTypeId")
1654
+ record["ContentTypeId"] = self._content_type_map.resolve(content_type_id)
1655
+ if self._identity_field:
1656
+ record[self._identity_field] = item.dest_path
1657
+ approval = {
1658
+ field_name: str(record[record_key])
1659
+ for record_key, field_name in _APPROVAL_FIELDS.items()
1660
+ if record.get(record_key) is not None
1661
+ }
1662
+ if approval:
1663
+ self._pending_approval[item.dest_path] = approval
1664
+ return record
1665
+
1666
+ def translate_record(self, record: dict) -> dict:
1667
+ """A source record mapped onto this list without writing it.
1668
+
1669
+ A document library folder is a list item that already exists on the target,
1670
+ so its column values cannot go through the create queue: the record is mapped
1671
+ here (target content-type id, Lookup ids, Person logins, multi-value shapes)
1672
+ and applied with an update instead.
1673
+ """
1674
+ mapped = dict(record)
1675
+ if self._content_type_map:
1676
+ resolved = self._content_type_map.resolve(mapped.get("ContentTypeId"))
1677
+ if resolved:
1678
+ mapped["ContentTypeId"] = resolved
1679
+ else:
1680
+ mapped.pop("ContentTypeId", None)
1681
+ self._remap_typed_values([mapped])
1682
+ return mapped
1683
+
231
1684
  def _queue(self, records: list[dict]) -> None:
1685
+ self._ensure_identity()
1686
+ self._remap_typed_values(records)
232
1687
  if not self._key_columns:
233
1688
  for record in records:
234
- self._list.items.queue_records([record])
1689
+ self._list.items.queue_records([self._multi_value_create(record)])
235
1690
  return
236
1691
  from office365.runtime.converters.upsert import keyed_queue
237
1692
  from office365.sharepoint.fields.name import internal_field_name
@@ -245,30 +1700,700 @@ class SharePointListTarget:
245
1700
  self._target = target
246
1701
  key_columns = [internal_field_name(c) for c in self._key_columns]
247
1702
  keyed_queue(
248
- target,
1703
+ _MultiValueCreateTarget(target, self._multi_value_create),
249
1704
  records,
250
1705
  key_columns=key_columns,
251
1706
  existing=self._existing,
252
1707
  on_conflict=self._on_conflict,
253
1708
  )
254
1709
 
1710
+ def _multi_id_keys(self) -> set[str]:
1711
+ """The ``...Id`` record keys of the multi-value Lookup/Person columns."""
1712
+ names = (*self._multi_lookup_columns(), *self._multi_user_columns())
1713
+ return {f"{name}Id" for name in names}
1714
+
1715
+ def _multi_value_create(self, record: dict) -> dict:
1716
+ """Reshape multi-value columns for a *create* (``{"results": [...]}``).
1717
+
1718
+ A bare list is the semantic value the source records and the checksum work
1719
+ with, and it is also what a MERGE update wants; only a create needs the
1720
+ ``{"results": [...]}`` wrapper (live-probed). Lists without multi-value
1721
+ columns are passed through untouched.
1722
+ """
1723
+ keys = self._multi_id_keys() | self._multi_choice_columns()
1724
+ if not keys:
1725
+ return record
1726
+ wrapped = dict(record)
1727
+ for key in keys:
1728
+ values = _multi_values(wrapped.get(key))
1729
+ if values is not None:
1730
+ wrapped[key] = _collection_payload(values)
1731
+ return wrapped
1732
+
1733
+ def _ensure_identity(self) -> None:
1734
+ """Create the source-id marker column (once), best-effort."""
1735
+ if self._identity_ensured or not self._identity_field:
1736
+ return
1737
+ self._identity_ensured = True
1738
+ name = self._identity_field
1739
+ from office365.sharepoint.fields.creation_information import FieldCreationInformation
1740
+ from office365.sharepoint.fields.type import FieldType
1741
+
1742
+ try:
1743
+ self._list.fields.ensure(FieldCreationInformation(name, FieldType.Text)).execute_query()
1744
+ except Exception as e: # noqa: BLE001 — identity is best-effort; content still migrates
1745
+ self._identity_field = None
1746
+ self.warnings.append(f"could not create the {name!r} identity column: {e}")
1747
+
1748
+ def _columns(self) -> _TypedColumns:
1749
+ """The target's typed columns.
1750
+
1751
+ Returns ``(lookup: name -> referenced list id, user names, multi-lookup:
1752
+ name -> referenced list id, multi-user names, taxonomy: name -> (allows
1753
+ many, hidden note field internal name), multi-choice names)``.
1754
+ """
1755
+ if self._columns_cache is None:
1756
+ self._columns_cache = self._load_columns()
1757
+ return self._columns_cache
1758
+
1759
+ def _lookup_columns(self) -> dict[str, str]:
1760
+ return self._columns()[0]
1761
+
1762
+ def _user_columns(self) -> set[str]:
1763
+ return self._columns()[1]
1764
+
1765
+ def _multi_lookup_columns(self) -> dict[str, str]:
1766
+ return self._columns()[2]
1767
+
1768
+ def _multi_user_columns(self) -> set[str]:
1769
+ return self._columns()[3]
1770
+
1771
+ def _taxonomy_columns(self) -> dict[str, tuple[bool, str | None]]:
1772
+ return self._columns()[4]
1773
+
1774
+ def _multi_choice_columns(self) -> set[str]:
1775
+ return self._columns()[5]
1776
+
1777
+ def _load_columns(self) -> _TypedColumns:
1778
+ lookups: dict[str, str] = {}
1779
+ users: set[str] = set()
1780
+ multi_lookups: dict[str, str] = {}
1781
+ multi_users: set[str] = set()
1782
+ taxonomy: dict[str, tuple[bool, str | None]] = {}
1783
+ multi_choices: set[str] = set()
1784
+ try:
1785
+ fields = (
1786
+ self._list.fields.select(
1787
+ ["InternalName", "TypeAsString", "LookupList", "Id", "TextField", "AllowMultipleValues"]
1788
+ )
1789
+ .get()
1790
+ .execute_query()
1791
+ )
1792
+ except Exception as e: # noqa: BLE001 — without columns we cannot remap; content still migrates
1793
+ self.warnings.append(f"could not read target columns for typed-value remapping: {e}")
1794
+ return lookups, users, multi_lookups, multi_users, taxonomy, multi_choices
1795
+ by_id = {normalize_guid(getattr(field, "id", None)): getattr(field, "internal_name", None) for field in fields}
1796
+ for field in fields:
1797
+ internal_name = getattr(field, "internal_name", None)
1798
+ if not internal_name or internal_name in _PERSON_EXCLUDED:
1799
+ continue
1800
+ field_type = getattr(field, "type_as_string", None)
1801
+ if field_type in _TAXONOMY_FIELD_TYPES:
1802
+ # The companion note column that actually carries the write payload
1803
+ # has an unpredictable name; resolve it through the column's
1804
+ # ``TextField`` id.
1805
+ note_name = by_id.get(normalize_guid(field.properties.get("TextField")))
1806
+ taxonomy[internal_name] = (field_type == "TaxonomyFieldTypeMulti", note_name)
1807
+ elif field_type in _TYPED_VALUE_TYPES:
1808
+ multiple = _allows_multiple_values(field)
1809
+ if field_type.startswith("User"):
1810
+ if multiple:
1811
+ multi_users.add(internal_name)
1812
+ else:
1813
+ users.add(internal_name)
1814
+ else:
1815
+ list_id = normalize_guid(field.properties.get("LookupList"))
1816
+ if not list_id:
1817
+ continue
1818
+ if multiple:
1819
+ multi_lookups[internal_name] = list_id
1820
+ else:
1821
+ lookups[internal_name] = list_id
1822
+ elif _is_multi_choice_column(field):
1823
+ multi_choices.add(internal_name)
1824
+ return lookups, users, multi_lookups, multi_users, taxonomy, multi_choices
1825
+
1826
+ def _lookup_index(self, target_list_id: str) -> tuple[dict[int, int], dict[int, int]]:
1827
+ """``(source id -> target id, target id -> source id)`` for a referenced list."""
1828
+ cached = self._lookup_indexes.get(target_list_id)
1829
+ if cached is not None:
1830
+ return cached
1831
+ forward: dict[int, int] = {}
1832
+ backward: dict[int, int] = {}
1833
+ self._flush_pending()
1834
+ try:
1835
+ referenced = self._list.context.web.lists.get_by_id(target_list_id)
1836
+ items = referenced.items.select(["Id", DEFAULT_LIST_IDENTITY_FIELD]).get_all().execute_query()
1837
+ except Exception as e: # noqa: BLE001 — an unreadable referenced list is reported, not fatal
1838
+ self.warnings.append(f"lookup target list {target_list_id} could not be read: {e}")
1839
+ items = []
1840
+ for referenced_item in items:
1841
+ source_id = parse_int(referenced_item.properties.get(DEFAULT_LIST_IDENTITY_FIELD))
1842
+ target_id = parse_int(referenced_item.id)
1843
+ if source_id is None or target_id is None:
1844
+ continue
1845
+ forward[source_id] = target_id
1846
+ backward[target_id] = source_id
1847
+ self._lookup_indexes[target_list_id] = (forward, backward)
1848
+ return forward, backward
1849
+
1850
+ def _remap_typed_values(self, records: list[dict]) -> None:
1851
+ """Translate each record's source lookup/person ids into target values.
1852
+
1853
+ A lookup references another migrated list (matched on its items' source-id
1854
+ marker); a person carries the *login name* the source annotated, resolved
1855
+ here with ``EnsureUser``. Either value with no match is dropped and reported
1856
+ once rather than written as a wrong id.
1857
+ """
1858
+ self._remap_lookups(records)
1859
+ self._remap_users(records)
1860
+ self._remap_multi_lookups(records)
1861
+ self._remap_multi_users(records)
1862
+ self._remap_taxonomies(records)
1863
+
1864
+ def _remap_lookups(self, records: list[dict]) -> None:
1865
+ columns = self._lookup_columns()
1866
+ if not columns:
1867
+ return
1868
+ for record in records:
1869
+ for internal_name, target_list_id in columns.items():
1870
+ key = f"{internal_name}Id"
1871
+ source_id = parse_int(record.get(key))
1872
+ if source_id is None:
1873
+ continue
1874
+ forward, _ = self._lookup_index(target_list_id)
1875
+ target_id = forward.get(source_id)
1876
+ if target_id is None:
1877
+ record.pop(key, None)
1878
+ self._report_unresolved_lookup(internal_name, source_id)
1879
+ continue
1880
+ record[key] = target_id
1881
+
1882
+ def _remap_users(self, records: list[dict]) -> None:
1883
+ columns = self._user_columns()
1884
+ if not columns:
1885
+ return
1886
+ for record in records:
1887
+ for internal_name in columns:
1888
+ key = f"{internal_name}Id"
1889
+ login = record.get(key)
1890
+ if not isinstance(login, str) or not login:
1891
+ continue
1892
+ user = self._principals().user(login)
1893
+ if user is None or getattr(user, "id", None) is None:
1894
+ record.pop(key, None)
1895
+ self._report_unresolved_user(internal_name, login)
1896
+ continue
1897
+ record[key] = user.id
1898
+
1899
+ def _remap_multi_lookups(self, records: list[dict]) -> None:
1900
+ columns = self._multi_lookup_columns()
1901
+ if not columns:
1902
+ return
1903
+ for record in records:
1904
+ for internal_name, target_list_id in columns.items():
1905
+ key = f"{internal_name}Id"
1906
+ source_ids = _id_list(record.get(key))
1907
+ if not source_ids:
1908
+ continue
1909
+ forward, _ = self._lookup_index(target_list_id)
1910
+ target_ids: list[int] = []
1911
+ for value in source_ids:
1912
+ source_id = parse_int(value)
1913
+ target_id = forward.get(source_id) if source_id is not None else None
1914
+ if target_id is None:
1915
+ if source_id is not None:
1916
+ self._report_unresolved_lookup(internal_name, source_id)
1917
+ continue
1918
+ target_ids.append(target_id)
1919
+ if target_ids:
1920
+ record[key] = target_ids
1921
+ else:
1922
+ record.pop(key, None)
1923
+
1924
+ def _remap_multi_users(self, records: list[dict]) -> None:
1925
+ columns = self._multi_user_columns()
1926
+ if not columns:
1927
+ return
1928
+ for record in records:
1929
+ for internal_name in columns:
1930
+ key = f"{internal_name}Id"
1931
+ logins = record.get(key)
1932
+ if not isinstance(logins, (list, tuple)) or not logins:
1933
+ continue
1934
+ target_ids: list[int] = []
1935
+ for login in logins:
1936
+ user = self._principals().user(login) if isinstance(login, str) and login else None
1937
+ user_id = getattr(user, "id", None)
1938
+ if user is None or user_id is None:
1939
+ self._report_unresolved_user(internal_name, str(login))
1940
+ continue
1941
+ target_ids.append(user_id)
1942
+ if target_ids:
1943
+ record[key] = target_ids
1944
+ else:
1945
+ record.pop(key, None)
1946
+
1947
+ def _remap_taxonomies(self, records: list[dict]) -> None:
1948
+ """Write taxonomy terms in the form SharePoint accepts on the target.
1949
+
1950
+ A single-value column takes a typed ``SP.Taxonomy.TaxonomyFieldValue``
1951
+ object; a multi-value column only accepts the hidden note companion's text
1952
+ form ``-1;#Label|guid;#...`` (typed-collection JSON is rejected server-side).
1953
+ The term id is authoritative — the label can be left blank. A column with no
1954
+ target counterpart is dropped and reported rather than sent as an unknown
1955
+ property.
1956
+ """
1957
+ columns = self._taxonomy_columns()
1958
+ unresolved: set[str] = set()
1959
+ for record in records:
1960
+ for internal_name, value in list(record.items()):
1961
+ if not _is_taxonomy_value(value):
1962
+ continue
1963
+ guids = _taxonomy_guids(value)
1964
+ column = columns.get(internal_name)
1965
+ if column is None:
1966
+ record.pop(internal_name, None)
1967
+ unresolved.add(internal_name)
1968
+ continue
1969
+ allow_many, note_name = column
1970
+ if not guids:
1971
+ record.pop(internal_name, None)
1972
+ continue
1973
+ if allow_many and note_name:
1974
+ record.pop(internal_name, None)
1975
+ record[note_name] = ";#".join(f"-1;#|{guid}" for guid in guids)
1976
+ elif allow_many:
1977
+ record.pop(internal_name, None)
1978
+ unresolved.add(internal_name)
1979
+ else:
1980
+ record[internal_name] = {
1981
+ "__metadata": {"type": "SP.Taxonomy.TaxonomyFieldValue"},
1982
+ "Label": "",
1983
+ "TermGuid": guids[0],
1984
+ "WssId": -1,
1985
+ }
1986
+ if unresolved:
1987
+ self.warnings.append(
1988
+ f"taxonomy column(s) not available on the target, values left empty: {', '.join(sorted(unresolved))}"
1989
+ )
1990
+
1991
+ def _report_unresolved_user(self, internal_name: str, login: str) -> None:
1992
+ marker = (internal_name, login)
1993
+ if marker in self._unresolved_users:
1994
+ return
1995
+ self._unresolved_users.add(marker)
1996
+ self.warnings.append(
1997
+ f"person column '{internal_name}' login {login!r} could not be resolved on the target; "
1998
+ "the value was left empty"
1999
+ )
2000
+
2001
+ def _target_user_logins(self) -> dict[int, str]:
2002
+ """The target web's ``{site-user id: login name}`` map (cached)."""
2003
+ if self._user_logins is None:
2004
+ mapping: dict[int, str] = {}
2005
+ try:
2006
+ for site_user in self._list.context.web.site_users.get().execute_query():
2007
+ user_id = parse_int(site_user.id)
2008
+ if user_id is not None and site_user.login_name:
2009
+ mapping[user_id] = site_user.login_name
2010
+ except Exception as e: # noqa: BLE001 — without the map persons are left as ids
2011
+ self.warnings.append(f"could not read target site users for person verification: {e}")
2012
+ self._user_logins = mapping
2013
+ return self._user_logins
2014
+
2015
+ def _report_unresolved_lookup(self, internal_name: str, source_id: int) -> None:
2016
+ marker = (internal_name, source_id)
2017
+ if marker in self._unresolved_lookups:
2018
+ return
2019
+ self._unresolved_lookups.add(marker)
2020
+ self.warnings.append(
2021
+ f"lookup column '{internal_name}' value {source_id} could not be mapped to a target item "
2022
+ "(the referenced list's items were not migrated first); the value was left empty"
2023
+ )
2024
+
2025
+ def _canonicalize_lookups(self, properties: dict) -> None:
2026
+ """Rewrite target lookup ids back to the source ids (symmetric to the write).
2027
+
2028
+ The source checksum holds the source item id; the target's fresh item holds
2029
+ the target id. Mapping the latter back lets the content spot-check compare
2030
+ the two like for like instead of reporting a false mismatch.
2031
+ """
2032
+ for internal_name, target_list_id in self._lookup_columns().items():
2033
+ key = f"{internal_name}Id"
2034
+ target_id = parse_int(properties.get(key))
2035
+ if target_id is None:
2036
+ continue
2037
+ _, backward = self._lookup_index(target_list_id)
2038
+ source_id = backward.get(target_id)
2039
+ if source_id is not None:
2040
+ properties[key] = source_id
2041
+ for internal_name, target_list_id in self._multi_lookup_columns().items():
2042
+ key = f"{internal_name}Id"
2043
+ target_ids = _id_list(properties.get(key))
2044
+ if not target_ids:
2045
+ continue
2046
+ _, backward = self._lookup_index(target_list_id)
2047
+ mapped: list[Any] = []
2048
+ for value in target_ids:
2049
+ target_id = parse_int(value)
2050
+ mapped.append(backward.get(target_id, value) if target_id is not None else value)
2051
+ properties[key] = mapped
2052
+
2053
+ def _canonicalize_users(self, properties: dict) -> None:
2054
+ """Rewrite target person ids back to the login names the source recorded."""
2055
+ columns = self._user_columns()
2056
+ logins = self._target_user_logins() if columns or self._multi_user_columns() else {}
2057
+ for internal_name in columns:
2058
+ key = f"{internal_name}Id"
2059
+ user_id = parse_int(properties.get(key))
2060
+ if user_id is None:
2061
+ continue
2062
+ login = logins.get(user_id)
2063
+ if login is not None:
2064
+ properties[key] = login
2065
+ for internal_name in self._multi_user_columns():
2066
+ key = f"{internal_name}Id"
2067
+ user_ids = _id_list(properties.get(key))
2068
+ if not user_ids:
2069
+ continue
2070
+ mapped_logins: list[Any] = []
2071
+ for value in user_ids:
2072
+ user_id = parse_int(value)
2073
+ mapped_logins.append(logins.get(user_id, value) if user_id is not None else value)
2074
+ properties[key] = mapped_logins
2075
+
2076
+ def _canonicalize_multi_choices(self, properties: dict) -> None:
2077
+ """Reduce a read-back multi-choice cell to the bare list the source records.
2078
+
2079
+ A live read returns the selected values as a positional mapping
2080
+ (``{0: "red", 1: "green"}``); the source checksum holds a bare list, so the
2081
+ two are only comparable after the same projection.
2082
+ """
2083
+ for internal_name in self._multi_choice_columns():
2084
+ if internal_name not in properties:
2085
+ continue
2086
+ values = _multi_values(properties.get(internal_name))
2087
+ if values:
2088
+ properties[internal_name] = values
2089
+ else:
2090
+ properties.pop(internal_name, None)
2091
+
2092
+ def _write_attachment(self, item: MigrationItem, payload: object) -> None:
2093
+ if not self._identity_field:
2094
+ raise NotImplementedError("attachments need the identity column, which could not be created")
2095
+ parent_id, _, filename = str(item.dest_path).partition("/")
2096
+ if not filename:
2097
+ raise ValueError(f"invalid attachment destination {item.dest_path!r}")
2098
+ self._flush_pending()
2099
+ parent = self._find_by_identity(parent_id)
2100
+ if parent is None:
2101
+ raise ValueError(f"no target item for source id {parent_id!r}; cannot attach {filename!r}")
2102
+ content = payload if isinstance(payload, bytes) else str(payload).encode("utf-8")
2103
+ parent.attachment_files.add({"filename": filename, "content": content}).execute_query()
2104
+ if self._attachment_path_cache is not None:
2105
+ self._attachment_path_cache.add(str(item.dest_path))
2106
+
2107
+ def _flush_pending(self) -> None:
2108
+ if self._list.context.has_pending_request:
2109
+ self._flush(DEFAULT_BATCH_SIZE, 1)
2110
+
2111
+ def _find_by_identity(self, source_id: str):
2112
+ if self._identity_index is None or self._identity_index_stale:
2113
+ self._refresh_identity_index()
2114
+ return self._identity_index.get(str(source_id)) if self._identity_index else None
2115
+
2116
+ def _refresh_identity_index(self) -> None:
2117
+ """Load, or incrementally extend, the source-id -> target item map.
2118
+
2119
+ After a chunk is flushed only the newly created items are re-read
2120
+ (``Id gt <max>``) instead of re-scanning the whole list, so refreshing the
2121
+ index per chunk stays linear rather than O(n²).
2122
+ """
2123
+ if not self._identity_field:
2124
+ self._identity_index = {}
2125
+ return
2126
+ if self._identity_index is not None and self._identity_index_max_id > 0:
2127
+ self._index_identity_items(self._query_identity_items(f"Id gt {self._identity_index_max_id}"))
2128
+ return
2129
+ self._identity_index = {}
2130
+ self._identity_index_max_id = 0
2131
+ self._index_identity_items(self._query_identity_items())
2132
+
2133
+ def _query_identity_items(self, filter_expr: str | None = None):
2134
+ # A read query would implicitly flush any queued writes on this context;
2135
+ # flush them explicitly first so the cached index stays consistent.
2136
+ self._flush_pending()
2137
+ columns = ["Id", self._identity_field, "Created", "Modified", "AuthorId", "EditorId"]
2138
+ query = self._list.items.select(columns)
2139
+ if filter_expr:
2140
+ query = query.filter(filter_expr)
2141
+ return query.get_all().execute_query()
2142
+
2143
+ def _index_identity_items(self, items) -> None:
2144
+ if self._identity_index is None:
2145
+ self._identity_index = {}
2146
+ for target_item in items:
2147
+ value = target_item.properties.get(self._identity_field)
2148
+ if value is None:
2149
+ continue
2150
+ self._identity_index[str(value)] = target_item
2151
+ item_id = parse_int(getattr(target_item, "id", None))
2152
+ if item_id is not None and item_id > self._identity_index_max_id:
2153
+ self._identity_index_max_id = item_id
2154
+ self._identity_index_stale = False
2155
+
2156
+ def _attachment_paths(self) -> list[str]:
2157
+ if not self._identity_field:
2158
+ return []
2159
+ if self._attachment_path_cache is None:
2160
+ self._attachment_path_cache = set(self._query_attachment_paths())
2161
+ return list(self._attachment_path_cache)
2162
+
2163
+ def _query_attachment_paths(self) -> list[str]:
2164
+ if not self._identity_field:
2165
+ return []
2166
+ self._flush_pending()
2167
+ items = (
2168
+ self._list.items.select(["Id", self._identity_field]).expand(["AttachmentFiles"]).get_all().execute_query()
2169
+ )
2170
+ paths: list[str] = []
2171
+ for target_item in items:
2172
+ value = target_item.properties.get(self._identity_field)
2173
+ if value is None:
2174
+ continue
2175
+ for attachment in target_item.properties.get("AttachmentFiles") or []:
2176
+ name = getattr(attachment, "file_name", None)
2177
+ if name:
2178
+ paths.append(f"{value}/{name}")
2179
+ return paths
2180
+
255
2181
  def list_paths(self) -> list[str]:
256
- return [str(i.id) for i in self._list.items.get().execute_query()]
2182
+ if not self._identity_field:
2183
+ return [str(i.id) for i in self._list.items.get().execute_query()]
2184
+ # An empty source list never queues a write, so the marker column was not
2185
+ # created during migration; ensure it before verification queries it.
2186
+ self._ensure_identity()
2187
+ if not self._identity_field:
2188
+ return [str(i.id) for i in self._list.items.get().execute_query()]
2189
+ if self._identity_index is None or self._identity_index_stale:
2190
+ self._refresh_identity_index()
2191
+ index = self._identity_index or {}
2192
+ return list(index.keys()) + self._attachment_paths()
257
2193
 
258
2194
  def checksum(self, item: MigrationItem) -> str:
259
- return ""
2195
+ if item.item_type == "attachment":
2196
+ return self._attachment_checksum(item)
2197
+ target_item = self._fresh_target_item(item)
2198
+ if target_item is None:
2199
+ return ""
2200
+ return self.content_checksum(dict(target_item.properties))
2201
+
2202
+ def content_checksum(self, properties: dict) -> str:
2203
+ """The comparable content hash of an item's read-back properties.
2204
+
2205
+ Used for both a written record and a folder's item, so a library folder's
2206
+ column values can be reconciled against the source record that produced them.
2207
+ """
2208
+ properties = _canonicalize_taxonomy_record(dict(properties))
2209
+ self._canonicalize_lookups(properties)
2210
+ self._canonicalize_users(properties)
2211
+ self._canonicalize_multi_choices(properties)
2212
+ extra = {self._identity_field} if self._identity_field else set()
2213
+ # The hidden taxonomy note companion is not part of the source's portable
2214
+ # content; it is verified through the term ids on the column itself.
2215
+ extra.update(note for _, note in self._taxonomy_columns().values() if note)
2216
+ # The target derives its own ``<name>StringId`` companion; it is not part of
2217
+ # the source's canonical content.
2218
+ extra.update(f"{name}StringId" for name in self._user_columns())
2219
+ extra.update(f"{name}StringId" for name in self._multi_user_columns())
2220
+ return _content_checksum(properties, frozenset(extra))
2221
+
2222
+ def _attachment_checksum(self, item: MigrationItem) -> str:
2223
+ parent_id, _, filename = str(item.dest_path).partition("/")
2224
+ parent = self._find_by_identity(parent_id)
2225
+ if parent is None:
2226
+ return ""
2227
+ try:
2228
+ data = parent.attachment_files.get_by_filename(filename).get_content().execute_query().value
2229
+ except Exception: # noqa: BLE001 — a missing/unreadable attachment is unverifiable
2230
+ return ""
2231
+ return hashlib.md5(data).hexdigest() if data else ""
260
2232
 
261
2233
  def commit(self, options=None) -> None:
262
2234
  """Flush any remaining queued record writes through an OData batch."""
263
2235
  batch_size = getattr(options, "batch_size", None) or DEFAULT_BATCH_SIZE
264
2236
  concurrency = getattr(options, "concurrency", None) or 1
265
2237
  self._flush(batch_size, concurrency)
2238
+ self._pending_authorship.clear()
2239
+ self._pending_approval.clear()
266
2240
 
267
2241
  def _flush(self, batch_size: int, concurrency: int) -> None:
268
2242
  if not self._list.context.has_pending_request:
269
2243
  return
270
2244
  self._list.context.execute_batch(items_per_batch=batch_size, concurrency=concurrency)
271
2245
  self._list.items.clear()
2246
+ # Records were just created/updated, so the cached source-id index needs an
2247
+ # incremental refresh (new items only) before the next lookup.
2248
+ if self._identity_index is not None:
2249
+ self._identity_index_stale = True
2250
+
2251
+ def _principals(self) -> _TargetPrincipals:
2252
+ """The (lazily built) target principal/role resolver."""
2253
+ if self._principals_cache is None:
2254
+ self._principals_cache = _TargetPrincipals(self._list.context.web, self.warnings)
2255
+ return self._principals_cache
2256
+
2257
+ def _target_item(self, item: MigrationItem):
2258
+ """The written target item for a record (``None`` for attachments/folders)."""
2259
+ if item.item_type == "attachment":
2260
+ return None
2261
+ return self._find_by_identity(item.dest_path)
2262
+
2263
+ def apply_timestamps(self, item: MigrationItem) -> None:
2264
+ """Best-effort: restore ``Created``/``Modified`` on the written item.
2265
+
2266
+ Uses ``ValidateUpdateListItem`` with ``bNewDocumentUpdate=True`` -- a
2267
+ system update (the CSOM ``UpdateOverwriteVersion`` path), so the metadata
2268
+ lands **without** creating a new version and a migrated record keeps its
2269
+ original version instead of accumulating one version per fidelity write.
2270
+ ``datesInUTC=True`` requires the ``...Z`` UTC form. Authorship values
2271
+ resolved for the same item are folded into this final write so both land
2272
+ atomically, in one system update.
2273
+ """
2274
+ target_item = self._target_item(item)
2275
+ if target_item is None:
2276
+ return
2277
+ form_values: dict = {}
2278
+ created = _sp_utc_timestamp(item.created)
2279
+ modified = _sp_utc_timestamp(item.modified)
2280
+ if created:
2281
+ form_values["Created"] = created
2282
+ if modified:
2283
+ form_values["Modified"] = modified
2284
+ form_values.update(self._pending_authorship.pop(item.dest_path, {}))
2285
+ form_values.update(self._pending_approval.pop(item.dest_path, {}))
2286
+ if form_values:
2287
+ result = target_item.validate_update_list_item(
2288
+ form_values, dates_in_utc=True, new_document_update=True
2289
+ ).execute_query()
2290
+ _report_rejected_fields(result, self.warnings, f"timestamps {item.dest_path}")
2291
+
2292
+ def apply_authorship(self, item: MigrationItem) -> None:
2293
+ """Best-effort: resolve ``Author``/``Editor`` and cache them for one write.
2294
+
2295
+ A principal is applied only when its login name resolves to a target user
2296
+ (``EnsureUser``); unresolved principals are skipped (same-tenant only).
2297
+
2298
+ Nothing is sent here. ``ValidateUpdateListItem`` is one request per call, so
2299
+ writing the resolved principals immediately and then again with the
2300
+ timestamps cost every preserved item two system updates. The values are
2301
+ cached and folded into the timestamps write, or written by
2302
+ :meth:`finalize_fidelity` when timestamps are not preserved.
2303
+ """
2304
+ target_item = self._target_item(item)
2305
+ if target_item is None:
2306
+ return
2307
+ author = self._principals().user(item.author.login_name if item.author else None)
2308
+ editor = self._principals().user(item.editor.login_name if item.editor else None)
2309
+ if author is not None and _is_system_principal(author):
2310
+ author = None
2311
+ if editor is not None and _is_system_principal(editor):
2312
+ editor = None
2313
+ if author is None and editor is None and not self._pending_approval.get(item.dest_path):
2314
+ return
2315
+ form_values: dict = {}
2316
+ if author is not None:
2317
+ form_values["Author"] = FieldUserValue(author.id, author.login_name)
2318
+ if editor is not None:
2319
+ form_values["Editor"] = FieldUserValue(editor.id, editor.login_name)
2320
+ # Replay the approval state here too: this write is itself a system update, so
2321
+ # on a run that restores authorship without timestamps it is the last one.
2322
+ form_values.update(self._pending_approval.get(item.dest_path, {}))
2323
+ self._pending_authorship[item.dest_path] = dict(form_values)
2324
+
2325
+ def finalize_fidelity(self, item: MigrationItem) -> None:
2326
+ """Write the fidelity values that were only cached for this item.
2327
+
2328
+ The last hook wins the write: with timestamps preserved that is
2329
+ :meth:`apply_timestamps`; without them it is this method, so the item still
2330
+ gets one system update instead of one per property group. It runs before
2331
+ the approval write, which has to stay last (a system update re-approves).
2332
+ """
2333
+ form_values = self._pending_authorship.pop(item.dest_path, {})
2334
+ form_values.update(self._pending_approval.pop(item.dest_path, {}))
2335
+ if not form_values:
2336
+ return
2337
+ target_item = self._target_item(item)
2338
+ if target_item is None:
2339
+ return
2340
+ result = target_item.validate_update_list_item(
2341
+ form_values, dates_in_utc=True, new_document_update=True
2342
+ ).execute_query()
2343
+ _report_rejected_fields(result, self.warnings, f"authorship {item.dest_path}")
2344
+
2345
+ def apply_permissions(self, item: MigrationItem, permissions: list[PermissionEntry]) -> None:
2346
+ """Best-effort: replay the source item's unique ACLs onto the target item.
2347
+
2348
+ Same-tenant only — cross-tenant identity mapping is out of scope. See
2349
+ :func:`_replay_role_assignments` for what happens to principals, roles and
2350
+ inheritance that cannot be resolved.
2351
+ """
2352
+ target_item = self._target_item(item)
2353
+ if target_item is None:
2354
+ return
2355
+ _replay_role_assignments(target_item, self._principals(), permissions, self.warnings, item.dest_path)
2356
+ self._list.context.execute_query()
2357
+
2358
+ def _fresh_target_item(self, item: MigrationItem):
2359
+ """The written target item with freshly-read properties (verification hooks).
2360
+
2361
+ The cached identity-index object predates the fidelity writes
2362
+ (``apply_timestamps``/``apply_authorship``), so re-read it before diffing.
2363
+ """
2364
+ target_item = self._target_item(item)
2365
+ if target_item is not None:
2366
+ target_item.get().execute_query()
2367
+ return target_item
2368
+
2369
+ def read_metadata(self, item: MigrationItem) -> dict | None:
2370
+ """Comparable attributes of the written item (for the metadata diff)."""
2371
+ target_item = self._fresh_target_item(item)
2372
+ if target_item is None:
2373
+ return None
2374
+ properties = target_item.properties
2375
+ return {
2376
+ "created": iso_or_none(properties.get("Created")),
2377
+ "modified": iso_or_none(properties.get("Modified")),
2378
+ }
2379
+
2380
+ def read_authorship(self, item: MigrationItem) -> tuple[str | None, str | None]:
2381
+ """The written item's author/editor login names (for the authorship diff)."""
2382
+ target_item = self._fresh_target_item(item)
2383
+ if target_item is None:
2384
+ return (None, None)
2385
+ properties = target_item.properties
2386
+ return (
2387
+ _user_login(self._list.context.web, properties.get("AuthorId"), self._login_cache),
2388
+ _user_login(self._list.context.web, properties.get("EditorId"), self._login_cache),
2389
+ )
2390
+
2391
+ def read_permissions(self, item: MigrationItem) -> list[PermissionEntry]:
2392
+ """Read the written item's *unique* role assignments (for the permission diff)."""
2393
+ target_item = self._fresh_target_item(item)
2394
+ if target_item is None or not _has_unique_role_assignments(target_item, refresh=True):
2395
+ return []
2396
+ return _read_role_assignments(target_item, normalize=self._principals().normalize)
272
2397
 
273
2398
  def close(self) -> None:
274
2399
  pass
@@ -278,16 +2403,46 @@ class SharePointLibrarySource:
278
2403
  """Enumerates a document library's files and folders (recursively) for migration.
279
2404
 
280
2405
  Folder entries (``item_type="folder"``, trailing ``/`` on the destination)
281
- preserve the folder structure — including **empty** folders — on the target.
2406
+ preserve the folder structure — including **empty** folders — on the target, and
2407
+ carry the folder's own column values (a document set's metadata, a managed
2408
+ folder's department) as their payload when the parent list is known.
282
2409
  """
283
2410
 
2411
+ def rebind(self) -> "SharePointLibrarySource":
2412
+ """This source on its own cloned context (see :meth:`SharePointListSource.rebind`)."""
2413
+ context = _cloned_context(self._folder)
2414
+ sp_list = _cloned_list(self._sp_list, context) if self._sp_list is not None else None
2415
+ return SharePointLibrarySource(_cloned_folder(self._folder, context), self.on_bytes, sp_list=sp_list)
2416
+
284
2417
  def __init__(
285
2418
  self,
286
2419
  library_folder: "Folder",
287
2420
  on_bytes: Callable[[int, int | None], None] | None = None,
2421
+ sp_list: "SPList | None" = None,
288
2422
  ) -> None:
289
2423
  self._folder = library_folder
2424
+ # The parent list, when known — author/editor ids (``AuthorId``/``EditorId``)
2425
+ # are not exposed on the ``File`` resource and must be read from its items.
2426
+ self._sp_list = sp_list
2427
+ # Folder column values come from the list's items and are projected exactly
2428
+ # like a record's (typed values normalized), so that code is reused here.
2429
+ self._list_source = SharePointListSource(sp_list) if sp_list is not None else None
2430
+ self._folder_payloads: dict[str, dict] | None = None
290
2431
  self._files: dict[str, "File"] = {}
2432
+ # Folder resources by library-relative path, so their own system metadata
2433
+ # (``TimeCreated``/``TimeLastModified``) can be diffed after the run.
2434
+ self._folders: dict[str, "Folder"] = {}
2435
+ self._users: dict[int, "Principal | None"] = {}
2436
+ self._default_groups: _DefaultGroups | None = None
2437
+ self._item_authors: dict[str, tuple[int | None, int | None]] | None = None
2438
+ self._item_unique: dict[str, bool] | None = None
2439
+ # Content-approval state by file URL, read alongside the author ids (only
2440
+ # when the library moderates).
2441
+ self._item_moderation: dict[str, dict] | None = None
2442
+ # Server-relative URLs of checked-out files, read alongside the author ids.
2443
+ self._item_checkout: set[str] | None = None
2444
+ self._moderation_on: bool | None = None
2445
+ self.warnings: list[str] = []
291
2446
  # Optional hook invoked while a file downloads: (bytes_read, file_size)
292
2447
  self.on_bytes = on_bytes
293
2448
 
@@ -303,8 +2458,10 @@ class SharePointLibrarySource:
303
2458
  self._folder.ensure_properties(["ServerRelativeUrl"]).execute_query()
304
2459
  root = self._folder.server_relative_url or ""
305
2460
  root = root.rstrip("/")
2461
+ extra = _site_assets_scaffolding(self._folder)
306
2462
  loaded_folders = self._folder.get_folders(recursive=True).execute_query()
307
2463
  loaded = self._folder.get_files(recursive=True).execute_query()
2464
+ authors = self._file_authors()
308
2465
 
309
2466
  def _rel(url: str) -> str:
310
2467
  return url[len(root) :].lstrip("/") if root else url.lstrip("/")
@@ -314,33 +2471,114 @@ class SharePointLibrarySource:
314
2471
  for folder in loaded_folders:
315
2472
  url = folder.server_relative_url or ""
316
2473
  rel = _rel(url)
317
- if not rel or rel in seen:
2474
+ if not rel or rel in seen or _is_system_path(rel, extra):
318
2475
  continue
319
2476
  seen.add(rel)
320
- result.append(MigrationItem(source_path=url, dest_path=f"{rel}/", item_type="folder"))
2477
+ self._folders[rel] = folder
2478
+ author_id, editor_id = authors.get(url, (None, None))
2479
+ folder_item = MigrationItem(
2480
+ source_path=url,
2481
+ dest_path=f"{rel}/",
2482
+ item_type="folder",
2483
+ modified=iso_or_none(folder.time_last_modified),
2484
+ created=iso_or_none(folder.time_created),
2485
+ author_id=author_id,
2486
+ editor_id=editor_id,
2487
+ )
2488
+ if author_id is not None:
2489
+ folder_item.author = self._principal_for(author_id)
2490
+ if editor_id is not None:
2491
+ folder_item.editor = self._principal_for(editor_id)
2492
+ result.append(folder_item)
321
2493
  emit_progress(progress, done=len(result), stage="planning", items=[folder])
322
2494
  for file in loaded:
323
2495
  url = file.server_relative_url or ""
324
2496
  rel = _rel(url)
2497
+ if _is_system_path(rel, extra):
2498
+ continue
325
2499
  self._files[rel] = file
326
- result.append(
327
- MigrationItem(
328
- source_path=url,
329
- dest_path=rel,
330
- size_bytes=file.length or 0,
331
- item_type="file",
332
- modified=iso_or_none(file.time_last_modified),
333
- created=iso_or_none(file.time_created),
334
- author_id=parse_int(file.author.id),
335
- editor_id=parse_int(file.modified_by.id),
336
- )
2500
+ author_id = parse_int(file.author.id)
2501
+ editor_id = parse_int(file.modified_by.id)
2502
+ if author_id is None and editor_id is None:
2503
+ author_id, editor_id = authors.get(url, (None, None))
2504
+ item = MigrationItem(
2505
+ source_path=url,
2506
+ dest_path=rel,
2507
+ size_bytes=file.length or 0,
2508
+ item_type="file",
2509
+ modified=iso_or_none(file.time_last_modified),
2510
+ created=iso_or_none(file.time_created),
2511
+ author_id=author_id,
2512
+ editor_id=editor_id,
2513
+ source_id=str(file.unique_id) if file.unique_id else None,
2514
+ version_label=file.ui_version_label,
337
2515
  )
2516
+ if author_id is not None:
2517
+ item.author = self._principal_for(author_id)
2518
+ if editor_id is not None:
2519
+ item.editor = self._principal_for(editor_id)
2520
+ result.append(item)
338
2521
  emit_progress(progress, done=len(result), stage="planning", items=[file])
2522
+ self._report_checkouts(result)
2523
+ self._report_drafts(result)
339
2524
  return result
340
2525
 
341
- def read(self, item: MigrationItem) -> bytes:
2526
+ def _report_checkouts(self, items: list[MigrationItem]) -> None:
2527
+ """Warn about files whose in-progress edits REST cannot see.
2528
+
2529
+ A checked-out file exposes its last **checked-in** content, so the copy is
2530
+ faithful to what the source returns while the author's pending edits stay
2531
+ behind. Verification cannot catch that — it compares the same read — so the
2532
+ loss is reported here instead of passing silently.
2533
+ """
2534
+ checkout = self._item_checkout or set()
2535
+ checked = sorted(item.dest_path for item in items if item.item_type != "folder" and item.source_path in checkout)
2536
+ if not checked:
2537
+ return
2538
+ shown = ", ".join(checked[:_WARNING_SAMPLE])
2539
+ if len(checked) > _WARNING_SAMPLE:
2540
+ shown = f"{shown} and {len(checked) - _WARNING_SAMPLE} more"
2541
+ self.warnings.append(
2542
+ f"{len(checked)} checked-out file(s) were copied from their last checked-in version "
2543
+ f"(in-progress edits are not visible over REST): {shown}"
2544
+ )
2545
+
2546
+ def _report_drafts(self, items: list[MigrationItem]) -> None:
2547
+ """Warn about files whose current version is an un-published (minor) draft.
2548
+
2549
+ REST returns the *draft* content, so the copy is faithful to the version the
2550
+ source reports — but version history is not preserved, so the published
2551
+ version a draft supersedes never reaches the target and the copy keeps the
2552
+ draft alone. Verification compares the same read and cannot see the
2553
+ difference, so the loss is reported here instead of passing silently.
2554
+
2555
+ The signal rides the file listing (``MinorVersion``), so this costs no extra
2556
+ request: a published file is ``x.0``, a draft ``x.n`` with ``n > 0``.
2557
+ """
2558
+ drafts: list[str] = []
2559
+ for item in items:
2560
+ if item.item_type == "folder":
2561
+ continue
2562
+ file = self._files.get(item.dest_path)
2563
+ if file is None or not file.minor_version:
2564
+ continue
2565
+ drafts.append(item.dest_path)
2566
+ if not drafts:
2567
+ return
2568
+ drafts.sort()
2569
+ shown = ", ".join(drafts[:_WARNING_SAMPLE])
2570
+ if len(drafts) > _WARNING_SAMPLE:
2571
+ shown = f"{shown} and {len(drafts) - _WARNING_SAMPLE} more"
2572
+ self.warnings.append(
2573
+ f"{len(drafts)} file(s) are on an un-published (minor) version: the draft content "
2574
+ f"is copied, the published version it supersedes is not (version history is not "
2575
+ f"preserved): {shown}"
2576
+ )
2577
+
2578
+ def read(self, item: MigrationItem) -> object:
342
2579
  if item.item_type == "folder":
343
- return b""
2580
+ # A folder's payload is its column values (empty when it carries none).
2581
+ return self._folder_values(item) or b""
344
2582
  file = self._files.get(item.dest_path)
345
2583
  if file is None:
346
2584
  raise FileNotFoundError(item.source_path)
@@ -354,10 +2592,55 @@ class SharePointLibrarySource:
354
2592
  file.download_session(buffer, chunk_downloaded=_chunk_downloaded).execute_query()
355
2593
  return buffer.getvalue()
356
2594
 
2595
+ def read_version_history(self, item: MigrationItem) -> tuple[str | None, list[tuple[str, bytes]]]:
2596
+ """Read the file's current version label and its older versions.
2597
+
2598
+ Returns ``(current_label, older)`` where ``older`` is oldest-first and
2599
+ excludes the current version (which :meth:`read` already supplies). The
2600
+ current label comes from ``UIVersionLabel``; older content is streamed
2601
+ from each ``File.Versions`` entry. Folders have no version history.
2602
+ """
2603
+ if item.item_type == "folder":
2604
+ return None, []
2605
+ file = self._files.get(item.dest_path)
2606
+ if file is None:
2607
+ raise FileNotFoundError(item.source_path)
2608
+ if file.ui_version_label is None:
2609
+ file.ensure_property("UIVersionLabel").execute_query()
2610
+ older: list[tuple[str, bytes]] = []
2611
+ for version in file.versions.get().execute_query():
2612
+ label = version.version_label
2613
+ if not label:
2614
+ continue
2615
+ data = version.open_binary_stream().execute_query().value
2616
+ older.append((label, data or b""))
2617
+ older.sort(key=lambda row: _version_sort_key(row[0]))
2618
+ return file.ui_version_label, older
2619
+
2620
+ @property
2621
+ def supports_folder_records(self) -> bool:
2622
+ """Whether a folder's column values are migrated *and* comparable."""
2623
+ return self._list_source is not None
2624
+
357
2625
  def checksum(self, item: MigrationItem) -> str:
358
2626
  if item.item_type == "folder":
359
- return hashlib.md5(b"").hexdigest()
360
- return hashlib.md5(self.read(item)).hexdigest()
2627
+ return _content_checksum(_canonicalize_taxonomy_record(self._folder_values(item)))
2628
+ return hashlib.md5(cast(bytes, self.read(item))).hexdigest()
2629
+
2630
+ def _folder_values(self, item: MigrationItem) -> dict:
2631
+ """The source folder's column values (empty when it carries none)."""
2632
+ if self._folder_payloads is None:
2633
+ payloads: dict[str, dict] = {}
2634
+ if self._sp_list is None or self._list_source is None:
2635
+ self.warnings.append(
2636
+ "folder column values were not read: the parent library is unknown "
2637
+ "(pass sp_list to SharePointLibrarySource)"
2638
+ )
2639
+ else:
2640
+ payloads.update(self._list_source.folder_records())
2641
+ self.warnings.extend(self._list_source.warnings)
2642
+ self._folder_payloads = payloads
2643
+ return self._folder_payloads.get(str(item.source_path).rstrip("/"), {})
361
2644
 
362
2645
  def read_permissions(self, item: MigrationItem) -> list[PermissionEntry]:
363
2646
  """Read the item's role assignments (for best-effort ACL preservation).
@@ -368,16 +2651,56 @@ class SharePointLibrarySource:
368
2651
  securable = self._securable(item)
369
2652
  if securable is None:
370
2653
  return []
371
- assignments = securable.role_assignments.expand(["Member", "RoleDefinitionBindings"]).get().execute_query()
372
- result: list[PermissionEntry] = []
373
- for assignment in assignments:
374
- member = assignment.member
375
- name = member.login_name or member.user_principal_name
376
- if not name:
377
- continue
378
- roles = [r.name for r in assignment.role_definition_bindings if r.name]
379
- result.append(PermissionEntry(principal_name=name, roles=roles))
380
- return result
2654
+ # Files: reuse the unique-ACL flag read with the authorship pass (the
2655
+ # ``File``/``ListItem`` resources do not expose it without an extra GET).
2656
+ if item.item_type != "folder" and self._item_unique is not None:
2657
+ unique = self._item_unique.get(item.source_path)
2658
+ if unique is not None:
2659
+ securable.properties["HasUniqueRoleAssignments"] = unique
2660
+ if not _has_unique_role_assignments(securable):
2661
+ return []
2662
+ return _read_role_assignments(securable, resolve=self._principal_for, normalize=self._normalize_name)
2663
+
2664
+ def _normalize_name(self, name: str) -> str:
2665
+ """Canonicalize a principal name (default groups -> ``default:<Kind>``)."""
2666
+ context = getattr(self._folder, "context", None)
2667
+ web = getattr(context, "web", None) if context is not None else None
2668
+ if web is None:
2669
+ return name
2670
+ if self._default_groups is None:
2671
+ self._default_groups = _DefaultGroups(web)
2672
+ return self._default_groups.normalize(name)
2673
+
2674
+ def read_metadata(self, item: MigrationItem) -> dict | None:
2675
+ """Comparable attributes of the source item (for the post-run metadata diff)."""
2676
+ if item.item_type == "folder":
2677
+ folder = self._folders.get(str(item.dest_path).rstrip("/"))
2678
+ if folder is None:
2679
+ return None
2680
+ return {
2681
+ "created": iso_or_none(folder.time_created),
2682
+ "modified": iso_or_none(folder.time_last_modified),
2683
+ }
2684
+ file = self._files.get(item.dest_path)
2685
+ if file is None:
2686
+ return None
2687
+ metadata = {
2688
+ "created": iso_or_none(file.time_created),
2689
+ "modified": iso_or_none(file.time_last_modified),
2690
+ "size_bytes": file.length or 0,
2691
+ }
2692
+ self._file_authors()
2693
+ state = (self._item_moderation or {}).get(item.source_path)
2694
+ if state:
2695
+ metadata["moderation_status"] = state.get("_ModerationStatus")
2696
+ return metadata
2697
+
2698
+ def read_authorship(self, item: MigrationItem) -> tuple[str | None, str | None]:
2699
+ """The source author/editor login names (for the authorship diff)."""
2700
+ return (
2701
+ item.author.login_name if item.author else None,
2702
+ item.editor.login_name if item.editor else None,
2703
+ )
381
2704
 
382
2705
  def _securable(self, item: MigrationItem):
383
2706
  if item.item_type == "folder":
@@ -385,6 +2708,122 @@ class SharePointLibrarySource:
385
2708
  file = self._files.get(item.dest_path)
386
2709
  return file.listItemAllFields if file is not None else None
387
2710
 
2711
+ def _principal_for(self, user_id: Any) -> "Principal | None":
2712
+ """Resolve a site-user id to a :class:`Principal` (login + PUID), cached."""
2713
+ key = parse_int(user_id)
2714
+ if key is None:
2715
+ return None
2716
+ if key in self._users:
2717
+ return self._users[key]
2718
+ principal: "Principal | None" = None
2719
+ try:
2720
+ user = self._folder.context.web.site_users.get_by_id(cast("str", key)).get().execute_query()
2721
+ user_id_info = user.properties.get("UserId")
2722
+ raw_id = getattr(user_id_info, "NameId", None)
2723
+ system_id = normalize_puid(raw_id)
2724
+ if raw_id and system_id is None:
2725
+ # e.g. the app principal, whose ``UserId.NameId`` is not a PUID.
2726
+ # Emitting it would fail the whole ingestion job, so drop it.
2727
+ self.warnings.append(
2728
+ f"user {key} ({user.login_name!r}) has no usable PUID ({raw_id!r}); "
2729
+ "it is omitted from the server-side user map"
2730
+ )
2731
+ principal = Principal(
2732
+ login_name=user.login_name or "",
2733
+ system_id=system_id,
2734
+ display_name=user.title,
2735
+ email=user.properties.get("Email"),
2736
+ )
2737
+ except Exception as e: # noqa: BLE001 — a missing user degrades to no authorship
2738
+ self.warnings.append(f"user {key} could not be resolved: {e}")
2739
+ self._users[key] = principal
2740
+ return principal
2741
+
2742
+ def _file_authors(self) -> dict[str, tuple[int | None, int | None]]:
2743
+ """Author/editor ids by server-relative file URL (one list-items read).
2744
+
2745
+ ``File.Author``/``ModifiedBy`` is a non-expandable navigation on the File
2746
+ resource, so the ids come from the parent library's list items.
2747
+ """
2748
+ if self._item_authors is not None:
2749
+ return self._item_authors
2750
+ authors: dict[str, tuple[int | None, int | None]] = {}
2751
+ unique: dict[str, bool] = {}
2752
+ moderation: dict[str, dict] = {}
2753
+ checkout: set[str] = set()
2754
+ moderates = self._moderation_enabled()
2755
+ if self._sp_list is not None:
2756
+ try:
2757
+ # ``CheckoutUser`` is a user field: it only projects when its
2758
+ # sub-properties are selected *and* the field is expanded — a bare
2759
+ # ``$select=CheckoutUser`` is rejected by the server.
2760
+ select = [
2761
+ "FileRef",
2762
+ "AuthorId",
2763
+ "EditorId",
2764
+ "HasUniqueRoleAssignments",
2765
+ "CheckoutUser/Id",
2766
+ "CheckoutUser/Title",
2767
+ ]
2768
+ if moderates:
2769
+ select.extend(_MODERATION_FIELDS)
2770
+ items = self._sp_list.items.select(select).expand(["CheckoutUser"]).get_all().execute_query()
2771
+ for item in items:
2772
+ ref = item.properties.get("FileRef")
2773
+ if ref:
2774
+ authors[ref] = (
2775
+ parse_int(item.properties.get("AuthorId")),
2776
+ parse_int(item.properties.get("EditorId")),
2777
+ )
2778
+ unique[ref] = bool(item.properties.get("HasUniqueRoleAssignments"))
2779
+ if item.properties.get("CheckoutUser"):
2780
+ checkout.add(ref)
2781
+ if moderates:
2782
+ state = _moderation_state(item.properties)
2783
+ if state:
2784
+ moderation[ref] = state
2785
+ except Exception as e: # noqa: BLE001 — authorship is best-effort
2786
+ self.warnings.append(f"author/editor read failed: {e}")
2787
+ self._item_authors = authors
2788
+ self._item_unique = unique
2789
+ self._item_moderation = moderation
2790
+ self._item_checkout = checkout
2791
+ return authors
2792
+
2793
+ def _moderation_enabled(self) -> bool:
2794
+ """Whether the parent library enables content approval (read once)."""
2795
+ if self._moderation_on is None:
2796
+ self._moderation_on = False
2797
+ if self._sp_list is not None:
2798
+ try:
2799
+ self._sp_list.ensure_properties(["EnableModeration"]).get().execute_query()
2800
+ self._moderation_on = bool(self._sp_list.properties.get("EnableModeration"))
2801
+ except Exception as e: # noqa: BLE001 — approval state is best-effort
2802
+ self.warnings.append(f"moderation setting read failed: {e}")
2803
+ return self._moderation_on
2804
+
2805
+ @property
2806
+ def supports_item_state(self) -> bool:
2807
+ """Whether approval state can be read (needs the parent library)."""
2808
+ return self._list_source is not None
2809
+
2810
+ def read_item_state(self, item: MigrationItem) -> dict | None:
2811
+ """Approval state that a fresh upload would not reproduce.
2812
+
2813
+ An uploaded document starts out *approved*, so only a draft/pending or
2814
+ rejected source file carries state, together with its approval comments.
2815
+ """
2816
+ if item.item_type == "folder" or self._list_source is None:
2817
+ return None
2818
+ self._file_authors()
2819
+ state = (self._item_moderation or {}).get(item.source_path)
2820
+ if not state:
2821
+ return None
2822
+ status = state.get("_ModerationStatus")
2823
+ if status is None or status == _APPROVED_STATUS:
2824
+ return None
2825
+ return state
2826
+
388
2827
  def close(self) -> None:
389
2828
  pass
390
2829
 
@@ -394,30 +2833,177 @@ class SharePointLibraryTarget:
394
2833
 
395
2834
  Uses the simple upload for files up to ~4MB; ``write_many`` (parallel,
396
2835
  ``concurrency > 1``) uses ``create_upload_session`` for larger files.
2836
+
2837
+ Pass ``sp_list`` (the target library) to also replay a folder's column values: a
2838
+ folder is a list item, so a document set's metadata or a managed folder's
2839
+ department is written onto the created folder's item. ``content_type_map``
2840
+ rewrites the folder's content type id to its target counterpart, exactly as it
2841
+ does for an item record.
397
2842
  """
398
2843
 
399
- def __init__(self, library_folder: "Folder", concurrency: int = 1) -> None:
2844
+ #: Files carry content-approval state (``apply_item_state``). Documents refuse a
2845
+ #: moderation change combined with other property writes, so it is a write of its
2846
+ #: own, applied after the timestamps that would otherwise re-approve the file.
2847
+ supports_item_state = True
2848
+
2849
+ def rebind(self) -> "SharePointLibraryTarget":
2850
+ """This target on its own cloned context (see :meth:`SharePointListSource.rebind`)."""
2851
+ context = _cloned_context(self._folder)
2852
+ sp_list = _cloned_list(self._sp_list, context) if self._sp_list is not None else None
2853
+ return SharePointLibraryTarget(
2854
+ _cloned_folder(self._folder, context),
2855
+ concurrency=self._concurrency,
2856
+ sp_list=sp_list,
2857
+ content_type_map=self._content_type_map,
2858
+ )
2859
+
2860
+ def __init__(
2861
+ self,
2862
+ library_folder: "Folder",
2863
+ concurrency: int = 1,
2864
+ sp_list: "SPList | None" = None,
2865
+ content_type_map: "ContentTypeMap | None" = None,
2866
+ ) -> None:
400
2867
  self._folder = library_folder
401
2868
  self._concurrency = concurrency
402
- self._role_definitions: dict[str, RoleDefinition] | None = None
2869
+ # The folder-record mapping is the record mapping: reuse the list target so a
2870
+ # folder's Lookup/Person/taxonomy/choice values resolve the same way.
2871
+ self._sp_list = sp_list
2872
+ self._list_target = (
2873
+ SharePointListTarget(sp_list, content_type_map=content_type_map) if sp_list is not None else None
2874
+ )
2875
+ self._content_type_map = content_type_map
2876
+ self.warnings: list[str] = []
2877
+ self._login_cache: dict[int, str | None] = {}
2878
+ self._principals_cache: _TargetPrincipals | None = None
2879
+ # Author/Editor values written by ``apply_authorship`` and folded into the
2880
+ # final timestamp write so ``Editor`` isn't reset by the latter.
2881
+ self._pending_authorship: dict[str, dict] = {}
2882
+ # Target web time zone (lazily loaded) and a UTC->local conversion cache.
2883
+ self._time_zone = None
2884
+ self._local_cache: dict[str, str] = {}
2885
+ self._field_scan: SharePointListSource | None = None
2886
+
2887
+ def _utc_to_local(self, *values: str | None) -> list[str | None]:
2888
+ """Convert UTC timestamp strings to web-local wall-clock (one round trip).
2889
+
2890
+ Document items parse form dates in the web's locale, so the desired UTC
2891
+ instant must be converted to local wall-clock first. The conversion runs
2892
+ server-side (``TimeZone.UTCToLocalTime``) so DST is handled correctly.
2893
+ Results are cached by the exact UTC value.
2894
+ """
2895
+ if self._time_zone is None:
2896
+ self._time_zone = self._folder.context.web.regional_settings.time_zone
2897
+ outputs: list[str | None] = [None] * len(values)
2898
+ pending: dict[str, object] = {}
2899
+ assignments: list[tuple[int, str]] = []
2900
+ for index, value in enumerate(values):
2901
+ if not value:
2902
+ continue
2903
+ utc = _sp_utc_timestamp(value)
2904
+ if utc is None:
2905
+ continue
2906
+ if not utc.endswith("Z"):
2907
+ # Unparseable value: pass it through unchanged so a rejection is
2908
+ # surfaced by ``_report_rejected_fields`` rather than silently lost.
2909
+ outputs[index] = str(value)
2910
+ continue
2911
+ cached = self._local_cache.get(utc)
2912
+ if cached is not None:
2913
+ outputs[index] = cached
2914
+ continue
2915
+ instant = parse_datetime(utc)
2916
+ if instant is None:
2917
+ outputs[index] = str(value)
2918
+ continue
2919
+ if utc not in pending:
2920
+ pending[utc] = self._time_zone.utc_to_local_time(instant)
2921
+ assignments.append((index, utc))
2922
+ if pending:
2923
+ self._folder.context.execute_query()
2924
+ for utc, result in pending.items():
2925
+ local = _sp_local_timestamp(getattr(result, "value", None))
2926
+ if local is not None:
2927
+ self._local_cache[utc] = local
2928
+ for index, utc in assignments:
2929
+ outputs[index] = self._local_cache.get(utc)
2930
+ return outputs
2931
+
2932
+ def _principals(self) -> _TargetPrincipals:
2933
+ """The (lazily built) target principal/role resolver."""
2934
+ if self._principals_cache is None:
2935
+ self._principals_cache = _TargetPrincipals(self._folder.context.web, self.warnings)
2936
+ return self._principals_cache
403
2937
 
404
2938
  def label(self) -> str:
405
2939
  return f"library:{self._folder.server_relative_url}"
406
2940
 
407
2941
  def _url(self, item: MigrationItem) -> str:
408
- return f"{(self._folder.server_relative_url or '').rstrip('/')}/{item.dest_path}"
2942
+ return f"{(self._folder.server_relative_url or '').rstrip('/')}/{str(item.dest_path).strip('/')}"
409
2943
 
410
2944
  def exists(self, item: MigrationItem) -> bool:
2945
+ url = self._url(item)
411
2946
  try:
412
- self._folder.context.web.get_file_by_server_relative_url(self._url(item)).get().execute_query()
2947
+ if item.item_type == "folder":
2948
+ self._folder.context.web.get_folder_by_server_relative_url(url).get().execute_query()
2949
+ else:
2950
+ self._folder.context.web.get_file_by_server_relative_url(url).get().execute_query()
413
2951
  return True
414
- except Exception: # noqa: BLE001 — a missing file surfaces as a request error
2952
+ except Exception: # noqa: BLE001 — a missing item surfaces as a request error
415
2953
  return False
416
2954
 
2955
+ def _ensure_folders(self, items: list[MigrationItem]) -> None:
2956
+ """Create folder items (deduplicated to deepest paths) in one round-trip."""
2957
+ paths = [str(item.dest_path).strip("/") for item in items]
2958
+ paths = [path for path in paths if path]
2959
+ if not paths:
2960
+ return
2961
+ self._folder.ensure_folders(paths)
2962
+ self._folder.context.execute_query()
2963
+
417
2964
  def write(self, item: MigrationItem, payload: object) -> None:
2965
+ if item.item_type == "folder":
2966
+ self._ensure_folders([item])
2967
+ if isinstance(payload, dict):
2968
+ self._apply_folder_values(item, payload)
2969
+ return
418
2970
  content = payload if isinstance(payload, bytes) else str(payload).encode("utf-8")
419
2971
  self._folder.upload_file(item.dest_path, content).execute_query()
420
2972
 
2973
+ @property
2974
+ def supports_folder_records(self) -> bool:
2975
+ """Whether a folder's column values are migrated *and* comparable."""
2976
+ return self._list_target is not None
2977
+
2978
+ def _apply_folder_values(self, item: MigrationItem, record: dict) -> None:
2979
+ """Write a folder's column values onto the folder's underlying list item."""
2980
+ assert self._list_target is not None
2981
+ values = _folder_record_values(record)
2982
+ if not values:
2983
+ return
2984
+ values = self._list_target.translate_record(values)
2985
+ folder_item = self._securable(item)
2986
+ for key, value in values.items():
2987
+ folder_item.set_property(key, value)
2988
+ try:
2989
+ folder_item.update().execute_query()
2990
+ except Exception as e: # noqa: BLE001 — a folder's structure is worth more than its values
2991
+ self.warnings.append(f"folder column values for {item.dest_path!r} could not be written: {e}")
2992
+
2993
+ def _folder_projection(self) -> list[str]:
2994
+ """The target's folder columns (read once) — the set the source projected."""
2995
+ if self._sp_list is None:
2996
+ return []
2997
+ if self._field_scan is None:
2998
+ # Field definitions are read by the same code on both sides: structure
2999
+ # provisioning keeps the internal names, so the projections line up.
3000
+ self._field_scan = SharePointListSource(self._sp_list)
3001
+ try:
3002
+ return self._field_scan.folder_projection()
3003
+ except Exception as e: # noqa: BLE001 — without a projection no folder values move
3004
+ self.warnings.append(f"could not read the target columns for folder values: {e}")
3005
+ return []
3006
+
421
3007
  def write_many(
422
3008
  self,
423
3009
  items: list[MigrationItem],
@@ -431,90 +3017,273 @@ class SharePointLibraryTarget:
431
3017
  """
432
3018
  from office365.migration.sharepoint.transfer import _transfer_files_parallel
433
3019
 
3020
+ failures: list[Failure] = []
3021
+ folders = [item for item in items if item.item_type == "folder"]
3022
+ if folders:
3023
+ try:
3024
+ self._ensure_folders(folders)
3025
+ except Exception: # noqa: BLE001 — fall back to per-folder to attribute errors
3026
+ for folder in folders:
3027
+ try:
3028
+ self._ensure_folders([folder])
3029
+ except Exception as e: # noqa: BLE001
3030
+ failures.append((folder.dest_path, str(e)))
3031
+ # A folder's column values can only be written once its item exists, so they
3032
+ # follow the (batched) folder creation.
3033
+ for item, payload in zip(items, payloads):
3034
+ if item.item_type == "folder" and isinstance(payload, dict):
3035
+ self._apply_folder_values(item, payload)
434
3036
  files = [
435
3037
  (item.dest_path, payload if isinstance(payload, bytes) else str(payload).encode("utf-8"))
436
3038
  for item, payload in zip(items, payloads)
3039
+ if item.item_type != "folder"
437
3040
  ]
438
- return _transfer_files_parallel(
439
- self._folder,
440
- files,
441
- concurrency=concurrency or self._concurrency or 1,
442
- )
3041
+ if files:
3042
+ failures.extend(
3043
+ _transfer_files_parallel(
3044
+ self._folder,
3045
+ files,
3046
+ concurrency=concurrency or self._concurrency or 1,
3047
+ )
3048
+ )
3049
+ return failures
443
3050
 
444
3051
  def list_paths(self) -> list[str]:
445
3052
  root = (self._folder.server_relative_url or "").rstrip("/")
446
- loaded = self._folder.get_files(recursive=True).execute_query()
447
- return [(f.server_relative_url or "")[len(root) :].lstrip("/") for f in loaded]
3053
+ extra = _site_assets_scaffolding(self._folder)
3054
+ loaded_files = self._folder.get_files(recursive=True).execute_query()
3055
+ loaded_folders = self._folder.get_folders(recursive=True).execute_query()
3056
+ paths = [(f.server_relative_url or "")[len(root) :].lstrip("/") for f in loaded_files]
3057
+ paths += [f"{(f.server_relative_url or '')[len(root) :].lstrip('/')}/" for f in loaded_folders]
3058
+ # Mirror the source projection: SharePoint-owned scaffolding (``Forms/``,
3059
+ # ``desktop.ini``, and on SiteAssets the site icon + auto-provisioned
3060
+ # notebook) is recreated server-side, never migrated, so it must not be
3061
+ # reported as target-only content during verification. Source folder paths
3062
+ # carry a trailing slash; target folder paths mirror that.
3063
+ return [p for p in paths if p != "/" and not _is_system_path(p, extra)]
448
3064
 
449
- def checksum(self, item: MigrationItem) -> str:
3065
+ def read(self, item: MigrationItem) -> bytes:
3066
+ """Download the written item's bytes (used by verification for Office files)."""
3067
+ if item.item_type == "folder":
3068
+ return b""
450
3069
  file = self._folder.context.web.get_file_by_server_relative_url(self._url(item)).get().execute_query()
451
3070
  buffer = io.BytesIO()
452
3071
  file.download(buffer).execute_query()
453
- return hashlib.md5(buffer.getvalue()).hexdigest()
3072
+ return buffer.getvalue()
3073
+
3074
+ def checksum(self, item: MigrationItem) -> str:
3075
+ if item.item_type == "folder":
3076
+ values = self._target_folder_values(item)
3077
+ if values is None or self._list_target is None:
3078
+ return ""
3079
+ return self._list_target.content_checksum(values)
3080
+ return hashlib.md5(self.read(item)).hexdigest()
3081
+
3082
+ def _target_folder_values(self, item: MigrationItem) -> dict | None:
3083
+ """The target folder's column values as they read back (``None`` if unknown)."""
3084
+ columns = self._folder_projection()
3085
+ if not columns:
3086
+ return None
3087
+ list_item = self._securable(item).select(columns).get().execute_query()
3088
+ return _folder_record_values(dict(list_item.properties))
454
3089
 
455
3090
  def modified(self, item: MigrationItem) -> str:
456
3091
  """Last-modified of the target file (for incremental migration)."""
3092
+ if item.item_type == "folder":
3093
+ return ""
457
3094
  file = self._folder.context.web.get_file_by_server_relative_url(self._url(item)).get().execute_query()
458
3095
  return iso_or_none(file.time_last_modified) or ""
459
3096
 
460
3097
  def apply_timestamps(self, item: MigrationItem) -> None:
461
3098
  """Best-effort: restore ``Created``/``Modified`` on the written item.
462
3099
 
463
- Uses ``ValidateUpdateListItem`` (the same mechanism as
464
- ``ListItem.system_update``), which is the only REST path that can set these
465
- system fields. ``preserve_versions`` still needs the server-side API.
3100
+ Uses ``ValidateUpdateListItem``, the only REST path that can set these
3101
+ system fields. For files it runs as a system update
3102
+ (``bNewDocumentUpdate=True``, the CSOM ``UpdateOverwriteVersion`` path) so
3103
+ the metadata lands without creating a new version; folders have no version
3104
+ history. ``preserve_versions`` still needs the server-side API. Authorship
3105
+ values resolved for the same item ride along in this write.
466
3106
  """
467
- form_values: dict[str, str] = {}
468
- created = _sp_timestamp(item.created)
469
- modified = _sp_timestamp(item.modified)
3107
+ form_values: dict = {}
3108
+ created, modified = self._utc_to_local(item.created, item.modified)
470
3109
  if created:
471
3110
  form_values["Created"] = created
472
3111
  if modified:
473
3112
  form_values["Modified"] = modified
3113
+ form_values.update(self._pending_authorship.pop(item.dest_path, {}))
474
3114
  if not form_values:
475
3115
  return
476
3116
  securable = self._securable(item)
477
3117
  if securable is None:
478
3118
  return
479
- securable.validate_update_list_item(
3119
+ result = securable.validate_update_list_item(
480
3120
  form_values,
481
3121
  dates_in_utc=True,
482
3122
  new_document_update=item.item_type != "folder",
483
3123
  ).execute_query()
3124
+ _report_rejected_fields(result, self.warnings, f"timestamps {item.dest_path}")
3125
+
3126
+ def apply_authorship(self, item: MigrationItem) -> None:
3127
+ """Best-effort: resolve ``Author``/``Editor`` and cache them for one write.
3128
+
3129
+ A principal is applied only when its login name resolves to a target user
3130
+ (``EnsureUser``); unresolved principals are skipped (same-tenant only).
3131
+
3132
+ Nothing is sent here: a separate write per item doubled the cost of a
3133
+ preserved run, and a system update re-approves a document, so the cached
3134
+ values are better folded into the timestamps write (or in
3135
+ :meth:`finalize_fidelity` when timestamps are not preserved).
3136
+ """
3137
+ securable = self._securable(item)
3138
+ if securable is None:
3139
+ return
3140
+ author = self._principals().user(item.author.login_name if item.author else None)
3141
+ editor = self._principals().user(item.editor.login_name if item.editor else None)
3142
+ if author is not None and _is_system_principal(author):
3143
+ author = None
3144
+ if editor is not None and _is_system_principal(editor):
3145
+ editor = None
3146
+ if author is None and editor is None:
3147
+ return
3148
+ form_values: dict[str, FieldUserValue] = {}
3149
+ if author is not None:
3150
+ form_values["Author"] = FieldUserValue(author.id, author.login_name)
3151
+ if editor is not None:
3152
+ form_values["Editor"] = FieldUserValue(editor.id, editor.login_name)
3153
+ self._pending_authorship[item.dest_path] = dict(form_values)
3154
+
3155
+ def finalize_fidelity(self, item: MigrationItem) -> None:
3156
+ """Write the fidelity values that were only cached for this item.
3157
+
3158
+ The last hook wins the write: with timestamps preserved that is
3159
+ :meth:`apply_timestamps`; without them it is this method, so an item costs
3160
+ one system update instead of one per property group. Runs before the
3161
+ approval write, which must stay last.
3162
+ """
3163
+ form_values = self._pending_authorship.pop(item.dest_path, {})
3164
+ if not form_values:
3165
+ return
3166
+ securable = self._securable(item)
3167
+ if securable is None:
3168
+ return
3169
+ result = securable.validate_update_list_item(
3170
+ form_values,
3171
+ dates_in_utc=True,
3172
+ new_document_update=item.item_type != "folder",
3173
+ ).execute_query()
3174
+ _report_rejected_fields(result, self.warnings, f"authorship {item.dest_path}")
484
3175
 
485
3176
  def apply_permissions(self, item: MigrationItem, permissions: list[PermissionEntry]) -> None:
486
- """Best-effort: break inheritance and recreate the source role assignments.
3177
+ """Best-effort: replay a file's or folder's unique ACLs onto the target item.
487
3178
 
488
- Principals are resolved by login name on the target; roles that don't exist
489
- there are skipped. This is a same-tenant copy — cross-tenant identity
490
- mapping is out of scope.
3179
+ Same-tenant only — cross-tenant identity mapping is out of scope. See
3180
+ :func:`_replay_role_assignments` for what happens to principals, roles and
3181
+ inheritance that cannot be resolved.
491
3182
  """
492
3183
  securable = self._securable(item)
493
3184
  if securable is None:
494
3185
  return
495
- securable.break_role_inheritance(copy_role_assignments=False, clear_sub_scopes=False)
496
- for entry in permissions:
497
- for role_name in entry.roles:
498
- role = self._role_definition(role_name)
499
- if role is not None:
500
- securable.add_role_assignment(entry.principal_name, role)
3186
+ _replay_role_assignments(securable, self._principals(), permissions, self.warnings, item.dest_path)
501
3187
  securable.context.execute_query()
502
3188
 
3189
+ def apply_item_state(self, item: MigrationItem, state: dict) -> None:
3190
+ """Best-effort: restore a migrated file's content-approval state.
3191
+
3192
+ An uploaded file starts out approved, and the timestamp/authorship system
3193
+ updates approve it again, so a source draft or rejected file would
3194
+ otherwise silently become visible. Documents reject a moderation change
3195
+ combined with other properties (``You cannot change moderation status and
3196
+ set other item properties at that same time.``), hence a separate write,
3197
+ applied last. A library without moderation enabled has no such fields: the
3198
+ write fails and is reported, never fatal.
3199
+ """
3200
+ if item.item_type == "folder" or not state:
3201
+ return
3202
+ securable = self._securable(item)
3203
+ if securable is None:
3204
+ return
3205
+ try:
3206
+ result = securable.validate_update_list_item(
3207
+ dict(state),
3208
+ dates_in_utc=True,
3209
+ new_document_update=True,
3210
+ ).execute_query()
3211
+ except Exception as e: # noqa: BLE001 — approval state is best-effort
3212
+ self.warnings.append(f"approval state for {item.dest_path!r} could not be restored: {e}")
3213
+ return
3214
+ _report_rejected_fields(result, self.warnings, f"approval state {item.dest_path}")
3215
+
3216
+ def read_metadata(self, item: MigrationItem) -> dict | None:
3217
+ """Comparable attributes of the written item (for the metadata diff)."""
3218
+ if item.item_type == "folder":
3219
+ # A folder is an item too: the timestamp system update lands on its list
3220
+ # item, and the folder resource reflects it (same read as for a file).
3221
+ folder = (
3222
+ self._folder.context.web.get_folder_by_server_relative_path(self._url(item).rstrip("/"))
3223
+ .get()
3224
+ .execute_query()
3225
+ )
3226
+ return {
3227
+ "created": iso_or_none(folder.time_created),
3228
+ "modified": iso_or_none(folder.time_last_modified),
3229
+ }
3230
+ file = self._folder.context.web.get_file_by_server_relative_url(self._url(item)).get().execute_query()
3231
+ metadata = {
3232
+ "created": iso_or_none(file.time_created),
3233
+ "modified": iso_or_none(file.time_last_modified),
3234
+ "size_bytes": file.length or 0,
3235
+ }
3236
+ status = self._target_moderation_status(item)
3237
+ if status is not None:
3238
+ metadata["moderation_status"] = status
3239
+ return metadata
3240
+
3241
+ def _target_moderation_status(self, item: MigrationItem) -> str | None:
3242
+ """The written file's approval status, or ``None`` when not readable."""
3243
+ securable = self._securable(item)
3244
+ if securable is None:
3245
+ return None
3246
+ try:
3247
+ securable.select(["OData__ModerationStatus"]).get().execute_query()
3248
+ except Exception: # noqa: BLE001 — a library without moderation has no such column
3249
+ return None
3250
+ status = securable.properties.get("OData__ModerationStatus")
3251
+ return None if status is None else str(status)
3252
+
3253
+ def read_permissions(self, item: MigrationItem) -> list[PermissionEntry]:
3254
+ """Read the written item's *unique* role assignments (for the permission diff)."""
3255
+ securable = self._securable(item)
3256
+ if securable is None or not _has_unique_role_assignments(securable, refresh=True):
3257
+ return []
3258
+ return _read_role_assignments(securable, normalize=self._principals().normalize)
3259
+
3260
+ def _fresh_securable(self, item: MigrationItem):
3261
+ """Re-read the written item so its properties reflect what was applied."""
3262
+ securable = self._securable(item)
3263
+ if securable is not None:
3264
+ securable.get().execute_query()
3265
+ return securable
3266
+
3267
+ def read_authorship(self, item: MigrationItem) -> tuple[str | None, str | None]:
3268
+ """The written item's author/editor login names (for the authorship diff)."""
3269
+ securable = self._fresh_securable(item)
3270
+ if securable is None:
3271
+ return (None, None)
3272
+ properties = securable.properties
3273
+ return (
3274
+ _user_login(self._folder.context.web, properties.get("AuthorId"), self._login_cache),
3275
+ _user_login(self._folder.context.web, properties.get("EditorId"), self._login_cache),
3276
+ )
3277
+
503
3278
  def _securable(self, item: MigrationItem):
504
3279
  url = self._url(item).rstrip("/")
505
3280
  if item.item_type == "folder":
506
3281
  return self._folder.context.web.get_folder_by_server_relative_path(url).list_item_all_fields
507
3282
  return self._folder.context.web.get_file_by_server_relative_url(url).listItemAllFields
508
3283
 
509
- def _role_definition(self, name: str) -> "RoleDefinition | None":
510
- """Resolve (and cache) a target role definition by name."""
511
- if self._role_definitions is None:
512
- loaded = self._folder.context.web.role_definitions.get().execute_query()
513
- self._role_definitions = {rd.name: rd for rd in loaded if rd.name}
514
- return self._role_definitions.get(name)
515
-
516
3284
  def commit(self, options=None) -> None:
517
- pass
3285
+ self._pending_authorship.clear()
3286
+ self._local_cache.clear()
518
3287
 
519
3288
  def close(self) -> None:
520
3289
  pass