diff --git a/examples/onedrive/files/download.py b/examples/onedrive/files/download.py index d58b781b..8f286ff5 100644 --- a/examples/onedrive/files/download.py +++ b/examples/onedrive/files/download.py @@ -8,7 +8,6 @@ from office365.graph_client import GraphClient from tests import test_client_id, test_password, test_tenant, test_username -from tests.graph_case import acquire_token_by_username_password client = GraphClient.with_username_and_password( test_tenant, test_client_id, test_username, test_password diff --git a/examples/onedrive/files/send_sharing_invitation.py b/examples/onedrive/files/send_sharing_invitation.py index 9b2ca28d..c6568559 100644 --- a/examples/onedrive/files/send_sharing_invitation.py +++ b/examples/onedrive/files/send_sharing_invitation.py @@ -10,7 +10,6 @@ from office365.graph_client import GraphClient from tests import test_client_id, test_password, test_tenant, test_username -from tests.graph_case import acquire_token_by_username_password file_name = "Financial Sample.xlsx" client = GraphClient.with_username_and_password( diff --git a/examples/onedrive/folders/list_files.py b/examples/onedrive/folders/list_files.py index 5d9a0fe7..3b7cd83d 100644 --- a/examples/onedrive/folders/list_files.py +++ b/examples/onedrive/folders/list_files.py @@ -4,7 +4,6 @@ """ from office365.graph_client import GraphClient from tests import test_client_id, test_password, test_tenant, test_username -from tests.graph_case import acquire_token_by_username_password client = GraphClient.with_username_and_password( test_tenant, test_client_id, test_username, test_password diff --git a/examples/onenote/create_page.py b/examples/onenote/create_page.py index 9da0ba92..5e931b0e 100644 --- a/examples/onenote/create_page.py +++ b/examples/onenote/create_page.py @@ -5,9 +5,11 @@ """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) files = {} with open("../data/Sample.html", "rb") as f, open( diff --git a/examples/outlook/calendars/share_with.py b/examples/outlook/calendars/share_with.py index 70dd3a0f..3ef62d83 100644 --- a/examples/outlook/calendars/share_with.py +++ b/examples/outlook/calendars/share_with.py @@ -7,11 +7,13 @@ """ from office365.graph_client import GraphClient from office365.outlook.calendar.role_type import CalendarRoleType -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) -my_cal = client.me.calendar -cal_perm = my_cal.calendar_permissions.add( +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) + +cal_perm = client.me.calendar.calendar_permissions.add( "samanthab@adatum.onmicrosoft.com", CalendarRoleType.read ).execute_query() print(cal_perm) diff --git a/examples/outlook/events/delete.py b/examples/outlook/events/delete.py index 9bc9cc01..344e5d03 100644 --- a/examples/outlook/events/delete.py +++ b/examples/outlook/events/delete.py @@ -3,9 +3,11 @@ """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) event_id = "--event id goes here--" event_to_del = client.me.calendar.events[event_id] event_to_del.delete_object().execute_query() diff --git a/examples/outlook/events/list.py b/examples/outlook/events/list.py index f29bf965..cd6ad3c1 100644 --- a/examples/outlook/events/list.py +++ b/examples/outlook/events/list.py @@ -4,11 +4,13 @@ https://learn.microsoft.com/en-us/graph/api/calendar-list-events?view=graph-rest-1.0 """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) events = ( - client.me.calendar.events.get().top(100).select(["subject", "body"]).execute_query() + client.me.calendar.events.get().top(10).select(["subject", "body"]).execute_query() ) for event in events: print(event.subject) diff --git a/examples/outlook/messages/create_draft_with_attachments.py b/examples/outlook/messages/create_draft_with_attachments.py index 76a4b36e..9d4b5084 100644 --- a/examples/outlook/messages/create_draft_with_attachments.py +++ b/examples/outlook/messages/create_draft_with_attachments.py @@ -4,11 +4,13 @@ import base64 from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username with open(r"../../data/Sample.pdf", "rb") as f: content = base64.b64encode(f.read()).decode() -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) client.me.messages.add( subject="Meet for lunch?", body="The new cafeteria is open.", diff --git a/examples/outlook/messages/create_property.py b/examples/outlook/messages/create_property.py index 6dd5d875..27e6beda 100644 --- a/examples/outlook/messages/create_property.py +++ b/examples/outlook/messages/create_property.py @@ -8,9 +8,11 @@ import sys from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = client.me.messages.top(1).get().execute_query() if len(messages) == 0: sys.exit("No messages were found") diff --git a/examples/outlook/messages/create_subscription.py b/examples/outlook/messages/create_subscription.py index 3db7794e..195690e7 100644 --- a/examples/outlook/messages/create_subscription.py +++ b/examples/outlook/messages/create_subscription.py @@ -6,9 +6,11 @@ import datetime from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) existing_subscriptions = client.subscriptions.get().execute_query() diff --git a/examples/outlook/messages/download.py b/examples/outlook/messages/download.py index 71002a52..97b2a919 100644 --- a/examples/outlook/messages/download.py +++ b/examples/outlook/messages/download.py @@ -9,9 +9,11 @@ import tempfile from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = client.me.messages.select(["id", "subject"]).top(1).get().execute_query() with tempfile.TemporaryDirectory() as local_path: for message in messages: diff --git a/examples/outlook/messages/get_with_body.py b/examples/outlook/messages/get_with_body.py index 68ca49f2..be2fb01b 100644 --- a/examples/outlook/messages/get_with_body.py +++ b/examples/outlook/messages/get_with_body.py @@ -7,9 +7,11 @@ """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = client.me.messages.select(["subject", "body"]).top(10).get().execute_query() for message in messages: print(message.subject) diff --git a/examples/outlook/messages/list_new.py b/examples/outlook/messages/list_new.py index 8d16d8b7..3ab34051 100644 --- a/examples/outlook/messages/list_new.py +++ b/examples/outlook/messages/list_new.py @@ -5,9 +5,11 @@ """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = ( client.me.mail_folders["Inbox"] .messages.delta.change_type("created") diff --git a/examples/outlook/messages/mark_as_read.py b/examples/outlook/messages/mark_as_read.py index 3df7bf4e..fd61e858 100644 --- a/examples/outlook/messages/mark_as_read.py +++ b/examples/outlook/messages/mark_as_read.py @@ -7,9 +7,11 @@ import sys from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = client.me.messages.top(1).get().execute_query() if len(messages) == 0: sys.exit("No messages were found") diff --git a/examples/outlook/messages/move.py b/examples/outlook/messages/move.py index 8e8e64c6..ea5cf377 100644 --- a/examples/outlook/messages/move.py +++ b/examples/outlook/messages/move.py @@ -2,13 +2,15 @@ Move a message to another folder within the specified user's mailbox. This creates a new copy of the message in the destination folder and removes the original message. -https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http +https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0 """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) folder_name = "Archive" to_folder = client.me.mail_folders[folder_name] diff --git a/examples/outlook/messages/reply.py b/examples/outlook/messages/reply.py index 84f6bfaf..07562f5b 100644 --- a/examples/outlook/messages/reply.py +++ b/examples/outlook/messages/reply.py @@ -7,9 +7,11 @@ import sys from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) messages = client.me.messages.top(1).get().execute_query() if len(messages) == 0: sys.exit("No messages were found") diff --git a/examples/outlook/messages/search.py b/examples/outlook/messages/search.py index 2c0f4caa..6a603af9 100644 --- a/examples/outlook/messages/search.py +++ b/examples/outlook/messages/search.py @@ -5,10 +5,12 @@ """ from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) result = client.search.query_messages("Let's go for lunch").execute_query() for item in result.value: for hit in item.hitsContainers[0].hits: - print(hit.resource.properties.get("webLink")) + print(hit.resource.get("webLink")) diff --git a/examples/outlook/messages/send_with_attachment.py b/examples/outlook/messages/send_with_attachment.py index e4e02b85..3dc6b5ad 100644 --- a/examples/outlook/messages/send_with_attachment.py +++ b/examples/outlook/messages/send_with_attachment.py @@ -5,10 +5,17 @@ """ from office365.graph_client import GraphClient -from tests import test_user_principal_name_alt -from tests.graph_case import acquire_token_by_username_password +from tests import ( + test_client_id, + test_password, + test_tenant, + test_user_principal_name_alt, + test_username, +) -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) client.me.send_mail( subject="Meet for lunch?", body="The new cafeteria is open.", diff --git a/examples/outlook/messages/send_with_large_attachment.py b/examples/outlook/messages/send_with_large_attachment.py index 219d8194..0692177b 100644 --- a/examples/outlook/messages/send_with_large_attachment.py +++ b/examples/outlook/messages/send_with_large_attachment.py @@ -5,15 +5,23 @@ """ from office365.graph_client import GraphClient -from tests import test_user_principal_name_alt -from tests.graph_case import acquire_token_by_username_password +from tests import ( + test_client_id, + test_password, + test_tenant, + test_user_principal_name_alt, + test_username, +) def print_progress(range_pos): + # type: (int) -> None print("{0} bytes uploaded".format(range_pos)) -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) local_path = "../../../tests/data/big_buck_bunny.mp4" message = ( ( diff --git a/examples/planner/create_task.py b/examples/planner/create_task.py index 045a1183..b8f90dfb 100644 --- a/examples/planner/create_task.py +++ b/examples/planner/create_task.py @@ -5,9 +5,11 @@ import sys from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) group = client.groups.get_by_name("My Sample Team").get().execute_query() plans = group.planner.plans.get().execute_query() if len(plans) == 0: diff --git a/examples/sharepoint/fields/create_lookup.py b/examples/sharepoint/fields/create_lookup.py index d0e821fa..737827e0 100644 --- a/examples/sharepoint/fields/create_lookup.py +++ b/examples/sharepoint/fields/create_lookup.py @@ -14,5 +14,5 @@ lookup_field_name="Title", allow_multiple_values=True, ).execute_query() -print("Field {0} has been created", field.internal_name) +print("Field {0} has been created", field_name) field.delete_object().execute_query() # clean up diff --git a/examples/sharepoint/files/delete.py b/examples/sharepoint/files/delete.py index 6e548ef9..522cd45b 100644 --- a/examples/sharepoint/files/delete.py +++ b/examples/sharepoint/files/delete.py @@ -5,6 +5,15 @@ from tests import test_team_site_url, test_user_credentials ctx = ClientContext(test_team_site_url).with_credentials(test_user_credentials) -file_url = "Shared Documents/SharePoint User Guide.docx" +file_url = "Shared Documents/Financial Sample.xlsx" file = ctx.web.get_file_by_server_relative_url(file_url) -file.delete_object().execute_query() +# file.recycle().execute_query() +# or delete permanently via delete_object: +# file.delete_object().execute_query() +print("Deleted file: {0}".format(file_url)) + + +print("Print deleted files...") +result = ctx.web.get_recycle_bin_items().execute_query() +for recycle_bin_item in result: + print(recycle_bin_item) diff --git a/examples/sharepoint/webs/get_regional_settings.py b/examples/sharepoint/webs/get_regional_settings.py new file mode 100644 index 00000000..eee3a3e5 --- /dev/null +++ b/examples/sharepoint/webs/get_regional_settings.py @@ -0,0 +1,9 @@ +""" +Retrieves the locale settings of a site +""" +from office365.sharepoint.client_context import ClientContext +from tests import test_client_credentials, test_site_url + +client = ClientContext(test_site_url).with_credentials(test_client_credentials) +result = client.web.regional_settings.get().execute_query() +print(result) diff --git a/examples/teams/create_team.py b/examples/teams/create_team.py index 10679d41..8f4f79aa 100644 --- a/examples/teams/create_team.py +++ b/examples/teams/create_team.py @@ -8,10 +8,17 @@ """ from office365.graph_client import GraphClient -from tests import create_unique_name -from tests.graph_case import acquire_token_by_username_password +from tests import ( + create_unique_name, + test_client_id, + test_password, + test_tenant, + test_username, +) -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) team_name = create_unique_name("Team") print("Creating a team '{0}' ...".format(team_name)) team = client.teams.create(team_name).execute_query_and_wait() diff --git a/examples/teams/send_message.py b/examples/teams/send_message.py index 3e41b3e9..2002bff1 100644 --- a/examples/teams/send_message.py +++ b/examples/teams/send_message.py @@ -6,9 +6,11 @@ from office365.graph_client import GraphClient from office365.outlook.mail.item_body import ItemBody -from tests.graph_case import acquire_token_by_username_password +from tests import test_client_id, test_password, test_tenant, test_username -client = GraphClient(acquire_token_by_username_password) +client = GraphClient.with_username_and_password( + test_tenant, test_client_id, test_username, test_password +) my_teams = client.me.joined_teams.get().top(1).execute_query() if len(my_teams) == 0: sys.exit("No teams found") diff --git a/office365/sharepoint/alerts/collection.py b/office365/sharepoint/alerts/collection.py index cffff17b..0547db67 100644 --- a/office365/sharepoint/alerts/collection.py +++ b/office365/sharepoint/alerts/collection.py @@ -5,7 +5,7 @@ from office365.sharepoint.entity_collection import EntityCollection -class AlertCollection(EntityCollection): +class AlertCollection(EntityCollection[Alert]): """Content Type resource collection""" def __init__(self, context, resource_path=None): @@ -26,6 +26,7 @@ def add(self, parameters): return return_type def contains(self, id_alert): + # type: (str) -> ClientResult[bool] """ Returns true if the given alert exists in the alert collection. False otherwise. diff --git a/office365/sharepoint/contenttypes/content_type.py b/office365/sharepoint/contenttypes/content_type.py index 861e5b3f..5e23791e 100644 --- a/office365/sharepoint/contenttypes/content_type.py +++ b/office365/sharepoint/contenttypes/content_type.py @@ -20,7 +20,7 @@ class ContentType(Entity): """ def __str__(self): - return self.name + return self.name or self.entity_type_name def __repr__(self): return self.string_id or str(self.id) or self.entity_type_name diff --git a/office365/sharepoint/contenttypes/fieldlinks/collection.py b/office365/sharepoint/contenttypes/fieldlinks/collection.py index d6ae19c9..9bd53cb3 100644 --- a/office365/sharepoint/contenttypes/fieldlinks/collection.py +++ b/office365/sharepoint/contenttypes/fieldlinks/collection.py @@ -7,7 +7,7 @@ from office365.sharepoint.fields.field import Field -class FieldLinkCollection(EntityCollection): +class FieldLinkCollection(EntityCollection[FieldLink]): """Specifies a Collection for field links.""" def __init__(self, context, resource_path=None): diff --git a/office365/sharepoint/directory/helper.py b/office365/sharepoint/directory/helper.py index f809e9f8..27a08ec3 100644 --- a/office365/sharepoint/directory/helper.py +++ b/office365/sharepoint/directory/helper.py @@ -1,6 +1,7 @@ from office365.runtime.client_result import ClientResult from office365.runtime.paths.resource_path import ResourcePath from office365.runtime.queries.service_operation import ServiceOperationQuery +from office365.sharepoint.client_context import ClientContext from office365.sharepoint.directory.members_info import MembersInfo from office365.sharepoint.directory.membership_result import MembershipResult from office365.sharepoint.directory.my_groups_result import MyGroupsResult @@ -32,6 +33,7 @@ def is_member_of(context, principal_name, group_id, result=None): @staticmethod def check_site_availability(context, site_url): + # type: (ClientContext, str) -> ClientResult[bool] """ :param str site_url: Site Url :param office365.sharepoint.client_context.ClientContext context: SharePoint context diff --git a/office365/sharepoint/folders/folder.py b/office365/sharepoint/folders/folder.py index 29eb9668..95a92daa 100644 --- a/office365/sharepoint/folders/folder.py +++ b/office365/sharepoint/folders/folder.py @@ -554,9 +554,7 @@ def time_last_modified(self): @property def time_created(self): # type: () -> Optional[datetime] - """ - Gets when the folder was created in UTC. - """ + """Gets when the folder was created in UTC.""" return self.properties.get("TimeCreated", datetime.min) @property diff --git a/office365/sharepoint/migrationcenter/deploy_status.py b/office365/sharepoint/migrationcenter/deploy_status.py new file mode 100644 index 00000000..86686f50 --- /dev/null +++ b/office365/sharepoint/migrationcenter/deploy_status.py @@ -0,0 +1,5 @@ +from office365.sharepoint.entity import Entity + + +class MigrationCenterDeployStatus(Entity): + """""" diff --git a/office365/sharepoint/migrationcenter/service/performance/__init__.py b/office365/sharepoint/migrationcenter/service/performance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/office365/sharepoint/migrationcenter/service/performance/dashboard_data.py b/office365/sharepoint/migrationcenter/service/performance/dashboard_data.py new file mode 100644 index 00000000..55ce33a7 --- /dev/null +++ b/office365/sharepoint/migrationcenter/service/performance/dashboard_data.py @@ -0,0 +1,9 @@ +from office365.runtime.client_value import ClientValue + + +class PerformanceDashboardData(ClientValue): + """""" + + @property + def entity_type_name(self): + return "Microsoft.Online.SharePoint.MigrationCenter.Service.PerformanceDashboardData" diff --git a/office365/sharepoint/migrationcenter/service/performance/data.py b/office365/sharepoint/migrationcenter/service/performance/data.py new file mode 100644 index 00000000..55dce36f --- /dev/null +++ b/office365/sharepoint/migrationcenter/service/performance/data.py @@ -0,0 +1,7 @@ +from office365.sharepoint.migrationcenter.service.performance.entity_data import ( + MigrationPerformanceEntityData, +) + + +class PerformanceData(MigrationPerformanceEntityData): + """""" diff --git a/office365/sharepoint/migrationcenter/service/performance/entity_data.py b/office365/sharepoint/migrationcenter/service/performance/entity_data.py new file mode 100644 index 00000000..c0c4b526 --- /dev/null +++ b/office365/sharepoint/migrationcenter/service/performance/entity_data.py @@ -0,0 +1,5 @@ +from office365.sharepoint.entity import Entity + + +class MigrationPerformanceEntityData(Entity): + """""" diff --git a/office365/sharepoint/principal/principal.py b/office365/sharepoint/principal/principal.py index 03e4ac07..f2865290 100644 --- a/office365/sharepoint/principal/principal.py +++ b/office365/sharepoint/principal/principal.py @@ -8,7 +8,7 @@ class Principal(Entity): """Represents a user or group that can be assigned permissions to control security.""" def __str__(self): - return self.title + return self.title or self.entity_type_name def __repr__(self): return self.user_principal_name or self.id or self.entity_type_name @@ -28,9 +28,7 @@ def title(self): @title.setter def title(self, value): # type: (str) -> None - """ - Sets a value that specifies the name of the principal. - """ + """Sets a value that specifies the name of the principal.""" self.set_property("Title", value) @property diff --git a/office365/sharepoint/recyclebin/item.py b/office365/sharepoint/recyclebin/item.py index 4dfd6ef4..eb64eca6 100644 --- a/office365/sharepoint/recyclebin/item.py +++ b/office365/sharepoint/recyclebin/item.py @@ -1,16 +1,22 @@ -from typing import Optional +from datetime import datetime +from typing import TYPE_CHECKING, Optional from office365.runtime.paths.resource_path import ResourcePath -from office365.runtime.paths.service_operation import ServiceOperationPath -from office365.runtime.paths.v3.entity import EntityPath from office365.runtime.queries.service_operation import ServiceOperationQuery from office365.sharepoint.entity import Entity from office365.sharepoint.principal.users.user import User +from office365.sharepoint.types.resource_path import ResourcePath as SPResPath + +if TYPE_CHECKING: + from office365.sharepoint.recyclebin.item_collection import RecycleBinItemCollection class RecycleBinItem(Entity): """Represents a Recycle Bin item in the Recycle Bin of a site or a site collection.""" + def __str__(self): + return self.title or self.entity_type_name + def restore(self): """Restores the Recycle Bin item to its original location.""" qry = ServiceOperationQuery(self, "Restore") @@ -26,17 +32,95 @@ def move_to_second_stage(self): self.context.add_query(qry) return self + @property + def author_email(self): + # type: () -> Optional[str] + """Gets the email address of the user who originally created the Recycle Bin item.""" + return self.properties.get("AuthorEmail", None) + + @property + def author_name(self): + # type: () -> Optional[str] + """Gets the user display name of the user who originally created the Recycle Bin item.""" + return self.properties.get("AuthorName", None) + + @property + def deleted_by_email(self): + # type: () -> Optional[str] + """Gets the email address of the user who deleted the Recycle Bin item.""" + return self.properties.get("DeletedByEmail", None) + + @property + def deleted_by_name(self): + # type: () -> Optional[str] + """Gets the user display name of the user who deleted the Recycle Bin item.""" + return self.properties.get("DeletedByName", None) + + @property + def deleted_date_local_formatted(self): + # type: () -> Optional[str] + """ + Specifies when, in the default time zone for the current site, + the Recycle Bin item was moved to the Recycle Bin. + """ + return self.properties.get("DeletedDateLocalFormatted", None) + + @property + def dir_name(self): + # type: () -> Optional[str] + """ + Specifies the site-relative URL of the list or folder that originally contained the Recycle Bin item. + """ + return self.properties.get("DirName", None) + + @property + def dir_name_path(self): + # type: () -> Optional[SPResPath] + """Returns the site relative path of the list or folder that originally contained the Recycle Bin item.""" + return self.properties.get("DirNamePath", SPResPath()) + + @property + def item_state(self): + # type: () -> Optional[int] + """Specifies the Recycle Bin stage of the Recycle Bin item.""" + return self.properties.get("ItemState", None) + + @property + def item_type(self): + # type: () -> Optional[int] + """Specifies the type of the Recycle Bin item.""" + return self.properties.get("ItemType", None) + @property def id(self): + # type: () -> Optional[str] """Gets a value that specifies the identifier of the Recycle Bin item.""" return self.properties.get("Id", None) + @property + def leaf_name(self): + # type: () -> Optional[str] + """Specifies the leaf name of the Recycle Bin item.""" + return self.properties.get("LeafName", None) + + @property + def leaf_name_path(self): + # type: () -> Optional[SPResPath] + """Returns the leaf name path of the Recycle Bin item.""" + return self.properties.get("LeafNamePath", SPResPath()) + @property def size(self): # type: () -> Optional[int] """Gets a value that specifies the size of the Recycle Bin item in bytes.""" return self.properties.get("Size", None) + @property + def title(self): + # type: () -> Optional[str] + """Specifies the title of the Recycle Bin item.""" + return self.properties.get("Title", None) + @property def author(self): """Gets a value that specifies the user who created the Recycle Bin item.""" @@ -55,7 +139,23 @@ def deleted_by(self): @property def deleted_date(self): """Gets a value that specifies when the Recycle Bin item was moved to the Recycle Bin.""" - return self.properties.get("DeletedDate", None) + return self.properties.get("DeletedDate", datetime.min) + + @property + def parent_collection(self): + # type: () -> RecycleBinItemCollection + return self._parent_collection + + def get_property(self, name, default_value=None): + if default_value is None: + property_mapping = { + "DeletedBy": self.deleted_by, + "DeletedDate": self.deleted_date, + "DirNamePath": self.dir_name_path, + "LeafNamePath": self.leaf_name_path, + } + default_value = property_mapping.get(name, None) + return super(RecycleBinItem, self).get_property(name, default_value) def set_property(self, name, value, persist_changes=True): super(RecycleBinItem, self).set_property(name, value, persist_changes) @@ -63,8 +163,8 @@ def set_property(self, name, value, persist_changes=True): if name == "Id": if self._resource_path is None: - self._resource_path = ServiceOperationPath( - "GetById", [value], self._parent_collection.resource_path - ) + self._resource_path = self.parent_collection.get_by_id( + value + ).resource_path else: self._resource_path.patch(value) diff --git a/office365/sharepoint/sharing/information.py b/office365/sharepoint/sharing/information.py index 441ebe2a..b0bb9eae 100644 --- a/office365/sharepoint/sharing/information.py +++ b/office365/sharepoint/sharing/information.py @@ -5,6 +5,7 @@ from office365.sharepoint.sharing.links.default_templates_collection import ( SharingLinkDefaultTemplatesCollection, ) +from office365.sharepoint.sharing.permission_collection import PermissionCollection from office365.sharepoint.sharing.picker_settings import PickerSettings @@ -21,6 +22,13 @@ def access_request_settings(self): """ return self.properties.get("accessRequestSettings", AccessRequestSettings()) + @property + def permissions_information(self): + """ + The PermissionCollection that are on the list item. It contains a collection of PrincipalInfo and LinkInfo. + """ + return self.properties.get("permissionsInformation", PermissionCollection()) + @property def picker_settings(self): """PickerSettings used by the PeoplePicker Control.""" @@ -49,6 +57,7 @@ def get_property(self, name, default_value=None): if default_value is None: property_mapping = { "accessRequestSettings": self.access_request_settings, + "permissionsInformation": self.permissions_information, "pickerSettings": self.picker_settings, "sharingAbilities": self.sharing_abilities, "sharingLinkTemplates": self.sharing_link_templates, diff --git a/office365/sharepoint/sharing/link_info.py b/office365/sharepoint/sharing/link_info.py new file mode 100644 index 00000000..13ca1fb8 --- /dev/null +++ b/office365/sharepoint/sharing/link_info.py @@ -0,0 +1,6 @@ +from office365.runtime.client_value import ClientValue + + +class LinkInfo(ClientValue): + """This class provides metadata for the tokenized sharing link including settings details, inheritance status, + and an optional array of members.""" diff --git a/office365/sharepoint/sharing/permission_collection.py b/office365/sharepoint/sharing/permission_collection.py index 8ffc47a3..b1a4bdf1 100644 --- a/office365/sharepoint/sharing/permission_collection.py +++ b/office365/sharepoint/sharing/permission_collection.py @@ -1,4 +1,7 @@ from office365.runtime.client_value import ClientValue +from office365.runtime.client_value_collection import ClientValueCollection +from office365.sharepoint.sharing.link_info import LinkInfo +from office365.sharepoint.utilities.principal_info import PrincipalInfo class PermissionCollection(ClientValue): @@ -9,6 +12,21 @@ class PermissionCollection(ClientValue): implicit access. """ + def __init__( + self, hasInheritedLinks=None, links=None, principals=None, siteAdmins=None + ): + """ + :param bool hasInheritedLinks: + :param list[LinkInfo] links: The List of tokenized sharing links with their LinkInfo objects. + :param list[PrincipalInfo] principals: The List of Principals with their roles on this list item. + :param list[PrincipalInfo] siteAdmins: The List of Principals who are Site Admins. This property is returned + only if the caller is an Auditor. + """ + self.hasInheritedLinks = hasInheritedLinks + self.links = ClientValueCollection(LinkInfo, links) + self.principals = ClientValueCollection(PrincipalInfo, principals) + self.siteAdmins = ClientValueCollection(PrincipalInfo, siteAdmins) + @property def entity_type_name(self): return "SP.Sharing.PermissionCollection" diff --git a/office365/sharepoint/sites/site.py b/office365/sharepoint/sites/site.py index 919dbbaf..ddbf57e0 100644 --- a/office365/sharepoint/sites/site.py +++ b/office365/sharepoint/sites/site.py @@ -219,7 +219,9 @@ def get_recycle_bin_items(self, row_limit=100, is_ascending=True): specified in the orderBy parameter. A value of true indicates ascending order, and a value of false indicates descending order. """ - return_type = RecycleBinItemCollection(self.context) + return_type = RecycleBinItemCollection( + self.context, self.recycle_bin.resource_path + ) payload = {"rowLimit": row_limit, "isAscending": is_ascending} qry = ServiceOperationQuery( self, "GetRecycleBinItems", None, payload, None, return_type @@ -228,9 +230,7 @@ def get_recycle_bin_items(self, row_limit=100, is_ascending=True): return return_type def get_site_administrators(self): - """ - Gets site collection administrators - """ + """Gets site collection administrators""" return_type = ClientResult( self.context, ClientValueCollection(SiteAdministratorsInfo) ) diff --git a/office365/sharepoint/tenant/administration/powerapps/__init__.py b/office365/sharepoint/tenant/administration/powerapps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/office365/sharepoint/tenant/administration/powerapps/environment_context.py b/office365/sharepoint/tenant/administration/powerapps/environment_context.py new file mode 100644 index 00000000..5f19e661 --- /dev/null +++ b/office365/sharepoint/tenant/administration/powerapps/environment_context.py @@ -0,0 +1,8 @@ +from office365.runtime.client_value import ClientValue + + +class PowerAppsEnvironmentContext(ClientValue): + @property + def entity_type_name(self): + # type: () -> str + return "Microsoft.Online.SharePoint.TenantAdministration.PowerAppsEnvironmentContext" diff --git a/office365/sharepoint/webs/information_collection.py b/office365/sharepoint/webs/information_collection.py index 0d3146ec..ff7547eb 100644 --- a/office365/sharepoint/webs/information_collection.py +++ b/office365/sharepoint/webs/information_collection.py @@ -3,7 +3,7 @@ from office365.sharepoint.webs.information import WebInformation -class WebInformationCollection(EntityCollection): +class WebInformationCollection(EntityCollection[WebInformation]): """Specifies a collection of objects containing metadata about a site""" def __init__(self, context, resource_path=None): diff --git a/office365/sharepoint/webs/regional_settings.py b/office365/sharepoint/webs/regional_settings.py index 38c0a0d8..88d97d73 100644 --- a/office365/sharepoint/webs/regional_settings.py +++ b/office365/sharepoint/webs/regional_settings.py @@ -9,6 +9,30 @@ class RegionalSettings(Entity): """Represents regional settings that are used on the server that is running SharePoint Server.""" + @property + def adjust_hijri_days(self): + # type: () -> Optional[int] + """Specifies the number of days to extend or reduce the current month in Hijri calendars on the site""" + return self.properties.get("AdjustHijriDays", None) + + @property + def alternate_calendar_type(self): + # type: () -> Optional[int] + """Gets an alternate calendar type that is used on the site""" + return self.properties.get("AlternateCalendarType", None) + + @property + def am(self): + # type: () -> Optional[str] + """Specifies the string that is used to represent time before midday on the site""" + return self.properties.get("AM", None) + + @property + def calendar_type(self): + # type: () -> Optional[int] + """Specifies the calendar type that SHOULD be used when processing date values on the site""" + return self.properties.get("CalendarType", None) + @property def collation(self): # type: () -> Optional[int] @@ -19,12 +43,59 @@ def collation(self): """ return self.properties.get("Collation", None) + @property + def collation_lcid(self): + # type: () -> Optional[int] + """Gets the language code identifier (LCID) for the collation rules that are used on the site""" + return self.properties.get("CollationLCID", None) + @property def date_format(self): # type: () -> Optional[int] """Gets the date format that is used on the server.""" return self.properties.get("DateFormat", None) + @property + def date_separator(self): + # type: () -> Optional[str] + """Gets the separator that is used for dates on the server.""" + return self.properties.get("DateSeparator", None) + + @property + def decimal_separator(self): + # type: () -> Optional[str] + """Gets the separator that is used for decimals on the server.""" + return self.properties.get("DecimalSeparator", None) + + @property + def digit_grouping(self): + # type: () -> Optional[str] + """Gets the separator that is used in grouping digits.""" + return self.properties.get("DigitGrouping", None) + + @property + def first_day_of_week(self): + # type: () -> Optional[int] + """Specifies the first day of the week used in calendars on the server. + The possible values are specified in the following table: + + - 0 Sunday + - 1 Monday + - 2 Tuesday + - 3 Wednesday + - 4 Thursday + - 5 Friday + - 6 Saturday + """ + return self.properties.get("FirstDayOfWeek", None) + + @property + def first_week_of_year(self): + # type: () -> Optional[int] + """Specifies the first week of the year used in calendars on the server. + Specifies which week of a year is considered the first week.""" + return self.properties.get("FirstWeekOfYear", None) + @property def locale_id(self): # type: () -> Optional[int] diff --git a/office365/sharepoint/webs/web.py b/office365/sharepoint/webs/web.py index 495272b7..182a47c9 100644 --- a/office365/sharepoint/webs/web.py +++ b/office365/sharepoint/webs/web.py @@ -1,6 +1,6 @@ # coding=utf-8 import datetime -from typing import AnyStr, Optional +from typing import TYPE_CHECKING, AnyStr, Optional from office365.runtime.client_result import ClientResult from office365.runtime.client_value_collection import ClientValueCollection @@ -106,6 +106,9 @@ from office365.sharepoint.webs.template_collection import WebTemplateCollection from office365.sharepoint.webs.theme_info import ThemeInfo +if TYPE_CHECKING: + from office365.sharepoint.client_context import ClientContext + class Web(SecurableObject): """ @@ -549,7 +552,9 @@ def get_recycle_bin_items( :param int order_by: the column by which to order the Recycle Bin query. :param int item_state: Recycle Bin stage of items to return in the query. """ - return_type = RecycleBinItemCollection(self.context) + return_type = RecycleBinItemCollection( + self.context, self.recycle_bin.resource_path + ) payload = { "rowLimit": row_limit, "isAscending": is_ascending, @@ -651,15 +656,14 @@ def get_regional_datetime_schema(self): return return_type def get_sharing_link_data(self, link_url): + # type: (str) -> ClientResult[SharingLinkData] """ This method determines basic information about the supplied link URL, including limited data about the object the link URL refers to and any additional sharing link data if the link URL is a tokenized sharing link :param str link_url: A URL that is either a tokenized sharing link or a canonical URL for a document """ - return_type = ClientResult( - self.context, SharingLinkData() - ) # type: ClientResult[SharingLinkData] + return_type = ClientResult(self.context, SharingLinkData()) payload = {"linkUrl": link_url} qry = ServiceOperationQuery( self, "GetSharingLinkData", None, payload, None, return_type @@ -669,6 +673,7 @@ def get_sharing_link_data(self, link_url): @staticmethod def get_context_web_theme_data(context): + # type: (ClientContext) -> ClientResult[str] """ Get ThemeData for the context web. diff --git a/office365/teams/apps/catalog.py b/office365/teams/apps/catalog.py index ed998d2e..cb12c8f2 100644 --- a/office365/teams/apps/catalog.py +++ b/office365/teams/apps/catalog.py @@ -9,6 +9,7 @@ class AppCatalogs(Entity): @property def teams_apps(self): + # type: () -> EntityCollection[TeamsApp] """List apps from the Microsoft Teams app catalog.""" return self.properties.get( "teamsApps", diff --git a/office365/teams/chats/chat.py b/office365/teams/chats/chat.py index a5b5a901..2817d377 100644 --- a/office365/teams/chats/chat.py +++ b/office365/teams/chats/chat.py @@ -41,6 +41,7 @@ def last_message_preview(self): @property def members(self): + # type: () -> ConversationMemberCollection """A collection of membership records associated with the chat.""" return self.properties.setdefault( "members", @@ -51,6 +52,7 @@ def members(self): @property def messages(self): + # type: () -> EntityCollection[ChatMessage] """A collection of all the messages in the chat. Nullable.""" return self.properties.get( "messages", @@ -61,6 +63,7 @@ def messages(self): @property def operations(self): + # type: () -> EntityCollection[TeamsAsyncOperation] """ A collection of all the Teams async operations that ran or are running on the chat. Nullable. """ @@ -75,6 +78,7 @@ def operations(self): @property def tabs(self): + # type: () -> EntityCollection[TeamsTab] """A collection of all the tabs in the chat.""" return self.properties.get( "tabs", diff --git a/office365/teams/schedule/schedule.py b/office365/teams/schedule/schedule.py index f5bb42b6..94a7e925 100644 --- a/office365/teams/schedule/schedule.py +++ b/office365/teams/schedule/schedule.py @@ -22,6 +22,7 @@ def time_zone(self, value): @property def open_shift_change_requests(self): + # type: () -> EntityCollection[OpenShiftChangeRequest] """The shifts in the shifts.""" return self.properties.get( "openShiftChangeRequests", @@ -34,6 +35,7 @@ def open_shift_change_requests(self): @property def shifts(self): + # type: () -> EntityCollection[Shift] """The shifts in the shifts.""" return self.properties.get( "shifts", @@ -43,7 +45,8 @@ def shifts(self): ) @property - def scheduling_group(self): + def scheduling_groups(self): + # type: () -> EntityCollection[SchedulingGroup] """The logical grouping of users in the shifts (usually by role).""" return self.properties.get( "schedulingGroups", @@ -56,6 +59,7 @@ def scheduling_group(self): @property def time_off_reasons(self): + # type: () -> EntityCollection[TimeOffReason] """The set of reasons for a time off in the schedule.""" return self.properties.get( "timeOffReasons", @@ -70,7 +74,7 @@ def get_property(self, name, default_value=None): if default_value is None: property_mapping = { "openShiftChangeRequests": self.open_shift_change_requests, - "schedulingGroups": self.scheduling_group, + "schedulingGroups": self.scheduling_groups, "timeOffReasons": self.time_off_reasons, } default_value = property_mapping.get(name, None) diff --git a/tests/graph_case.py b/tests/graph_case.py index 5074c891..f3f340d4 100644 --- a/tests/graph_case.py +++ b/tests/graph_case.py @@ -1,10 +1,7 @@ from unittest import TestCase -import msal - from office365.graph_client import GraphClient from tests import ( - load_settings, test_client_id, test_password, test_tenant, @@ -12,24 +9,6 @@ ) -def acquire_token_by_username_password(): - settings = load_settings() - authority_url = "https://login.microsoftonline.com/{0}".format( - settings.get("default", "tenant") - ) - app = msal.PublicClientApplication( - authority=authority_url, - client_id=settings.get("client_credentials", "client_id"), - ) - - result = app.acquire_token_by_username_password( - username=settings.get("user_credentials", "username"), - password=settings.get("user_credentials", "password"), - scopes=["https://graph.microsoft.com/.default"], - ) - return result - - class GraphTestCase(TestCase): """Microsoft Graph specific test case base class""" diff --git a/tests/sharepoint/test_directory_session.py b/tests/sharepoint/test_directory_session.py index 33e78f76..805a1fb4 100644 --- a/tests/sharepoint/test_directory_session.py +++ b/tests/sharepoint/test_directory_session.py @@ -3,7 +3,7 @@ from office365.sharepoint.client_context import ClientContext from office365.sharepoint.directory.helper import SPHelper from office365.sharepoint.directory.session import DirectorySession -from tests import test_site_url, test_user_credentials, test_user_principal_name +from tests import test_site_url, test_user_credentials class TestDirectorySession(TestCase):