-
Notifications
You must be signed in to change notification settings - Fork 5
Add support for embedded attachments #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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', | ||
| attachments, suffix="embedded_item_by_id") | ||
| self.add_route(user + '/messages/{itemid}/attachments/{attachmentid}/item/$value', | ||
| messages, suffix="embedded_value") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| 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") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, the same for here. Something like |
||
| self.add_route(user + '/mailFolders/{folderid}/messages/{itemid}/attachments/{attachmentid}/$value', | ||
| attachments, suffix="binary_in_folder_by_id") | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
As it doesn't have any default value, it's not needed to mention it. Because Python will raise an error anyway. |
||||||
| """ | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure this validation is needed? Because |
||||||
|
|
||||||
| response = partial(self.respond, req, resp) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a suggestion: you can call |
||||||
| 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. | ||||||
|
|
||||||
|
|
@@ -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): | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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. | ||||||
|
|
||||||
|
|
@@ -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. | ||||||
|
|
@@ -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: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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) | ||||||
|
|
@@ -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), | ||||||
| } | ||||||
| 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 | ||
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All attachments are embedded? |
||
| } | ||
|
|
||
| # GET | ||
|
|
@@ -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() | ||
|
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. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
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 (
../itemor../item/..) under the "Attachments" section in the Microsoft Graph. Would you please share the Microsoft Graph page you've followed for these endpoints?