Skip to content

Commit e5c140b

Browse files
SharePoint API: sharing & permissions namespaces changes (RoleDefintion and RoleAssignment resources), examples refactorings
1 parent 4de9e92 commit e5c140b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+421
-192
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# About
22
Office 365 & Microsoft Graph library for Python
33

4+
5+
#### Sponsored by
6+
[![https://tracking.gitads.io/?repo=Office365-REST-Python-Client](https://images.gitads.io/Office365-REST-Python-Client)](https://tracking.gitads.io/?repo=Office365-REST-Python-Client)
7+
48
# Usage
59

610
1. [Installation](#Installation)

examples/sharepoint/audit/__init__.py

Whitespace-only changes.

examples/sharepoint/files/__init__.py

Whitespace-only changes.

examples/sharepoint/upload_file.py renamed to examples/sharepoint/files/upload_file.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
UserCredential(settings['user_credentials']['username'],
1010
settings['user_credentials']['password']))
1111

12-
path = "../../tests/data/SharePoint User Guide.docx"
12+
path = "../../../tests/data/SharePoint User Guide.docx"
1313
with open(path, 'rb') as content_file:
1414
file_content = content_file.read()
1515

examples/sharepoint/upload_large_file.py renamed to examples/sharepoint/files/upload_large_file.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from settings import settings
2-
32
from office365.runtime.auth.userCredential import UserCredential
43
from office365.sharepoint.client_context import ClientContext
54

@@ -13,7 +12,7 @@ def print_upload_progress(offset):
1312
UserCredential(settings['user_credentials']['username'],
1413
settings['user_credentials']['password']))
1514
size_1Mb = 1000000
16-
local_path = "../../tests/data/big_buck_bunny.mp4"
15+
local_path = "../../../tests/data/big_buck_bunny.mp4"
1716
target_url = "/Shared Documents"
1817
result_file = ctx.web.get_folder_by_server_relative_url(target_url) \
1918
.files.create_upload_session(local_path, size_1Mb, print_upload_progress)

examples/sharepoint/folders/__init__.py

Whitespace-only changes.

examples/sharepoint/search/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from office365.runtime.auth.userCredential import UserCredential
2+
from office365.sharepoint.client_context import ClientContext
3+
from office365.sharepoint.search.searchRequest import SearchRequest
4+
from office365.sharepoint.search.searchService import SearchService
5+
from settings import settings
6+
7+
8+
ctx = ClientContext.connect_with_credentials(settings['url'],
9+
UserCredential(settings['user_credentials']['username'],
10+
settings['user_credentials']['password']))
11+
12+
search = SearchService(ctx)
13+
request = SearchRequest("IsDocument:1")
14+
result = search.post_query(request)
15+
ctx.execute_query()
16+
relevant_results = result.PrimaryQueryResult.RelevantResults
17+
for i in relevant_results['Table']['Rows']:
18+
cells = relevant_results['Table']['Rows'][i]['Cells']
19+
print(cells[6]['Value'])

office365/graph/directory/directoryObjectCollection.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ def add(self, user_id):
4141

4242
def remove(self, user_id):
4343
"""Remove a user from the group."""
44+
4445
qry = ServiceOperationQuery(self, "{0}/$ref".format(user_id))
4546
self.context.add_query(qry)
46-
self.context.get_pending_request().beforeExecute += self._construct_remove_user_request
4747

48-
def _construct_remove_user_request(self, request):
49-
"""
48+
def _construct_remove_user_request(request):
49+
"""
50+
:type request: RequestOptions
51+
"""
52+
request.method = HttpMethod.Delete
5053

51-
:type request: RequestOptions
52-
"""
53-
request.method = HttpMethod.Delete
54-
self.context.get_pending_request().beforeExecute -= self._construct_remove_user_request
54+
self.context.before_execute(_construct_remove_user_request)

office365/graph/directory/group.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ def add_team(self):
1919
team._parent_collection = self.parent_collection
2020
qry = ServiceOperationQuery(self, "team", None, team, None, team)
2121
self.context.add_query(qry)
22-
self.context.get_pending_request().beforeExecute += self._construct_create_team_request
22+
23+
def _construct_create_team_request(request):
24+
request.method = HttpMethod.Put
25+
request.set_header('Content-Type', "application/json")
26+
request.data = json.dumps(request.data)
27+
28+
self.context.before_execute(_construct_create_team_request)
2329
return team
2430

2531
def delete_object(self, permanent_delete=False):
@@ -33,12 +39,6 @@ def delete_object(self, permanent_delete=False):
3339
deleted_item = self.context.directory.deletedGroups[self.id]
3440
deleted_item.delete_object()
3541

36-
def _construct_create_team_request(self, request):
37-
request.method = HttpMethod.Put
38-
request.set_header('Content-Type', "application/json")
39-
request.data = json.dumps(request.data)
40-
self.context.get_pending_request().beforeExecute -= self._construct_create_team_request
41-
4242
@property
4343
def members(self):
4444
"""Users and groups that are members of this group."""

office365/graph/onedrive/driveItem.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,12 @@ def _content_downloaded(result):
6060
file_object.write(result.value)
6161

6262
self.get_content()
63-
self.context.after_query_executed(_content_downloaded)
63+
self.context.after_execute_query(_content_downloaded)
6464

6565
def create_folder(self, name):
6666
"""Create a new folder or DriveItem in a Drive with a specified parent item or path.
6767
68-
:param name: Folder name
69-
:type name: str
68+
:param str name: Folder name
7069
"""
7170
drive_item = DriveItem(self.context, None)
7271
drive_item._parent_collection = self.children

office365/runtime/client_object.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def ensure_property(self, name_or_names, action):
9191

9292
if len(names_to_include) > 0:
9393
self.context.load(self, names_to_include)
94-
self.context.after_query_executed(action)
94+
self.context.after_execute_query(action)
9595
else:
9696
action(self)
9797

office365/runtime/client_object_collection.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from office365.runtime.resource_path import ResourcePath
12
from office365.runtime.client_result import ClientResult
23
from office365.runtime.client_object import ClientObject
34
from office365.runtime.http.request_options import RequestOptions
@@ -9,7 +10,7 @@ class ClientObjectCollection(ClientObject):
910
def __init__(self, context, item_type, resource_path=None):
1011
"""Client object collection
1112
12-
:type context: ClientRuntimeContext
13+
:type context: office365.runtime.client_runtime_context.ClientRuntimeContext
1314
:type item_type: type[ClientObject]
1415
:type resource_path: ResourcePath
1516
"""
@@ -52,6 +53,9 @@ def remove_child(self, client_object):
5253
self._data.remove(client_object)
5354

5455
def __iter__(self):
56+
"""
57+
:rtype: collections.Iterable[ClientObject]
58+
"""
5559
for cur_item in self._data:
5660
yield cur_item
5761

@@ -108,7 +112,7 @@ def _calc_items_count(target_collection):
108112
list(iter(target_collection))
109113
result.value = len(target_collection)
110114
self.context.load(self)
111-
self.context.after_query_executed(_calc_items_count)
115+
self.context.after_execute_query(_calc_items_count)
112116
return result
113117

114118
def _load_paged_items(self):

office365/runtime/client_runtime_context.py

+46-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import abc
2-
from functools import partial
2+
from office365.runtime.http.request_options import RequestOptions
3+
from office365.runtime.client_query import ClientQuery
34
from office365.runtime.client_query import ReadEntityQuery
45

56

7+
68
class ClientRuntimeContext(object):
79

810
def __init__(self, service_root_url, auth_context=None):
@@ -38,16 +40,53 @@ def load(self, client_object, properties_to_retrieve=None):
3840
qry = ReadEntityQuery(client_object, properties_to_retrieve)
3941
self.get_pending_request().add_query(qry)
4042

41-
def after_query_executed(self, action):
43+
def before_execute(self, action, once=True):
44+
"""
45+
Attach an event handler which is triggered before request is submitted to server
46+
47+
:param (RequestOptions) -> None action:
48+
:param bool once:
49+
:return: None
4250
"""
43-
:param (office365.runtime.client_object.ClientObject or office365.runtime.client_result.ClientResult) -> None action:
51+
def _process_request(request):
52+
if once:
53+
self.get_pending_request().beforeExecute -= _process_request
54+
action(request)
55+
56+
self.get_pending_request().beforeExecute += _process_request
57+
58+
def after_execute(self, action, once=True):
59+
"""
60+
Attach an event handler which is triggered after request is submitted to server
61+
62+
:param (RequestOptions) -> None action:
63+
:param bool once:
64+
:return: None
4465
"""
4566

46-
def _process_response(qry, response):
67+
def _process_response(response):
68+
if once:
69+
self.get_pending_request().afterExecute -= _process_response
70+
action(response)
71+
self.get_pending_request().afterExecute += _process_response
72+
73+
def after_execute_query(self, action, query=None):
74+
"""
75+
Attach an event handler (for a query) which is triggered once the request is submitted to server
76+
77+
:type action: (office365.runtime.client_object.ClientObject or office365.runtime.client_result.ClientResult) -> None
78+
:type query: ClientQuery or None
79+
"""
80+
if query is None:
81+
query = self.get_pending_request().last_query
82+
83+
def _process_response(response):
4784
current_qry = self.get_pending_request().current_query
48-
if current_qry.id == qry.id:
49-
action(qry.return_type)
50-
self.get_pending_request().afterExecute += partial(_process_response, self.get_pending_request().last_query)
85+
if current_qry.id == query.id:
86+
self.get_pending_request().afterExecute -= _process_response
87+
action(query.return_type)
88+
89+
self.get_pending_request().afterExecute += _process_response
5190

5291
def execute_request_direct(self, request):
5392
"""

office365/sharepoint/actions/download_file.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import typing
12
from office365.runtime.http.http_method import HttpMethod
23
from office365.runtime.queries.serviceOperationQuery import ServiceOperationQuery
34

@@ -6,20 +7,20 @@ class DownloadFileQuery(ServiceOperationQuery):
67

78
def __init__(self, web, file_url, file_object):
89
"""
10+
A download file content query
911
1012
:type file_url: str
11-
:type web: Web
12-
:type file_object: any
13+
:type web: office365.sharepoint.webs.web.Web
14+
:type file_object: typing.IO
1315
"""
14-
self.file_object = file_object
15-
web.context.get_pending_request().beforeExecute += self._construct_download_query
16-
web.context.get_pending_request().afterExecute += self._process_response
17-
super(DownloadFileQuery, self).__init__(web, r"getFileByServerRelativeUrl('{0}')/\$value".format(file_url))
1816

19-
def _construct_download_query(self, request):
20-
self.binding_type.context.get_pending_request().beforeExecute -= self._construct_download_query
21-
request.method = HttpMethod.Get
17+
def _construct_download_query(request):
18+
request.method = HttpMethod.Get
19+
20+
def _process_response(response):
21+
file_object.write(response.content)
22+
23+
web.context.before_execute(_construct_download_query)
24+
web.context.after_execute(_process_response)
25+
super(DownloadFileQuery, self).__init__(web, r"getFileByServerRelativeUrl('{0}')/\$value".format(file_url))
2226

23-
def _process_response(self, response):
24-
self.binding_type.context.get_pending_request().afterExecute -= self._process_response
25-
self.file_object.write(response.content)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import typing
2+
from office365.runtime.http.http_method import HttpMethod
3+
from office365.runtime.queries.serviceOperationQuery import ServiceOperationQuery
4+
5+
6+
class UploadFileQuery(ServiceOperationQuery):
7+
8+
def __init__(self, web, file_url, file_object):
9+
"""
10+
A download file content query
11+
12+
:type file_url: str
13+
:type web: office365.sharepoint.webs.web.Web
14+
:type file_object: typing.IO
15+
"""
16+
super().__init__(web)
17+
18+
def _process_response(response):
19+
"""
20+
:type response: RequestOptions
21+
"""
22+
file_object.write(response.content)
23+
24+
def _construct_upload_request(request):
25+
request.data = file_object.read()
26+
request.method = HttpMethod.Post
27+
request.set_header('X-HTTP-Method', 'PUT')
28+
29+
web.context.before_execute(_construct_upload_request)
30+
web.context.after_execute(_process_response)
31+
super(UploadFileQuery, self).__init__(web, r"getFileByServerRelativeUrl('{0}')/\$value".format(file_url))
32+
33+

office365/sharepoint/actions/upload_session.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ def __init__(self, files, source_path, chunk_size, chunk_uploaded):
4040
self._chunk_uploaded = chunk_uploaded
4141
self._uploaded_bytes = 0
4242
self._upload_results = []
43-
self._binding_type.context.get_pending_request().afterExecute += self._build_upload_session_query
43+
self._binding_type.context.after_execute(self._build_upload_session_query)
4444

4545
def _build_upload_session_query(self, response):
46-
self._binding_type.context.get_pending_request().afterExecute -= self._build_upload_session_query
4746
st = os.stat(self._source_path)
4847
# upload a file in chunks
4948
f_pos = 0
@@ -59,7 +58,7 @@ def _build_upload_session_query(self, response):
5958
self._return_type = self.file.finish_upload(self._upload_id, f_pos, piece)
6059
f_pos += len(piece)
6160
if self._chunk_uploaded is not None:
62-
self._binding_type.context.after_query_executed(self._process_chunk_upload)
61+
self._binding_type.context.after_execute_query(self._process_chunk_upload)
6362
fh.close()
6463

6564
def _process_chunk_upload(self, result_object):

office365/sharepoint/attachments/attachmentfile.py

+48
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,59 @@
1+
import typing
12
from office365.runtime.resource_path import ResourcePath
23
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
4+
from office365.sharepoint.actions.download_file import DownloadFileQuery
5+
from office365.sharepoint.actions.upload_file import UploadFileQuery
36
from office365.sharepoint.files.file import AbstractFile
47

58

69
class AttachmentFile(AbstractFile):
710
"""Represents an attachment file in a SharePoint List Item."""
811

12+
def download(self, file_object):
13+
"""Download attachment file content
14+
15+
:type file_object: typing.IO
16+
"""
17+
18+
def _download_inner(target_attachment_file):
19+
url = self.server_relative_url
20+
qry = DownloadFileQuery(self.context.web, url, file_object)
21+
self.context.add_query(qry)
22+
23+
self.ensure_property("ServerRelativeUrl", _download_inner)
24+
25+
def upload(self, file_object):
26+
"""
27+
:type file_object: typing.IO file_object
28+
"""
29+
30+
def _upload_inner(target_attachment_file):
31+
qry = UploadFileQuery(self.context.web, self.server_relative_url, file_object)
32+
self.context.add_query(qry)
33+
34+
self.ensure_property("ServerRelativeUrl", _upload_inner)
35+
36+
@property
37+
def file_name(self):
38+
"""
39+
:rtype: str or None
40+
"""
41+
return self.properties.get("FileName", None)
42+
43+
@property
44+
def server_relative_url(self):
45+
"""
46+
:rtype: str or None
47+
"""
48+
return self.properties.get("ServerRelativeUrl", None)
49+
50+
@property
51+
def parent_collection(self):
52+
"""
53+
:rtype: office365.sharepoint.attachments.attachmentfile_collection.AttachmentFileCollection
54+
"""
55+
return self._parent_collection
56+
957
def set_property(self, name, value, persist_changes=True):
1058
super(AttachmentFile, self).set_property(name, value, persist_changes)
1159
# fallback: create a new resource path

0 commit comments

Comments
 (0)