Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions grapi/api/v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,20 @@ def add_routes(self, default_backend, options):
attachments, suffix="by_id")
self.add_route(user + '/messages/{itemid}/attachments/{attachmentid}',
attachments, suffix="by_id")
self.add_route(user + '/messages/{itemid}/attachments/{attachmentid}/item',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I can't find this endpoint (../item or ../item/..) under the "Attachments" section in the Microsoft Graph. Would you please share the Microsoft Graph page you've followed for these endpoints?

attachments, suffix="embedded_item_by_id")
self.add_route(user + '/messages/{itemid}/attachments/{attachmentid}/item/$value',
messages, suffix="embedded_value")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask you to change the suffix to be more consistent? Something like embedded_item_by_value like the previous route. What's your idea?

self.add_route(user + '/messages/{itemid}/attachments/{attachmentid}/$value',
attachments, suffix="binary_by_id")
self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments',
attachments, suffix="in_folder_by_id")
self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments/{attachmentid}',
attachments, suffix="in_folder_by_id")
self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments/{attachmentid}/item',
attachments, suffix="embedded_item_by_id")
self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments/{attachmentid}/item/$value',
messages, suffix="embedded_folder_value")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the same for here. Something like embedded_item_by_value. What's your idea?

self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments/{attachmentid}/$value',
attachments, suffix="binary_in_folder_by_id")

Expand Down
52 changes: 43 additions & 9 deletions grapi/backend/kopano/attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,26 @@ def on_get_by_id(self, req, resp, itemid, attachmentid=None):
item = get_item(req, itemid)
self._response_attachments(req, resp, item, attachmentid)

def on_get_embedded_item_by_id(self, req, resp, itemid, attachmentid):
"""Response attachment embedded item by itemid with attachment ID.

Args:
req (Request): Falcon request object.
resp (Response): Falcon response object.
item (Item): instance of an item.
attachmentid (str): attachment ID which is related to the item. Must be set.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
attachmentid (str): attachment ID which is related to the item. Must be set.
attachmentid (str): attachment ID which is related to the item.

As it doesn't have any default value, it's not needed to mention it. Because Python will raise an error anyway.

"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

if not attachmentid:
raise falcon.HTTPNotFound()
Comment on lines +204 to +205
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this validation is needed? Because attachmentid has no default value, it will raise an error anyway.


response = partial(self.respond, req, resp)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion: you can call self.respond without using the partial function based on your function implementation.

item = get_item(req, itemid)
attachment = item.attachment(attachmentid)
embedded_item = attachment.item

response(embedded_item, message.EmbeddedMessageResource.fields)

def on_get_binary_by_id(self, req, resp, itemid, attachmentid):
"""Return attachment's raw by itemid and attachmentid.

Expand Down Expand Up @@ -218,6 +238,18 @@ def on_get_in_folder_by_id(self, req, resp, folderid, itemid, attachmentid=None)
item = get_item_by_folder(req, folder, itemid)
self._response_attachments(req, resp, item, attachmentid)

def on_get_embedded_item_in_folder_by_id(self, req, resp, folderid, itemid, attachmentid):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find any suffix in the routes list with this name. Where does this method connect to?

"""Response attachment embedded item by itemid with attachment ID in a specific folder.

Args:
req (Request): Falcon request object.
resp (Response): Falcon response object.
folderid (str): folder ID which the item exists in.
itemid (str): item ID (e.g. message ID or event ID).
attachmentid (str): attachment ID which is related to the item. Must be set.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
attachmentid (str): attachment ID which is related to the item. Must be set.
attachmentid (str): attachment ID which is related to the item.

The same for here.

"""
self.on_get_embedded_item_by_id(req, resp, itemid, attachmentid)

def on_get_binary_in_folder_by_id(self, req, resp, folderid, itemid, attachmentid):
"""Return attachment's raw by itemid and attachmentid in a specific folder.

Expand All @@ -236,11 +268,6 @@ def on_get_binary_in_folder_by_id(self, req, resp, folderid, itemid, attachmenti
def _response_attachments(self, req, resp, item, attachmentid=None):
"""Response attachments by itemid with or without attachment ID.

Todo:
to return list of attachments, we're hardcoded fields of FileAttachmentResource
while it should be more dynamic to support other types like reference or item
attachments as well. (issue: KC-1914)

Args:
req (Request): Falcon request object.
resp (Response): Falcon response object.
Expand All @@ -250,9 +277,16 @@ def _response_attachments(self, req, resp, item, attachmentid=None):
response = partial(self.respond, req, resp)
if attachmentid is None:
attachments = list(item.attachments(embedded=True))

att_fields = []
for att in attachments:
if att.embedded is True:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if att.embedded is True:
if att.embedded:

Just a suggestion.

att_fields.append((att, ItemAttachmentResource))
else:
att_fields.append((att, FileAttachmentResource))

response(
(attachments, DEFAULT_TOP, 0, len(attachments)),
FileAttachmentResource.fields
(att_fields, DEFAULT_TOP, 0, len(att_fields))
)
else:
attachment = item.attachment(attachmentid)
Expand Down Expand Up @@ -386,6 +420,6 @@ class ItemAttachmentResource(AttachmentResource):
'name': lambda attachment: attachment.item.subject, # TODO faster? attachment.something?
})

expansions = {
'microsoft.graph.itemAttachment/item': lambda attachment: (attachment.item, message.EmbeddedMessageResource),
relations = {
'item': lambda attachment: (attachment.item, message.EmbeddedMessageResource),
}
47 changes: 46 additions & 1 deletion grapi/backend/kopano/message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import falcon

import inetmapi

from grapi.api.v1.schema import message as message_schema

from . import attachment # import as module since this is a circular import
Expand Down Expand Up @@ -124,7 +126,7 @@ class MessageResource(ItemResource):
deleted_resource = DeletedMessageResource

relations = {
'attachments': lambda message: (message.attachments, attachment.FileAttachmentResource), # TODO embedded
'attachments': lambda message: (message.attachments(embedded=True), attachment.FileAttachmentResource), # TODO embedded
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All attachments are embedded?

}

# GET
Expand Down Expand Up @@ -176,6 +178,49 @@ def on_get_messages_by_folderid(self, req, resp, folderid):
data = self.folder_gen(req, data)
self.respond(req, resp, data, MessageResource.fields)

def on_get_embedded_folder_value(self, req, resp, folderid=None, itemid=None, attachmentid=None):
"""Get a message as RFC-2822 by item and attachment ID including a folderid.

Args:
req (Request): Falcon request object.
resp (Response): Falcon response object.
folderid (str): folder ID. Defaults to None.
itemid (str): message ID. Defaults to None. itemid value is mandatory.
attachmentid (str): message ID. Defaults to None. attachmentid value is mandatory.

Raises:
HTTPNotFound: when itemid or attachmentid is None.

Note:
Based on MS Explorer result, it never validate folderid. So, we ignore it.
"""
if itemid is None or attachmentid is None:
raise HTTPNotFound()
self.on_get_embedded_value(self, req, resp, itemid, attachmentid)

def on_get_embedded_value(self, req, resp, itemid=None, attachmentid=None):
"""Get a message as RFC-2822 by item and attachment ID.

Args:
req (Request): Falcon request object.
resp (Response): Falcon response object.
itemid (str): message ID. Defaults to None. itemid value is mandatory.
attachmentid (str): message ID. Defaults to None. attachmentid value is mandatory.

Raises:
HTTPNotFound: when itemid or attachmentid is None.
"""
if itemid is None or attachmentid is None:
raise HTTPNotFound()
store = req.context.server_store[1]
item = _item(store, itemid)
attachment = item.attachment(attachmentid)
embedded_item = attachment.item
# Super ugly hack to get data of embedded items and make them available (not available via kopano-python)
sopt = inetmapi.sending_options()
resp.body = inetmapi.IMToINet(store.server.mapisession, None, embedded_item.mapiobj, sopt).decode()
Comment thread
mortymacs marked this conversation as resolved.
resp.content_type = "text/plain"

def on_get_value(self, req, resp, folderid=None, itemid=None):
"""Get a message as RFC-2822 by folder ID.

Expand Down