diff --git a/examples/__init__.py b/examples/__init__.py
index ff62e0649..e69de29bb 100644
--- a/examples/__init__.py
+++ b/examples/__init__.py
@@ -1,9 +0,0 @@
-from office365.onedrive.drives.drive import Drive
-
-
-def upload_sample_files(drive):
- # type: (Drive) -> None
- local_paths = ["../../data/Financial Sample.xlsx"]
- for local_path in local_paths:
- file = drive.root.resumable_upload(local_path).get().execute_query()
- print("File {0} has been uploaded", file.web_url)
diff --git a/examples/onedrive/files/share_invitation.py b/examples/onedrive/files/share_invitation.py
index 6c6ffe7cb..f4f8bd2ad 100644
--- a/examples/onedrive/files/share_invitation.py
+++ b/examples/onedrive/files/share_invitation.py
@@ -8,13 +8,11 @@
"""
from datetime import datetime, timedelta
-from examples import upload_sample_files
from office365.graph_client import GraphClient
from tests.graph_case import acquire_token_by_username_password
file_name = "Financial Sample.xlsx"
client = GraphClient(acquire_token_by_username_password)
-# upload_sample_files(client.me.drive)
file_item = client.me.drive.root.get_by_path(file_name)
expired = datetime.utcnow() + timedelta(days=1)
permissions = file_item.invite(
diff --git a/examples/onedrive/files/upload_large.py b/examples/onedrive/files/upload_large.py
index e89617805..054df8337 100644
--- a/examples/onedrive/files/upload_large.py
+++ b/examples/onedrive/files/upload_large.py
@@ -7,15 +7,14 @@
from office365.graph_client import GraphClient
from tests.graph_case import acquire_token_by_username_password
-client = GraphClient(acquire_token_by_username_password)
-
-chunk_size = 3 * 1024 * 1024
-
def print_progress(range_pos):
+ # type: (int) -> None
print("{0} bytes uploaded".format(range_pos))
+client = GraphClient(acquire_token_by_username_password)
+chunk_size = 1 * 1024 * 1024
local_path = "../../../tests/data/big_buck_bunny.mp4"
remote_folder = client.me.drive.root.get_by_path("archive")
remote_file = (
diff --git a/examples/outlook/messages/search.py b/examples/outlook/messages/search.py
index d2215fbd2..2c0f4caa7 100644
--- a/examples/outlook/messages/search.py
+++ b/examples/outlook/messages/search.py
@@ -4,11 +4,11 @@
https://learn.microsoft.com/en-us/graph/search-concept-messages
"""
-import json
-
from office365.graph_client import GraphClient
from tests.graph_case import acquire_token_by_username_password
client = GraphClient(acquire_token_by_username_password)
result = client.search.query_messages("Let's go for lunch").execute_query()
-print(json.dumps(result.value.to_json(), indent=4))
+for item in result.value:
+ for hit in item.hitsContainers[0].hits:
+ print(hit.resource.properties.get("webLink"))
diff --git a/examples/outlook/messages/send_with_large_attachment.py b/examples/outlook/messages/send_with_large_attachment.py
index e731b0743..219d81949 100644
--- a/examples/outlook/messages/send_with_large_attachment.py
+++ b/examples/outlook/messages/send_with_large_attachment.py
@@ -1,28 +1,31 @@
"""
-Demonstrates how to upload large attachment to Outlook message
+Demonstrates how send e message with large attachment to Outlook message
https://learn.microsoft.com/en-us/graph/api/attachment-createuploadsession?view=graph-rest-1.0
"""
from office365.graph_client import GraphClient
+from tests import test_user_principal_name_alt
from tests.graph_case import acquire_token_by_username_password
-client = GraphClient(acquire_token_by_username_password)
-
-local_path = "../../../tests/data/big_buck_bunny.mp4"
-
def print_progress(range_pos):
print("{0} bytes uploaded".format(range_pos))
+client = GraphClient(acquire_token_by_username_password)
+local_path = "../../../tests/data/big_buck_bunny.mp4"
message = (
- client.me.messages.add(
- subject="Meet for lunch?",
- body="The new cafeteria is open.",
- to_recipients=["fannyd@contoso.onmicrosoft.com"],
+ (
+ client.me.messages.add(
+ subject="Meet for lunch?",
+ body="The new cafeteria is open.",
+ to_recipients=[
+ "fannyd@contoso.onmicrosoft.com",
+ test_user_principal_name_alt,
+ ],
+ ).upload_attachment(local_path, print_progress)
)
- .upload_attachment(local_path, print_progress)
+ .send()
.execute_query()
)
-message.send().execute_query()
diff --git a/generator/metadata/MicrosoftGraph.xml b/generator/metadata/MicrosoftGraph.xml
index 9b7c115ae..4fc9a8cf2 100644
--- a/generator/metadata/MicrosoftGraph.xml
+++ b/generator/metadata/MicrosoftGraph.xml
@@ -3871,6 +3871,11 @@
+
+
+
+
+
@@ -5242,6 +5247,11 @@
+
+
+
+
+
@@ -5298,11 +5308,18 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5311,6 +5328,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5407,13 +5437,6 @@
-
-
-
-
-
-
-
@@ -20612,6 +20635,7 @@
+
@@ -22504,6 +22528,7 @@
+
@@ -22905,6 +22930,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -23111,14 +23184,6 @@
-
-
-
-
-
-
-
-
@@ -23126,10 +23191,6 @@
-
-
-
-
@@ -23170,17 +23231,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -23225,18 +23275,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -31068,6 +31106,12 @@
+
+
+
+
+
+
diff --git a/office365/onedrive/driveitems/driveItem.py b/office365/onedrive/driveitems/driveItem.py
index 9f4e7609b..ab0702a24 100644
--- a/office365/onedrive/driveitems/driveItem.py
+++ b/office365/onedrive/driveitems/driveItem.py
@@ -31,9 +31,6 @@
from office365.onedrive.folders.folder import Folder
from office365.onedrive.internal.paths.children import ChildrenPath
from office365.onedrive.internal.paths.url import UrlPath
-from office365.onedrive.internal.queries.resumable_file_upload import (
- create_resumable_file_upload_query,
-)
from office365.onedrive.listitems.item_reference import ItemReference
from office365.onedrive.listitems.list_item import ListItem
from office365.onedrive.operations.pending import PendingOperations
@@ -50,6 +47,7 @@
from office365.runtime.http.http_method import HttpMethod
from office365.runtime.http.request_options import RequestOptions
from office365.runtime.odata.v4.upload_session import UploadSession
+from office365.runtime.odata.v4.upload_session_request import UploadSessionRequest
from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.queries.create_entity import CreateEntityQuery
from office365.runtime.queries.function import FunctionQuery
@@ -198,12 +196,21 @@ def resumable_upload(self, source_path, chunk_size=2000000, chunk_uploaded=None)
:param str source_path: File path
:param int chunk_size: chunk size
"""
+
+ def _start_upload():
+ with open(source_path, "rb") as local_file:
+ session_request = UploadSessionRequest(
+ local_file, chunk_size, chunk_uploaded
+ )
+ session_request.execute_query(qry)
+
file_name = os.path.basename(source_path)
return_type = DriveItem(self.context, UrlPath(file_name, self.resource_path))
- qry = create_resumable_file_upload_query(
- return_type, source_path, chunk_size, chunk_uploaded
+
+ qry = UploadSessionQuery(
+ return_type, {"item": DriveItemUploadableProperties(name=file_name)}
)
- self.context.add_query(qry)
+ self.context.add_query(qry).after_query_execute(_start_upload)
return return_type
def create_upload_session(self, item):
diff --git a/office365/onedrive/internal/queries/resumable_file_upload.py b/office365/onedrive/internal/queries/resumable_file_upload.py
deleted file mode 100644
index 87f6607e0..000000000
--- a/office365/onedrive/internal/queries/resumable_file_upload.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import os
-
-from office365.onedrive.driveitems.uploadable_properties import (
- DriveItemUploadableProperties,
-)
-from office365.runtime.odata.v4.upload_session_request import UploadSessionRequest
-from office365.runtime.queries.upload_session import UploadSessionQuery
-
-
-def create_resumable_file_upload_query(
- return_type, local_path, chunk_size, chunk_uploaded
-):
- """
- :type return_type: office365.onedrive.driveitems.driveItem.DriveItem
- :type local_path: str
- :type chunk_size: int
- :type chunk_uploaded: (int)->None
- """
- item = DriveItemUploadableProperties()
- item.name = os.path.basename(local_path)
- qry = UploadSessionQuery(return_type, {"item": item})
- context = return_type.context
-
- def _start_upload(resp):
- """
- :type resp: requests.Response
- """
- resp.raise_for_status()
- with open(local_path, "rb") as local_file:
- session_request = UploadSessionRequest(
- local_file, chunk_size, chunk_uploaded
- )
- session_request.execute_query(qry)
-
- context.after_execute(_start_upload)
- return qry
diff --git a/office365/outlook/internal/queries/attachment_upload.py b/office365/outlook/internal/queries/attachment_upload.py
deleted file mode 100644
index c9a32e125..000000000
--- a/office365/outlook/internal/queries/attachment_upload.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from office365.outlook.mail.attachments.attachment_item import AttachmentItem
-from office365.runtime.compat import parse_query_string
-from office365.runtime.odata.v4.upload_session_request import UploadSessionRequest
-from office365.runtime.queries.upload_session import UploadSessionQuery
-
-
-def create_attachment_upload_query(
- binding_type, return_type, source_path, chunk_size=1000000, chunk_uploaded=None
-):
- """
- :type binding_type: office365.outlook.mail.attachments.collection.AttachmentCollection
- :type return_type: FileAttachment
- :type source_path: str
- :type chunk_size: int
- :type chunk_uploaded: (int)->None
- """
- qry = UploadSessionQuery(
- binding_type, {"AttachmentItem": AttachmentItem.create_file(source_path)}
- )
- context = binding_type.context
-
- def _start_upload(resp):
- """
- :type resp: requests.Response
- """
- resp.raise_for_status()
- with open(source_path, "rb") as local_file:
- session_request = UploadSessionRequest(
- local_file, chunk_size, chunk_uploaded
- )
-
- def _construct_request(request):
- auth_token = parse_query_string(request.url, "authtoken")
- request.set_header("Authorization", "Bearer {0}".format(auth_token))
-
- session_request.beforeExecute += _construct_request
-
- def _process_response(response):
- """
- :type response: requests.Response
- """
- location = response.headers.get("Location", None)
- if location is None:
- return
- attachment_id = location[location.find("Attachments(") + 13 : -2]
- return_type.set_property("id", attachment_id)
-
- session_request.afterExecute += _process_response
-
- session_request.execute_query(qry)
-
- context.after_execute(_start_upload)
- return qry
diff --git a/office365/outlook/mail/attachments/collection.py b/office365/outlook/mail/attachments/collection.py
index b1c133779..1b95e9d24 100644
--- a/office365/outlook/mail/attachments/collection.py
+++ b/office365/outlook/mail/attachments/collection.py
@@ -1,10 +1,11 @@
import base64
+import requests
+
from office365.entity_collection import EntityCollection
-from office365.outlook.internal.queries.attachment_upload import (
- create_attachment_upload_query,
-)
from office365.outlook.mail.attachments.attachment import Attachment
+from office365.runtime.compat import parse_query_string
+from office365.runtime.odata.v4.upload_session_request import UploadSessionRequest
from office365.runtime.queries.upload_session import UploadSessionQuery
@@ -49,14 +50,41 @@ def resumable_upload(self, source_path, chunk_size=1000000, chunk_uploaded=None)
:param int chunk_size: File chunk size
:param (int)->None chunk_uploaded: Upload action
"""
+ from office365.outlook.mail.attachments.attachment_item import AttachmentItem
from office365.outlook.mail.attachments.file import FileAttachment
return_type = FileAttachment(self.context)
self.add_child(return_type)
- qry = create_attachment_upload_query(
- self, return_type, source_path, chunk_size, chunk_uploaded
+
+ qry = UploadSessionQuery(
+ self, {"AttachmentItem": AttachmentItem.create_file(source_path)}
+ )
+
+ def _start_upload():
+ with open(source_path, "rb") as local_file:
+ session_request = UploadSessionRequest(
+ local_file, chunk_size, chunk_uploaded
+ )
+
+ def _construct_request(request):
+ auth_token = parse_query_string(request.url, "authtoken")
+ request.set_header("Authorization", "Bearer {0}".format(auth_token))
+
+ def _process_response(response):
+ # type: (requests.Response) -> None
+ location = response.headers.get("Location", None)
+ if location is None:
+ return
+ attachment_id = location[location.find("Attachments(") + 13 : -2]
+ return_type.set_property("id", attachment_id)
+
+ session_request.beforeExecute += _construct_request
+ session_request.afterExecute += _process_response
+ session_request.execute_query(qry)
+
+ self.context.add_query(qry).after_query_execute(
+ _start_upload, execute_first=True
)
- self.context.add_query(qry)
return self
def create_upload_session(self, attachment_item):
diff --git a/office365/outlook/mail/messages/collection.py b/office365/outlook/mail/messages/collection.py
index 43027ec7c..5983c7bad 100644
--- a/office365/outlook/mail/messages/collection.py
+++ b/office365/outlook/mail/messages/collection.py
@@ -16,7 +16,6 @@ def add(self, subject=None, body=None, to_recipients=None, **kwargs):
:param str subject: The subject of the message.
:param str or ItemBody body: The body of the message. It can be in HTML or text format
:param list[str] to_recipients:
- :rtype: Message
"""
if to_recipients is not None:
kwargs["toRecipients"] = ClientValueCollection(
diff --git a/office365/runtime/odata/v4/upload_session_request.py b/office365/runtime/odata/v4/upload_session_request.py
index 3427e2837..05f489ae3 100644
--- a/office365/runtime/odata/v4/upload_session_request.py
+++ b/office365/runtime/odata/v4/upload_session_request.py
@@ -1,62 +1,57 @@
import os
+import typing
+from typing import Callable
+
+import requests
+from typing_extensions import Self
from office365.runtime.client_request import ClientRequest
from office365.runtime.http.http_method import HttpMethod
from office365.runtime.http.request_options import RequestOptions
+from office365.runtime.queries.upload_session import UploadSessionQuery
class UploadSessionRequest(ClientRequest):
def __init__(self, file_object, chunk_size, chunk_uploaded=None):
- """
- :type file_object: typing.IO
- :type chunk_size: int
- :type chunk_uploaded: (int) -> None
- """
+ # type: (typing.IO, int, Callable[[int], None]) -> None
super(UploadSessionRequest, self).__init__()
self._file_object = file_object
self._chunk_size = chunk_size
self._chunk_uploaded = chunk_uploaded
- self._range_start = 0
- self._range_end = 0
+ self._range_data = None
def build_request(self, query):
- """
- :type query: office365.runtime.queries.upload_session.UploadSessionQuery
- """
- range_data = self._read_next()
+ # type: (UploadSessionQuery) -> Self
request = RequestOptions(query.upload_session_url)
request.method = HttpMethod.Put
- request.set_header("Content-Length", str(len(range_data)))
+ request.set_header("Content-Length", str(len(self._range_data)))
request.set_header(
"Content-Range",
"bytes {0}-{1}/{2}".format(
- self._range_start, self._range_end - 1, self.file_size
+ self.range_start, self.range_end - 1, self.file_size
),
)
request.set_header("Accept", "*/*")
- request.data = range_data
+ request.data = self._range_data
return request
def process_response(self, response, query):
- """
- :type response: requests.Response
- :type query: office365.runtime.queries.upload_session.UploadSessionQuery
- """
+ # type: (requests.Response, UploadSessionQuery) -> None
response.raise_for_status()
if callable(self._chunk_uploaded):
self._chunk_uploaded(self.range_end)
- if self.has_pending_read:
- self.execute_query(query)
- def _read_next(self):
- self._range_start = self._file_object.tell()
- content = self._file_object.read(self._chunk_size)
- self._range_end = self._file_object.tell()
- return content
+ def execute_query(self, query):
+ # type: (UploadSessionQuery) -> None
+ for self._range_data in self._read_next():
+ super(UploadSessionRequest, self).execute_query(query)
- @property
- def has_pending_read(self):
- return self._range_end < self.file_size
+ def _read_next(self):
+ while True:
+ content = self._file_object.read(self._chunk_size)
+ if not content:
+ break
+ yield content
@property
def file_size(self):
@@ -64,8 +59,10 @@ def file_size(self):
@property
def range_start(self):
- return self._range_start
+ if self.range_end == 0:
+ return 0
+ return self.range_end - len(self._range_data)
@property
def range_end(self):
- return self._range_end
+ return self._file_object.tell()
diff --git a/office365/search/entity.py b/office365/search/entity.py
index c7ffd3322..8aae49baa 100644
--- a/office365/search/entity.py
+++ b/office365/search/entity.py
@@ -31,7 +31,17 @@ def query(self, query_string, entity_types=None):
payload = {"requests": ClientValueCollection(SearchRequest, [search_request])}
return_type = ClientResult(self.context, ClientValueCollection(SearchResponse))
qry = ServiceOperationQuery(self, "query", None, payload, None, return_type)
- self.context.add_query(qry)
+
+ def _process_response():
+ # patch 'resource' nav property
+ for item in return_type.value:
+ for hitCs in item.hitsContainers:
+ for hit in hitCs.hits:
+ json = hit.resource
+ hit.resource = Entity(self.context)
+ self.context.pending_request().map_json(json, hit.resource)
+
+ self.context.add_query(qry).after_query_execute(_process_response)
return return_type
def query_messages(self, query_string):