From 6a6d61ffe76dd0e7ff73ddf27caf0d9331c3a494 Mon Sep 17 00:00:00 2001 From: vgrem Date: Mon, 6 Nov 2023 22:54:50 +0200 Subject: [PATCH] typings enhancements and examples updates --- examples/auth/register_sharepoint_apponly.py | 21 +++++++ examples/directory/applications/__init__.py | 0 examples/directory/groups/__init__.py | 0 examples/outlook/messages/mark_all_as_read.py | 2 +- examples/sharepoint/contenttypes/__init__.py | 0 .../sharepoint/contenttypes/get_by_name.py | 2 +- examples/sharepoint/features/__init__.py | 0 ...sharing_link.py => get_by_sharing_link.py} | 9 ++- .../{get_folder_files.py => get_files.py} | 0 examples/sharepoint/sharing/share_web.py | 8 ++- .../tenant/change_external_sharing.py | 47 --------------- office365/directory/audit/signins/signin.py | 20 +++---- .../directory/groups/lifecycle_policy.py | 15 ++--- .../directory/permissions/email_identity.py | 7 +++ office365/directory/rolemanagement/role.py | 4 +- .../directory/rolemanagement/template.py | 8 +-- .../rolemanagement/unified_role_assignment.py | 9 +-- .../rolemanagement/unified_role_definition.py | 10 ++-- office365/directory/users/user.py | 3 + office365/outlook/calendar/calendar.py | 2 +- office365/outlook/contacts/contact.py | 12 +--- office365/outlook/contacts/folder.py | 3 + office365/outlook/mail/messages/message.py | 20 +++---- .../sharepoint/contenttypes/content_type.py | 6 ++ office365/sharepoint/files/file.py | 3 + office365/sharepoint/folders/folder.py | 3 +- .../sharing/links/share_response.py | 3 + office365/sharepoint/sharing/result.py | 59 ++++++------------- office365/sharepoint/webs/web.py | 34 +++++------ office365/todo/attachments/base.py | 16 +++-- 30 files changed, 145 insertions(+), 181 deletions(-) create mode 100644 examples/auth/register_sharepoint_apponly.py delete mode 100644 examples/directory/applications/__init__.py delete mode 100644 examples/directory/groups/__init__.py delete mode 100644 examples/sharepoint/contenttypes/__init__.py delete mode 100644 examples/sharepoint/features/__init__.py rename examples/sharepoint/files/{get_from_sharing_link.py => get_by_sharing_link.py} (80%) rename examples/sharepoint/folders/{get_folder_files.py => get_files.py} (100%) delete mode 100644 examples/sharepoint/tenant/change_external_sharing.py diff --git a/examples/auth/register_sharepoint_apponly.py b/examples/auth/register_sharepoint_apponly.py new file mode 100644 index 000000000..2e7fd0483 --- /dev/null +++ b/examples/auth/register_sharepoint_apponly.py @@ -0,0 +1,21 @@ +""" +Setting up an Azure AD app for app-only access for SharePoint API + +Steps: + +1. create and configure a self-signed X.509 certificate, which will be used to authenticate your Application + against Azure AD, while requesting the App Only access token. For example, to create the self-signed certificate, + run the following command at a terminal prompt: + openssl req -x509 -newkey rsa:2048 -keyout selfsignkey.pem -out selfsigncert.pem -nodes -days 365 + +2. register Azure AD application +3. add permissions +4. upload certificate (public key) + +""" +from office365.graph_client import GraphClient +from tests import test_client_id, test_password, test_tenant, test_username + +admin_client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) diff --git a/examples/directory/applications/__init__.py b/examples/directory/applications/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/directory/groups/__init__.py b/examples/directory/groups/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/outlook/messages/mark_all_as_read.py b/examples/outlook/messages/mark_all_as_read.py index ab7ee1ac3..8c5c1ed4c 100644 --- a/examples/outlook/messages/mark_all_as_read.py +++ b/examples/outlook/messages/mark_all_as_read.py @@ -1,5 +1,5 @@ """ - +Marks all items (messages) in folder as read """ from office365.graph_client import GraphClient from tests import test_client_id, test_password, test_tenant, test_username diff --git a/examples/sharepoint/contenttypes/__init__.py b/examples/sharepoint/contenttypes/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/sharepoint/contenttypes/get_by_name.py b/examples/sharepoint/contenttypes/get_by_name.py index 5c97cf934..717ae5365 100644 --- a/examples/sharepoint/contenttypes/get_by_name.py +++ b/examples/sharepoint/contenttypes/get_by_name.py @@ -7,4 +7,4 @@ ctx = ClientContext(test_team_site_url).with_credentials(test_client_credentials) ct = ctx.web.content_types.get_by_name("Document").get().execute_query() -print(ct.id) +print(ct) diff --git a/examples/sharepoint/features/__init__.py b/examples/sharepoint/features/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/sharepoint/files/get_from_sharing_link.py b/examples/sharepoint/files/get_by_sharing_link.py similarity index 80% rename from examples/sharepoint/files/get_from_sharing_link.py rename to examples/sharepoint/files/get_by_sharing_link.py index 2e3341340..e9b897898 100644 --- a/examples/sharepoint/files/get_from_sharing_link.py +++ b/examples/sharepoint/files/get_by_sharing_link.py @@ -1,3 +1,7 @@ +""" +Gets file by shared link +""" + from office365.sharepoint.client_context import ClientContext from office365.sharepoint.sharing.links.kind import SharingLinkKind from tests import test_team_site_url, test_user_credentials @@ -12,6 +16,5 @@ result = file.share_link(SharingLinkKind.OrganizationView).execute_query() # Resolve file by sharing link url (guest url) -guest_url = result.value.sharingLinkInfo.Url -shared_file = ctx.web.get_file_by_guest_url(guest_url).execute_query() -print(shared_file.name) +file = ctx.web.get_file_by_guest_url(str(result.value)).execute_query() +print(file) diff --git a/examples/sharepoint/folders/get_folder_files.py b/examples/sharepoint/folders/get_files.py similarity index 100% rename from examples/sharepoint/folders/get_folder_files.py rename to examples/sharepoint/folders/get_files.py diff --git a/examples/sharepoint/sharing/share_web.py b/examples/sharepoint/sharing/share_web.py index a9b423389..179e57276 100644 --- a/examples/sharepoint/sharing/share_web.py +++ b/examples/sharepoint/sharing/share_web.py @@ -18,6 +18,10 @@ test_user_principal_name_alt, ExternalSharingSiteOption.View ).execute_query() if result.error_message is not None: - sys.exit(f"Web sharing failed: {result.error_message}") + sys.exit("Web sharing failed: {0}".format(result.error_message)) -print(f"Web '{result.url}' has been shared with user '{test_user_principal_name_alt}'") +print( + "Web '{0}' has been shared with user '{1}'".format( + result.url, test_user_principal_name_alt + ) +) diff --git a/examples/sharepoint/tenant/change_external_sharing.py b/examples/sharepoint/tenant/change_external_sharing.py deleted file mode 100644 index eb5293bd0..000000000 --- a/examples/sharepoint/tenant/change_external_sharing.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Set external sharing on site collections in Office 365 - -https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/set-external-sharing-on-site-collections-in-office-365 -""" - -from office365.sharepoint.client_context import ClientContext -from office365.sharepoint.tenant.administration.sharing_capabilities import ( - SharingCapabilities, -) -from tests import ( - test_admin_credentials, - test_admin_site_url, - test_cert_thumbprint, - test_client_id, - test_team_site_url, - test_tenant, -) - -cert_credentials = { - "tenant": test_tenant, - "client_id": test_client_id, - "thumbprint": test_cert_thumbprint, - "cert_path": "../../selfsignkey.pem", - "scopes": ["{0}/.default".format(test_admin_site_url)], -} - -admin_client = ClientContext(test_admin_site_url).with_credentials( - test_admin_credentials -) -# admin_client = ClientContext(test_admin_site_url).with_client_certificate(**cert_credentials) -site_props = admin_client.tenant.get_site_properties_by_url( - test_team_site_url, True -).execute_query() -default_value = SharingCapabilities.ExternalUserAndGuestSharing - -if site_props.sharing_capability != default_value: - print("Enabling external sharing on site: {0} ...".format(test_team_site_url)) - site_props.sharing_capability = default_value - site_props.update().execute_query() - print("Updated.") -else: - print( - "External sharing has already been enabled on site: {0}".format( - test_team_site_url - ) - ) diff --git a/office365/directory/audit/signins/signin.py b/office365/directory/audit/signins/signin.py index 6c533d09a..4d5cd87d7 100644 --- a/office365/directory/audit/signins/signin.py +++ b/office365/directory/audit/signins/signin.py @@ -14,18 +14,14 @@ class SignIn(Entity): @property def app_display_name(self): - """ - App name displayed in the Azure Portal. - :rtype: str - """ + # type: () -> Optional[str] + """App name displayed in the Azure Portal.""" return self.properties.get("appDisplayName", None) @property def app_id(self): - """ - Unique GUID representing the app ID in the Azure Active Directory. - :rtype: str - """ + # type: () -> Optional[str] + """Unique GUID representing the app ID in the Azure Active Directory.""" return self.properties.get("appId", None) @property @@ -92,34 +88,34 @@ def resource_id(self): @property def risk_detail(self): + # type: () -> Optional[str] """ Provides the 'reason' behind a specific state of a risky user, sign-in or a risk event. - :rtype: str or None """ return self.properties.get("riskDetail", None) @property def user_id(self): + # type: () -> Optional[str] """ ID of the user that initiated the sign-in. Supports $filter (eq operator only). - :rtype: str or None """ return self.properties.get("userId", None) @property def user_principal_name(self): + # type: () -> Optional[str] """ User principal name of the user that initiated the sign-in. Supports $filter (eq and startsWith operators only). - :rtype: str or None """ return self.properties.get("userPrincipalName", None) @property def status(self): + # type: () -> Optional[SignInStatus] """ Sign-in status. Includes the error code and description of the error (in case of a sign-in failure). Supports $filter (eq operator only) on errorCode property. - :rtype: str or None """ return self.properties.get("status", SignInStatus()) diff --git a/office365/directory/groups/lifecycle_policy.py b/office365/directory/groups/lifecycle_policy.py index 5f155695d..22d884c08 100644 --- a/office365/directory/groups/lifecycle_policy.py +++ b/office365/directory/groups/lifecycle_policy.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.entity import Entity from office365.runtime.client_result import ClientResult from office365.runtime.queries.service_operation import ServiceOperationQuery @@ -28,12 +30,12 @@ def add_group(self, group_id): return return_type def remove_group(self, group_id): + # type: (str) -> ClientResult[bool] """ Removes a group from a lifecycle policy. - :param str group_id: The identifier of the group to add to the policy. """ - return_type = ClientResult[bool](self.context) + return_type = ClientResult(self.context) payload = {"groupId": group_id} qry = ServiceOperationQuery( self, "removeGroup", None, payload, None, return_type @@ -43,25 +45,24 @@ def remove_group(self, group_id): @property def alternate_notification_emails(self): + # type: () -> Optional[str] """ List of email address to send notifications for groups without owners. Multiple email address can be defined by separating email address with a semicolon. - :rtype: str or None """ return self.properties.get("alternateNotificationEmails", None) @property def group_lifetime_in_days(self): + # type: () -> Optional[int] """ Number of days before a group expires and needs to be renewed. Once renewed, the group expiration is extended by the number of days defined. - :rtype: int or None """ return self.properties.get("groupLifetimeInDays", None) @property def managed_group_types(self): - """The group type for which the expiration policy applies. Possible values are All, Selected or None. - :rtype: str or None - """ + # type: () -> Optional[str] + """The group type for which the expiration policy applies. Possible values are All, Selected or None.""" return self.properties.get("managedGroupTypes", None) diff --git a/office365/directory/permissions/email_identity.py b/office365/directory/permissions/email_identity.py index a160ae16e..36a12a03c 100644 --- a/office365/directory/permissions/email_identity.py +++ b/office365/directory/permissions/email_identity.py @@ -3,3 +3,10 @@ class EmailIdentity(Identity): """Represents the email identity of a user.""" + + def __init__(self, id_=None, email=None, display_name=None): + """ + :param str email: + """ + super(EmailIdentity, self).__init__(display_name, id_) + self.email = email diff --git a/office365/directory/rolemanagement/role.py b/office365/directory/rolemanagement/role.py index b0a314672..495c28fc6 100644 --- a/office365/directory/rolemanagement/role.py +++ b/office365/directory/rolemanagement/role.py @@ -24,9 +24,7 @@ def display_name(self): @property def members(self): - """ - Users that are members of this directory role. - """ + """Users that are members of this directory role.""" from office365.directory.object_collection import DirectoryObjectCollection return self.properties.get( diff --git a/office365/directory/rolemanagement/template.py b/office365/directory/rolemanagement/template.py index 3f2727d35..6986796a8 100644 --- a/office365/directory/rolemanagement/template.py +++ b/office365/directory/rolemanagement/template.py @@ -13,15 +13,11 @@ def __repr__(self): @property def display_name(self): # type: () -> Optional[str] - """ - The display name to set for the directory role - """ + """The display name to set for the directory role""" return self.properties.get("displayName", None) @property def description(self): # type: () -> Optional[str] - """ - The display name to set for the directory role - """ + """The display name to set for the directory role""" return self.properties.get("description", None) diff --git a/office365/directory/rolemanagement/unified_role_assignment.py b/office365/directory/rolemanagement/unified_role_assignment.py index 33687783b..8b37fff71 100644 --- a/office365/directory/rolemanagement/unified_role_assignment.py +++ b/office365/directory/rolemanagement/unified_role_assignment.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.directory.rolemanagement.unified_role_definition import ( UnifiedRoleDefinition, ) @@ -13,20 +15,19 @@ class UnifiedRoleAssignment(Entity): @property def app_scope_id(self): + # type: () -> Optional[str] """ Identifier of the app-specific scope when the assignment scope is app-specific. Either this property or directoryScopeId is required. App scopes are scopes that are defined and understood by this application only. Use / for tenant-wide app scopes. Use directoryScopeId to limit the scope to particular directory objects, for example, administrative units. Supports $filter (eq, in). - :rtype: str """ return self.properties.get("appScopeId", None) @property def condition(self): - """ - :rtype: str - """ + # type: () -> Optional[str] + """ """ return self.properties.get("condition", None) @property diff --git a/office365/directory/rolemanagement/unified_role_definition.py b/office365/directory/rolemanagement/unified_role_definition.py index e8d4993a5..8bc376832 100644 --- a/office365/directory/rolemanagement/unified_role_definition.py +++ b/office365/directory/rolemanagement/unified_role_definition.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.directory.rolemanagement.unified_role_permission import ( UnifiedRolePermission, ) @@ -13,16 +15,15 @@ class UnifiedRoleDefinition(Entity): @property def display_name(self): - """The display name for the unifiedRoleDefinition. - :rtype: str - """ + # type: () -> Optional[str] + """The display name for the unifiedRoleDefinition.""" return self.properties.get("displayName", None) @property def is_built_in(self): + # type: () -> Optional[bool] """Flag indicating whether the role definition is part of the default set included in Azure Active Directory (Azure AD) or a custom definition. - :rtype: bool """ return self.properties.get("isBuiltIn", None) @@ -37,6 +38,7 @@ def role_permissions(self): @property def inherits_permissions_from(self): + # type: () -> EntityCollection[UnifiedRoleDefinition] """ Read-only collection of role definitions that the given role definition inherits from. Only Azure AD built-in roles (isBuiltIn is true) support this attribute. Supports $expand. diff --git a/office365/directory/users/user.py b/office365/directory/users/user.py index c6853e150..13cbd79a9 100644 --- a/office365/directory/users/user.py +++ b/office365/directory/users/user.py @@ -71,6 +71,9 @@ class User(DirectoryObject): """Represents an Azure AD user account. Inherits from directoryObject.""" + def __repr__(self): + return self.user_principal_name or self.id or self.entity_type_name + def add_extension(self, name): """ Creates an open extension (openTypeExtension object) and add custom properties in a new or existing instance diff --git a/office365/outlook/calendar/calendar.py b/office365/outlook/calendar/calendar.py index c1049d792..3cd7844fb 100644 --- a/office365/outlook/calendar/calendar.py +++ b/office365/outlook/calendar/calendar.py @@ -28,7 +28,7 @@ class Calendar(Entity): """ def __repr__(self): - return self.name + return self.name or self.id or self.entity_type_name def allowed_calendar_sharing_roles(self, user): """ diff --git a/office365/outlook/contacts/contact.py b/office365/outlook/contacts/contact.py index bdd06efee..6d0791c5b 100644 --- a/office365/outlook/contacts/contact.py +++ b/office365/outlook/contacts/contact.py @@ -45,25 +45,19 @@ def manager(self): @manager.setter def manager(self, value): # type: (str) -> None - """ - Sets name of the contact's manager. - """ + """Sets name of the contact's manager.""" self.set_property("manager", value) @property def mobile_phone(self): # type: () -> Optional[str] - """ - The contact's mobile phone number. - """ + """The contact's mobile phone number.""" return self.properties.get("mobilePhone", None) @mobile_phone.setter def mobile_phone(self, value): # type: (str) -> None - """ - Sets contact's mobile phone number. - """ + """Sets contact's mobile phone number.""" self.set_property("mobilePhone", value) @property diff --git a/office365/outlook/contacts/folder.py b/office365/outlook/contacts/folder.py index 65c0d34f3..e075ca00c 100644 --- a/office365/outlook/contacts/folder.py +++ b/office365/outlook/contacts/folder.py @@ -24,6 +24,7 @@ def contacts(self): @property def child_folders(self): + # type: () -> EntityCollection["ContactFolder"] """The collection of child folders in the folder. Navigation property. Read-only. Nullable.""" return self.properties.get( "childFolders", @@ -36,6 +37,7 @@ def child_folders(self): @property def multi_value_extended_properties(self): + # type: () -> EntityCollection[MultiValueLegacyExtendedProperty] """The collection of multi-value extended properties defined for the Contact folder.""" return self.properties.get( "multiValueExtendedProperties", @@ -48,6 +50,7 @@ def multi_value_extended_properties(self): @property def single_value_extended_properties(self): + # type: () -> EntityCollection[SingleValueLegacyExtendedProperty] """The collection of single-value extended properties defined for the Contact folder.""" return self.properties.get( "singleValueExtendedProperties", diff --git a/office365/outlook/mail/messages/message.py b/office365/outlook/mail/messages/message.py index c4ff5d7f6..b52cbd926 100644 --- a/office365/outlook/mail/messages/message.py +++ b/office365/outlook/mail/messages/message.py @@ -205,13 +205,12 @@ def forward(self, to_recipients, comment=""): @property def has_attachments(self): + # type: () -> Optional[bool] """ Indicates whether the message has attachments. This property doesn't include inline attachments, so if a message contains only inline attachments, this property is false. To verify the existence of inline attachments, parse the body property to look for a src attribute, such as . - - :rtype: bool or None """ return self.properties.get("hasAttachments", None) @@ -229,6 +228,7 @@ def attachments(self): @property def extensions(self): + # type: () -> EntityCollection[Extension] """The collection of open extensions defined for the message. Nullable.""" return self.properties.get( "extensions", @@ -253,24 +253,19 @@ def body(self, value): @property def body_preview(self): # type: () -> Optional[str] - """ - The first 255 characters of the message body. It is in text format.""" + """The first 255 characters of the message body. It is in text format.""" return self.properties.get("bodyPreview", None) @property def conversation_id(self): # type: () -> Optional[str] - """ - The ID of the conversation the email belongs to. - """ + """The ID of the conversation the email belongs to.""" return self.properties.get("conversationId", None) @property def conversation_index(self): # type: () -> Optional[str] - """ - Indicates the position of the message within the conversation. - """ + """Indicates the position of the message within the conversation.""" return self.properties.get("conversationIndex", None) @property @@ -292,9 +287,7 @@ def sent_from(self): @property def importance(self): # type: () -> Optional[str] - """ - The importance of the message. - """ + """The importance of the message.""" return self.properties.get("importance", None) @property @@ -308,6 +301,7 @@ def inference_classification(self): @property def internet_message_headers(self): + # type: () -> ClientValueCollection[InternetMessageHeader] """ A collection of message headers defined by RFC5322. The set includes message headers indicating the network path taken by a message from the sender to the recipient. It can also contain custom message headers that diff --git a/office365/sharepoint/contenttypes/content_type.py b/office365/sharepoint/contenttypes/content_type.py index 265e8fe88..861e5b3fc 100644 --- a/office365/sharepoint/contenttypes/content_type.py +++ b/office365/sharepoint/contenttypes/content_type.py @@ -19,6 +19,12 @@ class ContentType(Entity): to items of those types """ + def __str__(self): + return self.name + + def __repr__(self): + return self.string_id or str(self.id) or self.entity_type_name + def reorder_fields(self, field_names): """ The ReorderFields method is called to change the order in which fields appear in a content type. diff --git a/office365/sharepoint/files/file.py b/office365/sharepoint/files/file.py index f9ab64085..7ec63c705 100644 --- a/office365/sharepoint/files/file.py +++ b/office365/sharepoint/files/file.py @@ -64,6 +64,9 @@ def __repr__(self): else: return self.serverRelativeUrl or self.unique_id or self.entity_type_name + def __str__(self): + return self.name + @staticmethod def from_url(abs_url): """ diff --git a/office365/sharepoint/folders/folder.py b/office365/sharepoint/folders/folder.py index ad94829fa..91bc6d6ba 100644 --- a/office365/sharepoint/folders/folder.py +++ b/office365/sharepoint/folders/folder.py @@ -14,6 +14,7 @@ from office365.sharepoint.entity import Entity from office365.sharepoint.listitems.listitem import ListItem from office365.sharepoint.sharing.document_manager import DocumentSharingManager +from office365.sharepoint.sharing.links.share_response import ShareLinkResponse from office365.sharepoint.sharing.user_sharing_result import UserSharingResult from office365.sharepoint.storagemetrics.storage_metrics import StorageMetrics from office365.sharepoint.types.resource_path import ResourcePath as SPResPath @@ -202,7 +203,7 @@ def _move_folder(): return return_type def share_link(self, link_kind, expiration=None, role=None, password=None): - # type: (int, Optional[datetime], Optional[int], Optional[str]) -> str + # type: (int, Optional[datetime], Optional[int], Optional[str]) -> ClientResult[ShareLinkResponse] """Creates a tokenized sharing link for a folder based on the specified parameters and optionally sends an email to the people that are listed in the specified parameters. diff --git a/office365/sharepoint/sharing/links/share_response.py b/office365/sharepoint/sharing/links/share_response.py index d89de09e6..718cd4200 100644 --- a/office365/sharepoint/sharing/links/share_response.py +++ b/office365/sharepoint/sharing/links/share_response.py @@ -13,6 +13,9 @@ def __init__(self, sharing_link_info=SharingLinkInfo()): """ self.sharingLinkInfo = sharing_link_info + def __str__(self): + return self.sharingLinkInfo.Url + @property def entity_type_name(self): return "SP.Sharing.ShareLinkResponse" diff --git a/office365/sharepoint/sharing/result.py b/office365/sharepoint/sharing/result.py index 1205267d1..205fb2ac1 100644 --- a/office365/sharepoint/sharing/result.py +++ b/office365/sharepoint/sharing/result.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.runtime.client_value_collection import ClientValueCollection from office365.runtime.paths.resource_path import ResourcePath from office365.sharepoint.entity import Entity @@ -13,63 +15,48 @@ class SharingResult(Entity): @property def url(self): - """Gets the URL of the securable object being shared. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets the URL of the securable object being shared.""" return self.properties.get("Url", None) @property def error_message(self): - """Gets an error message about the failure if sharing was unsuccessful. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets an error message about the failure if sharing was unsuccessful.""" return self.properties.get("ErrorMessage", None) @property def name(self): - """ - Gets the name of the securable object being shared. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets the name of the securable object being shared.""" return self.properties.get("Name", None) @property def icon_url(self): - """ - Gets a URL to an icon that represents the securable object, if one exists. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets a URL to an icon that represents the securable object, if one exists.""" return self.properties.get("IconUrl", None) @property def status_code(self): + # type: () -> Optional[int] """ Gets the enumeration value which summarizes the result of the sharing operation. - - :rtype: int or None """ return self.properties.get("StatusCode", None) @property def permissions_page_relative_url(self): - """ - Gets the relative URL of the page that shows permissions. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets the relative URL of the page that shows permissions.""" return self.properties.get("PermissionsPageRelativeUrl", None) @property def invited_users(self): + # type: () -> ClientValueCollection[SPInvitationCreationResult] """ Gets a list of SPInvitationCreationResult (section 3.2.5.325) objects representing the external users being invited to have access. - - :rtype: ClientValueCollection """ return self.properties.get( "InvitedUsers", ClientValueCollection(SPInvitationCreationResult) @@ -77,20 +64,13 @@ def invited_users(self): @property def uniquely_permissioned_users(self): - """ - - :rtype: ClientValueCollection - """ + # type: () -> ClientValueCollection[UserSharingResult] return self.properties.get( "UniquelyPermissionedUsers", ClientValueCollection(UserSharingResult) ) @property def groups_shared_with(self): - """ - - :rtype: ClientValueCollection - """ return self.properties.get( "GroupsSharedWith", GroupCollection( @@ -100,11 +80,8 @@ def groups_shared_with(self): @property def users_added_to_group(self): - """ - Gets the list of users being added to the SharePoint permissions group. - - :rtype: ClientValueCollection - """ + # type: () -> ClientValueCollection[UserSharingResult] + """Gets the list of users being added to the SharePoint permissions group.""" return self.properties.get( "UsersAddedToGroup", ClientValueCollection(UserSharingResult) ) @@ -124,5 +101,5 @@ def set_property(self, name, value, persist_changes=True): if self._resource_path is None: if name == "Name": pass - # self._resource_path = ResourcePath(value, self._parent_collection.resource_path) + # self._resource_path = ResourcePath(value, self.parent_collection.resource_path) return self diff --git a/office365/sharepoint/webs/web.py b/office365/sharepoint/webs/web.py index d81dbf8e1..402f11d9a 100644 --- a/office365/sharepoint/webs/web.py +++ b/office365/sharepoint/webs/web.py @@ -1098,7 +1098,6 @@ def get_custom_list_templates(self): def get_file_by_guest_url(self, guest_url): """ Returns the file object from the guest access URL. - :param str guest_url: The guest access URL to get the file with. """ return_type = File(self.context) @@ -1282,9 +1281,10 @@ def share( """ return_type = SharingResult(self.context) - def _picker_value_resolved(resp, picker_result, group): + def _share(picker_result, group): + # type: (ClientResult[str], Group) -> None picker_input = "[{0}]".format(picker_result.value) - role_value = "group:{groupId}".format(groupId=group.properties["Id"]) + role_value = "group:{groupId}".format(groupId=group.id) Web.share_object( self.context, self.url, @@ -1299,26 +1299,26 @@ def _picker_value_resolved(resp, picker_result, group): return_type=return_type, ) - def _grp_resolved(group): - picker_result = ( - ClientPeoplePickerWebServiceInterface.client_people_picker_resolve_user( - self.context, user_principal_name - ) - ) - self.context.after_execute( - _picker_value_resolved, True, picker_result, group - ) - def _web_resolved(): groups = { ExternalSharingSiteOption.View: self.associated_visitor_group, ExternalSharingSiteOption.Edit: self.associated_member_group, ExternalSharingSiteOption.Owner: self.associated_owner_group, - } - selected_group = groups[share_option] - self.context.load(selected_group, after_loaded=_grp_resolved) + } # type: dict[ExternalSharingSiteOption, Group] + + ClientPeoplePickerWebServiceInterface.client_people_picker_resolve_user( + self.context, user_principal_name + ).after_execute(_share, groups[share_option]) - self.ensure_property("Url", _web_resolved) + self.ensure_properties( + [ + "Url", + "AssociatedVisitorGroup", + "AssociatedMemberGroup", + "AssociatedOwnerGroup", + ], + _web_resolved, + ) return return_type def unshare(self): diff --git a/office365/todo/attachments/base.py b/office365/todo/attachments/base.py index 6f9ab1195..d8d2863d4 100644 --- a/office365/todo/attachments/base.py +++ b/office365/todo/attachments/base.py @@ -1,4 +1,5 @@ import datetime +from typing import Optional from office365.entity import Entity @@ -9,14 +10,13 @@ class AttachmentBase(Entity): @property def content_type(self): - """ - The MIME type. - :rtype: str or None - """ + # type: () -> Optional[str] + """The MIME type.""" return self.properties.get("contentType", None) @property def last_modified_datetime(self): + # type: () -> Optional[datetime.datetime] """ The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. """ @@ -24,16 +24,14 @@ def last_modified_datetime(self): @property def name(self): + # type: () -> Optional[str] """ The display name of the attachment. This does not need to be the actual file name. - :rtype: str or None """ return self.properties.get("name", None) @property def size(self): - """ - The length of the attachment in bytes. - :rtype: int or None - """ + # type: () -> Optional[int] + """The length of the attachment in bytes.""" return self.properties.get("size", None)