Skip to content

Commit ab9b2ec

Browse files
committed
update Outlook types and tests
1 parent 345b001 commit ab9b2ec

File tree

13 files changed

+416
-30
lines changed

13 files changed

+416
-30
lines changed

generator/import_metadata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ def export_to_file(path, content):
2626
"--endpoint",
2727
dest="endpoint",
2828
help="Import metadata endpoint",
29-
default="sharepoint",
29+
default="graph",
3030
)
3131
parser.add_argument(
3232
"-p",
3333
"--path",
3434
dest="path",
35-
default="./metadata/SharePoint.xml",
35+
default="./metadata/Graph.xml",
3636
help="Import metadata endpoint",
3737
)
3838

generator/metadata/Graph.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40138,6 +40138,10 @@ within the time frame of their original request."/>
4013840138
<Parameter Name="bindingParameter" Type="Collection(graph.riskyUser)"/>
4013940139
<Parameter Name="userIds" Type="Collection(Edm.String)" Unicode="false"/>
4014040140
</Action>
40141+
<Action Name="confirmSafe" IsBound="true">
40142+
<Parameter Name="bindingParameter" Type="Collection(graph.riskyUser)"/>
40143+
<Parameter Name="userIds" Type="Collection(Edm.String)" Unicode="false"/>
40144+
</Action>
4014140145
<Action Name="reprocess" IsBound="true">
4014240146
<Parameter Name="bindingParameter" Type="graph.accessPackageAssignmentRequest"/>
4014340147
</Action>
@@ -41381,6 +41385,7 @@ within the time frame of their original request."/>
4138141385
<Action Name="clockIn" IsBound="true">
4138241386
<Parameter Name="bindingParameter" Type="Collection(graph.timeCard)"/>
4138341387
<Parameter Name="isAtApprovedLocation" Type="Edm.Boolean"/>
41388+
<Parameter Name="onBehalfOfUserId" Type="Edm.String" Unicode="false"/>
4138441389
<Parameter Name="notes" Type="graph.itemBody"/>
4138541390
<ReturnType Type="graph.timeCard"/>
4138641391
</Action>

generator/metadata/SharePoint.xml

Lines changed: 293 additions & 1 deletion
Large diffs are not rendered by default.

office365/outlook/mail/folders/collection.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,17 @@
33

44

55
class MailFolderCollection(DeltaCollection[MailFolder]):
6+
"""Mail folder's collection"""
7+
68
def __init__(self, context, resource_path=None):
79
super(MailFolderCollection, self).__init__(context, MailFolder, resource_path)
10+
11+
def add(self, display_name, is_hidden=False):
12+
# type: (str, bool) -> MailFolder
13+
"""
14+
Use this API to create a new mail folder in the root folder of the user's mailbox.
15+
16+
If you intend a new folder to be hidden, you must set the isHidden property to true on creation.
17+
"""
18+
props = {"displayName": display_name, "isHidden": is_hidden}
19+
return super(MailFolderCollection, self).add(**props)

office365/outlook/mail/folders/folder.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class MailFolder(Entity):
1616
"""A mail folder in a user's mailbox, such as Inbox and Drafts. Mail folders can contain messages,
1717
other Outlook items, and child mail folders."""
1818

19+
def __str__(self):
20+
return self.display_name or self.entity_type_name
21+
1922
def copy(self, destination_id):
2023
"""
2124
Copy a mailfolder and its contents to another mailfolder.
@@ -62,6 +65,12 @@ def _mark_all_items_as_unread(col):
6265
self.messages.get_all(page_loaded=_mark_all_items_as_unread)
6366
return self
6467

68+
def permanent_delete(self):
69+
"""Permanently delete a mail folder and remove its items from the user's mailbox."""
70+
qry = ServiceOperationQuery(self, "permanentDelete")
71+
self.context.add_query(qry)
72+
return self
73+
6574
@property
6675
def child_folder_count(self):
6776
# type: () -> Optional[int]

office365/outlook/mail/folders/search.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
from typing import Optional
2+
13
from office365.outlook.mail.folders.folder import MailFolder
4+
from office365.runtime.types.collections import StringCollection
25

36

47
class MailSearchFolder(MailFolder):
@@ -9,3 +12,30 @@ class MailSearchFolder(MailFolder):
912
to appear in Outlook, Outlook for the web, or Outlook Live, the folder must be created in the
1013
WellKnownFolderName.SearchFolders folder.
1114
"""
15+
16+
@property
17+
def filter_query(self):
18+
# type: () -> Optional[str]
19+
"""The OData query to filter the messages."""
20+
return self.properties.get("filterQuery", None)
21+
22+
@property
23+
def include_nested_folders(self):
24+
# type: () -> Optional[bool]
25+
"""Indicates how the mailbox folder hierarchy should be traversed in the search. true means that a deep
26+
search should be done to include child folders in the hierarchy of each folder explicitly specified in
27+
sourceFolderIds. false means a shallow search of only each of the folders explicitly specified
28+
in sourceFolderIds."""
29+
return self.properties.get("includeNestedFolders", None)
30+
31+
@property
32+
def is_supported(self):
33+
# type: () -> Optional[bool]
34+
"""Indicates whether a search folder is editable using REST APIs."""
35+
return self.properties.get("isSupported", None)
36+
37+
@property
38+
def source_folder_ids(self):
39+
# type: () -> StringCollection
40+
"""The mailbox folders that should be mined."""
41+
return self.properties.get("sourceFolderIds", StringCollection())

tests/booking/test_business.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test3_ensure_created(self):
3434
result = self.__class__.business.get().execute_query_retry()
3535
self.assertIsNotNone(result.resource_path)
3636

37-
#def test4_get_staff_availability(self):
37+
# def test4_get_staff_availability(self):
3838
# result = self.__class__.business.get_staff_availability().execute_query()
3939
# self.assertIsNotNone(result.resource_path)
4040

tests/communications/test_callrecords.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def setUpClass(cls):
1616
def tearDownClass(cls):
1717
pass
1818

19-
#def test1_create_peer_to_peer_call(self):
19+
# def test1_create_peer_to_peer_call(self):
2020
# result = self.client.communications.calls.create("https://bot.mediadev8.com/callback").execute_query()
2121
# self.assertIsNotNone(result.resource_path)
2222

tests/decorators.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,12 @@ def wrapper(self, *args, **kwargs):
3030
if not client:
3131
self.skipTest("No client available for permission check")
3232

33-
try:
34-
permissions = _get_cached_permissions(client, test_client_id)
33+
permissions = _get_cached_permissions(client, test_client_id)
3534

36-
if not any(role.value == app_role for role in permissions):
37-
self.skipTest(f"Required app permission '{app_role}' not granted")
35+
if not any(role.value == app_role for role in permissions):
36+
self.skipTest(f"Required app permission '{app_role}' not granted")
3837

39-
return test_method(self, *args, **kwargs)
40-
41-
except Exception as e:
42-
self.skipTest(f"Permission check failed: {str(e)}")
38+
return test_method(self, *args, **kwargs)
4339

4440
return wrapper
4541

@@ -68,19 +64,15 @@ def wrapper(self, *args, **kwargs):
6864
if not client:
6965
self.skipTest("No client available for permission check")
7066

71-
try:
72-
# Get permissions from cache or API
73-
granted_scopes = _get_cached_delegated_permissions(client, test_client_id)
74-
75-
if not any(scope in granted_scopes for scope in scopes):
76-
self.skipTest(
77-
f"Required delegated permission '{', '.join(scopes)}' not granted"
78-
)
67+
# Get permissions from cache or API
68+
granted_scopes = _get_cached_delegated_permissions(client, test_client_id)
7969

80-
return test_method(self, *args, **kwargs)
70+
if not any(scope in granted_scopes for scope in scopes):
71+
self.skipTest(
72+
f"Required delegated permission '{', '.join(scopes)}' not granted"
73+
)
8174

82-
except Exception as e:
83-
self.skipTest(f"Permission check failed: {str(e)}")
75+
return test_method(self, *args, **kwargs)
8476

8577
return wrapper # type: ignore[return-value]
8678

tests/onenote/test_notebook.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from office365.onenote.notebooks.notebook import Notebook
22
from tests import create_unique_name
3+
from tests.decorators import requires_delegated_permission
34
from tests.graph_case import GraphTestCase
45

56

@@ -14,6 +15,9 @@ def setUpClass(cls):
1415
def tearDownClass(cls):
1516
pass
1617

18+
@requires_delegated_permission(
19+
"Notes.Create", "Notes.ReadWrite", "Notes.ReadWrite.All"
20+
)
1721
def test1_create_notebook(self):
1822
notebook_name = create_unique_name("My Private notebook")
1923
new_notebook = self.client.me.onenote.notebooks.add(
@@ -22,14 +26,27 @@ def test1_create_notebook(self):
2226
self.assertIsNotNone(new_notebook.resource_path)
2327
self.__class__.target_notebook = new_notebook
2428

29+
@requires_delegated_permission("Notes.Create")
2530
def test2_list_notebooks(self):
2631
my_notebooks = self.client.me.onenote.notebooks.get().execute_query()
2732
self.assertIsNotNone(my_notebooks.resource_path)
2833

34+
@requires_delegated_permission(
35+
"Notes.Create",
36+
"Notes.Read",
37+
"Notes.Read.All",
38+
"Notes.ReadWrite",
39+
"Notes.ReadWrite.All",
40+
)
2941
def test3_get_recent_notebooks(self):
3042
result = self.client.me.onenote.notebooks.get_recent_notebooks().execute_query()
3143
self.assertIsNotNone(result.value)
3244

45+
@requires_delegated_permission(
46+
"Notes.Create",
47+
"Notes.ReadWrite",
48+
"Notes.ReadWrite.All",
49+
)
3350
def test4_create_section(self):
3451
name = create_unique_name("Section name")
3552
new_section = self.__class__.target_notebook.sections.add(

0 commit comments

Comments
 (0)