Skip to content

Commit

Permalink
Refactorings & improvements (fluent interface for ClientResult) Relea…
Browse files Browse the repository at this point in the history
…se 2.3.3
  • Loading branch information
[email protected] authored and [email protected] committed Apr 25, 2021
1 parent 190e9dd commit 98448bb
Show file tree
Hide file tree
Showing 61 changed files with 296 additions and 313 deletions.
34 changes: 12 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Usage
import adal
from office365.graph_client import GraphClient

def acquire_token():
def acquire_token_func():
authority_url = 'https://login.microsoftonline.com/{tenant_id_or_name}'
auth_ctx = adal.AuthenticationContext(authority_url)
token = auth_ctx.acquire_token_with_client_credentials(
Expand All @@ -211,7 +211,7 @@ The example demonstrates how to send an email via [Microsoft Graph endpoint](htt
```python
from office365.graph_client import GraphClient

client = GraphClient(acquire_token)
client = GraphClient(acquire_token_func)

message_json = {
"Message": {
Expand All @@ -232,8 +232,7 @@ message_json = {
}

login_name = "[email protected]"
client.users[login_name].send_mail(message_json)
client.execute_query()
client.users[login_name].send_mail(message_json).execute_query()
```


Expand All @@ -251,7 +250,7 @@ is used to obtain token
```python
import msal

def acquire_token():
def acquire_token_func():
"""
Acquire token via MSAL
"""
Expand Down Expand Up @@ -279,10 +278,8 @@ which corresponds to [`list available drives` endpoint](https://docs.microsoft.c
from office365.graph_client import GraphClient

tenant_name = "contoso.onmicrosoft.com"
client = GraphClient(acquire_token)
drives = client.drives
client.load(drives)
client.execute_query()
client = GraphClient(acquire_token_func)
drives = client.drives.get().execute_query()
for drive in drives:
print("Drive url: {0}".format(drive.web_url))
```
Expand All @@ -292,12 +289,9 @@ for drive in drives:

```python
from office365.graph_client import GraphClient
client = GraphClient(acquire_token)
client = GraphClient(acquire_token_func)
# retrieve drive properties
drive = client.users["{user_id_or_principal_name}"].drive
client.load(drive)
client.execute_query()

drive = client.users["{user_id_or_principal_name}"].drive.get().execute_query()
# download files from OneDrive into local folder
with tempfile.TemporaryDirectory() as path:
download_files(drive.root, path)
Expand All @@ -307,15 +301,12 @@ where

```python
def download_files(remote_folder, local_path):
drive_items = remote_folder.children
client.load(drive_items)
client.execute_query()
drive_items = remote_folder.children.get().execute_query()
for drive_item in drive_items:
if not drive_item.file.is_server_object_null: # is file?
# download file content
with open(os.path.join(local_path, drive_item.name), 'wb') as local_file:
drive_item.download(local_file)
client.execute_query()
drive_item.download(local_file).execute_query()
```


Expand All @@ -339,9 +330,8 @@ which corresponds to [`Create team` endpoint](https://docs.microsoft.com/en-us/g
```python
from office365.graph_client import GraphClient
tenant_name = "contoso.onmicrosoft.com"
client = GraphClient(tenant_name, acquire_token)
new_team = client.groups["{group_id}"].add_team()
client.execute_query()
client = GraphClient(tenant_name, acquire_token_func)
new_team = client.groups["{group_id}"].add_team().execute_query_retry()
```


Expand Down
41 changes: 13 additions & 28 deletions examples/onedrive/export_files.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
# --------------------------------------------------------------------------
# Example demonstrates how to export OneDrive files into local file system
# --------------------------------------------------------------------------

import os
import tempfile

from examples import acquire_token_client_credentials
from office365.graph_client import GraphClient
from tests import test_user_principal_name


def download_files(remote_folder, local_path):
"""
:type remote_folder: office365.onedrive.driveItem.DriveItem
:type local_path: str
"""
drive_items = remote_folder.children.get().execute_query()
for drive_item in drive_items:
if not drive_item.file.is_server_object_null: # is file?
# download file content
with open(os.path.join(local_path, drive_item.name), 'wb') as local_file:
drive_item.download(local_file)
client.execute_query()
print("File '{0}' has been downloaded".format(local_file.name))


# --------------------------------------------------------------------------
# Example demonstrates how to export OneDrive files into local file system
# --------------------------------------------------------------------------

# connect
client = GraphClient(acquire_token_client_credentials)
drive = client.users[test_user_principal_name].drive # get user's drive
with tempfile.TemporaryDirectory() as local_path:
drive_items = drive.root.children.get().execute_query()
file_items = [item for item in drive_items if not item.file.is_server_object_null] # files only
for drive_item in file_items:
with open(os.path.join(local_path, drive_item.name), 'wb') as local_file:
drive_item.download(local_file).execute_query() # download file content
print("File '{0}' has been downloaded".format(local_file.name))

# load drive properties
target_user_name = settings.get('first_account_name')
drive = client.users[target_user_name].drive
# download files from OneDrive
with tempfile.TemporaryDirectory() as path:
download_files(drive.root, path)
print("Done")
5 changes: 2 additions & 3 deletions examples/outlook/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@
},
"SaveToSentItems": "false"
}

client.users[test_user_principal_name].send_mail(message_json)
client.execute_query()
target_user = client.users[test_user_principal_name]
target_user.send_mail(message_json).execute_query()
19 changes: 19 additions & 0 deletions examples/sharepoint/lists_and_items/delete_lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from xml.etree import ElementTree
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.lists.list import List
from tests import test_client_credentials, test_team_site_url

ctx = ClientContext(test_team_site_url).with_credentials(test_client_credentials)


def is_custom_list(list_object):
xml = ElementTree.fromstring(list_object.properties["SchemaXml"])
scope_id = xml.attrib['ScopeId']
return True


lists = ctx.web.lists.select(["Title", "SchemaXml"]).top(10).get().execute_query()
lists_to_delete = [l for l in lists if is_custom_list(l)]
for list_obj in lists_to_delete: # type: List
print(f"Deleting list .. {list_obj.title}")
# list_obj.delete_object().execute_query()
10 changes: 5 additions & 5 deletions examples/sharepoint/search/search_documents.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import json

from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.search.searchRequest import SearchRequest
from office365.sharepoint.search.searchService import SearchService
from tests import test_site_url, test_user_credentials

ctx = ClientContext(test_site_url).with_credentials(test_user_credentials)

search = SearchService(ctx)
request = SearchRequest("IsDocument:1")
result = search.post_query(request)
ctx.execute_query()
relevant_results = result.PrimaryQueryResult.RelevantResults
result = search.post_query(request).execute_query()
relevant_results = result.value.PrimaryQueryResult.RelevantResults
for i in relevant_results['Table']['Rows']:
cells = relevant_results['Table']['Rows'][i]['Cells']
print(cells[6]['Value'])
print(json.dumps(cells, sort_keys=True, indent=4))
3 changes: 2 additions & 1 deletion office365/calendar/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from office365.calendar.event_collection import EventCollection
from office365.calendar.schedule_information import ScheduleInformation
from office365.entity import Entity
from office365.runtime.client_result import ClientResult
from office365.runtime.client_value_collection import ClientValueCollection
from office365.runtime.queries.service_operation_query import ServiceOperationQuery
from office365.runtime.resource_path import ResourcePath
Expand Down Expand Up @@ -32,7 +33,7 @@ def get_schedule(self, schedules, startTime=None, endTime=None, availabilityView
"endTime": DateTimeTimeZone.parse(endTime),
"availabilityViewInterval": availabilityViewInterval
}
result = ClientValueCollection(ScheduleInformation)
result = ClientResult(self.context, ClientValueCollection(ScheduleInformation))
qry = ServiceOperationQuery(self, "getSchedule", None, payload, None, result)
self.context.add_query(qry)
return result
Expand Down
8 changes: 5 additions & 3 deletions office365/directory/application.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from office365.directory.directoryObject import DirectoryObject
from office365.directory.keyCredential import KeyCredential
from office365.directory.passwordCredential import PasswordCredential
from office365.runtime.client_result import ClientResult
from office365.runtime.client_value_collection import ClientValueCollection
from office365.runtime.queries.service_operation_query import ServiceOperationQuery

Expand All @@ -18,10 +19,11 @@ def add_password(self, display_name):
"""Adds a strong password to an application.
:param str display_name: App display name
"""
return_type = PasswordCredential(displayName=display_name)
qry = ServiceOperationQuery(self, "addPassword", None, return_type, None, return_type)
params = PasswordCredential(displayName=display_name)
result = ClientResult(self.context, params)
qry = ServiceOperationQuery(self, "addPassword", None, params, None, result)
self.context.add_query(qry)
return return_type
return result

def remove_password(self, keyId):
"""Remove a password from an application."""
Expand Down
5 changes: 3 additions & 2 deletions office365/directory/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from office365.calendar.event_collection import EventCollection
from office365.mail.message_collection import MessageCollection
from office365.onedrive.siteCollection import SiteCollection
from office365.runtime.client_result import ClientResult
from office365.runtime.client_value_collection import ClientValueCollection
from office365.runtime.http.http_method import HttpMethod
from office365.runtime.queries.service_operation_query import ServiceOperationQuery
Expand Down Expand Up @@ -85,7 +86,7 @@ def find_meeting_times(self):
that the suggested results may differ over time.
"""
result = MeetingTimeSuggestionsResult()
result = ClientResult(self.context, MeetingTimeSuggestionsResult())
qry = ServiceOperationQuery(self, "findMeetingTimes", None, None, None, result)
self.context.add_query(qry)
return result
Expand Down Expand Up @@ -120,7 +121,7 @@ def get_reminder_view(self, start_dt, end_dt):
:param datetime.datetime start_dt: The start date and time of the event for which the reminder is set up.
The value is represented in ISO 8601 format, for example, "2015-11-08T19:00:00.0000000".
"""
result = ClientValueCollection(Reminder)
result = ClientResult(self.context, ClientValueCollection(Reminder))
params = {
"startDateTime": start_dt.isoformat(),
"endDateTime": end_dt.isoformat(),
Expand Down
2 changes: 1 addition & 1 deletion office365/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ def id(self):
def set_property(self, name, value, persist_changes=True):
super(Entity, self).set_property(name, value, persist_changes)
if name == "id" and self._resource_path is None:
self._resource_path = ResourcePath(value,self._parent_collection.resource_path)
self._resource_path = ResourcePath(value, self._parent_collection.resource_path)
return self
2 changes: 1 addition & 1 deletion office365/graph_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def shares(self):
return SharedDriveItemCollection(self, ResourcePath("shares"))

@property
def directoryObjects(self):
def directory_objects(self):
"""Get Directory Objects"""
return DirectoryObjectCollection(self, ResourcePath("directoryObjects"))

Expand Down
6 changes: 6 additions & 0 deletions office365/mail/bodyType.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class BodyType:
def __init__(self):
pass

html = "html"
text = "text"
7 changes: 7 additions & 0 deletions office365/mail/importance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Importance:
def __init__(self):
pass

low = "low"
normal = "normal"
high = "high"
13 changes: 8 additions & 5 deletions office365/mail/message_collection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from office365.entity_collection import EntityCollection
from office365.mail.bodyType import BodyType
from office365.mail.importance import Importance
from office365.mail.itemBody import ItemBody
from office365.mail.message import Message
from office365.mail.recipient import RecipientCollection
Expand All @@ -10,21 +12,22 @@ class MessageCollection(EntityCollection):
def __init__(self, context, resource_path=None):
super(MessageCollection, self).__init__(context, Message, resource_path)

def add(self, subject, content_html, to_emails):
def add(self, subject, content_html, to_recipient_emails, importance=Importance.low):
"""
Use this API to create a draft of a new message. Drafts can be created in any folder
and optionally updated before sending. To save to the Drafts folder, use the /messages shortcut.
:param int importance:
:param str subject:
:param str content_html:
:param list[str] to_emails:
:param list[str] to_recipient_emails:
:rtype: Message
"""

payload = {
"subject": subject,
"importance": "Low",
"body": ItemBody(content_html, "HTML"),
"toRecipients": RecipientCollection.from_emails(to_emails),
"importance": importance,
"body": ItemBody(content_html, BodyType.html),
"toRecipients": RecipientCollection.from_emails(to_recipient_emails),
}
return self.add_from_json(payload)

Expand Down
23 changes: 11 additions & 12 deletions office365/onedrive/driveItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,27 @@ class DriveItem(BaseItem):
"""The driveItem resource represents a file, folder, or other item stored in a drive. All file system objects in
OneDrive and SharePoint are returned as driveItem resources """

def create_link(self, type_, scope="", expirationDateTime=None, password=None, message=""):
def create_link(self, link_type, scope="", expiration_datetime=None, password=None, message=""):
"""
The createLink action will create a new sharing link if the specified link type doesn't already exist
for the calling application. If a sharing link of the specified type already exists for the app,
the existing sharing link will be returned.
:param str type_: The type of sharing link to create. Either view, edit, or embed.
:param str link_type: The type of sharing link to create. Either view, edit, or embed.
:param str scope: The scope of link to create. Either anonymous or organization.
:param str expirationDateTime: A String with format of yyyy-MM-ddTHH:mm:ssZ of DateTime indicates the expiration
:param str expiration_datetime: A String with format of yyyy-MM-ddTHH:mm:ssZ of DateTime indicates the expiration
time of the permission.
:param str password: The password of the sharing link that is set by the creator. Optional
and OneDrive Personal only.
:param str message:
"""
payload = {
"type": type_,
"type": link_type,
"scope": scope,
"message": message
"message": message,
"expirationDateTime": expiration_datetime,
"password": password
}
payload = {k: v for k, v in payload.items() if v is not None}

permission = Permission(self.context)
self.permissions.add_child(permission)
qry = ServiceOperationQuery(self, "createLink", None, payload, None, permission)
Expand Down Expand Up @@ -165,6 +165,7 @@ def download(self, file_object):
def _content_downloaded(resp):
file_object.write(result.value)
self.context.after_execute(_content_downloaded)
return self

def create_folder(self, name):
"""Create a new folder or DriveItem in a Drive with a specified parent item or path.
Expand Down Expand Up @@ -337,9 +338,7 @@ def set_property(self, name, value, persist_changes=True):
super(DriveItem, self).set_property(name, value, persist_changes)
if name == "id" and self._resource_path.parent.segment == "children":
self._resource_path = ResourcePath(
value,
ResourcePath("items", self._parent_collection.resource_path.parent.parent))
# elif name == "id" and self._resource_path.parent.segment == "root":
# self._resource_path = ResourcePath(value,
# ResourcePath("items", self._resource_path.parent.parent))
value, ResourcePath("items", self._parent_collection.resource_path.parent.parent))
elif name == "id" and self._resource_path.parent.segment == "root":
self._resource_path = ResourcePath(value, ResourcePath("items", self._resource_path.parent.parent))
return self
Loading

0 comments on commit 98448bb

Please sign in to comment.