Skip to content

Commit

Permalink
Fix IMAP identifiers not encoding correctly (#383)
Browse files Browse the repository at this point in the history
IMAP identifiers sometimes have characters that do not work well in the URL, including slashes, that would make the request fail. For these endpoints we ensure that we are encoding these IDs properly.
  • Loading branch information
mrashed-dev authored Sep 10, 2024
1 parent 6955356 commit 9c8cead
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
nylas-python Changelog
======================

Unreleased
----------------
* Fix IMAP identifiers not encoding correctly

v6.3.1
----------------
* Fix typo on Clean Messages
Expand Down
9 changes: 5 additions & 4 deletions nylas/resources/drafts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import io
import urllib.parse
from typing import Optional

from nylas.config import RequestOverrides
Expand Down Expand Up @@ -79,7 +80,7 @@ def find(
The requested Draft.
"""
return super().find(
path=f"/v3/grants/{identifier}/drafts/{draft_id}",
path=f"/v3/grants/{identifier}/drafts/{urllib.parse.quote(draft_id, safe='')}",
response_type=Draft,
overrides=overrides,
)
Expand Down Expand Up @@ -149,7 +150,7 @@ def update(
Returns:
The updated Draft.
"""
path = f"/v3/grants/{identifier}/drafts/{draft_id}"
path = f"/v3/grants/{identifier}/drafts/{urllib.parse.quote(draft_id, safe='')}"

# Use form data only if the attachment size is greater than 3mb
attachment_size = sum(
Expand Down Expand Up @@ -196,7 +197,7 @@ def destroy(
The deletion response.
"""
return super().destroy(
path=f"/v3/grants/{identifier}/drafts/{draft_id}",
path=f"/v3/grants/{identifier}/drafts/{urllib.parse.quote(draft_id, safe='')}",
overrides=overrides,
)

Expand All @@ -216,7 +217,7 @@ def send(
"""
json_response = self._http_client._execute(
method="POST",
path=f"/v3/grants/{identifier}/drafts/{draft_id}",
path=f"/v3/grants/{identifier}/drafts/{urllib.parse.quote(draft_id, safe='')}",
overrides=overrides,
)

Expand Down
8 changes: 5 additions & 3 deletions nylas/resources/messages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import io
import urllib.parse
from typing import Optional, List

from nylas.config import RequestOverrides
Expand Down Expand Up @@ -96,7 +97,7 @@ def find(
The requested Message.
"""
return super().find(
path=f"/v3/grants/{identifier}/messages/{message_id}",
path=f"/v3/grants/{identifier}/messages/{urllib.parse.quote(message_id, safe='')}",
response_type=Message,
query_params=query_params,
overrides=overrides,
Expand All @@ -122,7 +123,7 @@ def update(
The updated Message.
"""
return super().update(
path=f"/v3/grants/{identifier}/messages/{message_id}",
path=f"/v3/grants/{identifier}/messages/{urllib.parse.quote(message_id, safe='')}",
response_type=Message,
request_body=request_body,
overrides=overrides,
Expand All @@ -143,7 +144,8 @@ def destroy(
The deletion response.
"""
return super().destroy(
path=f"/v3/grants/{identifier}/messages/{message_id}", overrides=overrides
path=f"/v3/grants/{identifier}/messages/{urllib.parse.quote(message_id, safe='')}",
overrides=overrides,
)

def send(
Expand Down
8 changes: 5 additions & 3 deletions nylas/resources/threads.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import urllib.parse
from nylas.config import RequestOverrides
from nylas.handler.api_resources import (
ListableApiResource,
Expand Down Expand Up @@ -60,7 +61,7 @@ def find(
The requested Thread.
"""
return super().find(
path=f"/v3/grants/{identifier}/threads/{thread_id}",
path=f"/v3/grants/{identifier}/threads/{urllib.parse.quote(thread_id, safe='')}",
response_type=Thread,
overrides=overrides,
)
Expand All @@ -85,7 +86,7 @@ def update(
The updated Thread.
"""
return super().update(
path=f"/v3/grants/{identifier}/threads/{thread_id}",
path=f"/v3/grants/{identifier}/threads/{urllib.parse.quote(thread_id, safe='')}",
response_type=Thread,
request_body=request_body,
overrides=overrides,
Expand All @@ -106,5 +107,6 @@ def destroy(
The deletion response.
"""
return super().destroy(
path=f"/v3/grants/{identifier}/threads/{thread_id}", overrides=overrides
path=f"/v3/grants/{identifier}/threads/{urllib.parse.quote(thread_id, safe='')}",
overrides=overrides,
)
72 changes: 72 additions & 0 deletions tests/resources/test_drafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ def test_find_draft(self, http_client_response):
overrides=None,
)

def test_find_draft_encoded_id(self, http_client_response):
drafts = Drafts(http_client_response)

drafts.find(
identifier="abc-123",
draft_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_response._execute.assert_called_once_with(
"GET",
"/v3/grants/abc-123/drafts/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

def test_create_draft(self, http_client_response):
drafts = Drafts(http_client_response)
request_body = {
Expand Down Expand Up @@ -206,6 +223,30 @@ def test_update_draft(self, http_client_response):
overrides=None,
)

def test_update_draft_encoded_id(self, http_client_response):
drafts = Drafts(http_client_response)
request_body = {
"subject": "Hello from Nylas!",
"to": [{"name": "Jon Snow", "email": "[email protected]"}],
"cc": [{"name": "Arya Stark", "email": "[email protected]"}],
"body": "This is the body of my draft message.",
}

drafts.update(
identifier="abc-123",
draft_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
request_body=request_body,
)

http_client_response._execute.assert_called_once_with(
"PUT",
"/v3/grants/abc-123/drafts/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
request_body,
overrides=None,
)

def test_update_draft_small_attachment(self, http_client_response):
drafts = Drafts(http_client_response)
request_body = {
Expand Down Expand Up @@ -282,6 +323,23 @@ def test_destroy_draft(self, http_client_delete_response):
overrides=None,
)

def test_destroy_draft_encoded_id(self, http_client_delete_response):
drafts = Drafts(http_client_delete_response)

drafts.destroy(
identifier="abc-123",
draft_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_delete_response._execute.assert_called_once_with(
"DELETE",
"/v3/grants/abc-123/drafts/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

def test_send_draft(self, http_client_response):
drafts = Drafts(http_client_response)

Expand All @@ -290,3 +348,17 @@ def test_send_draft(self, http_client_response):
http_client_response._execute.assert_called_once_with(
method="POST", path="/v3/grants/abc-123/drafts/draft-123", overrides=None
)

def test_send_draft_encoded_id(self, http_client_response):
drafts = Drafts(http_client_response)

drafts.send(
identifier="abc-123",
draft_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_response._execute.assert_called_once_with(
method="POST",
path="/v3/grants/abc-123/drafts/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
overrides=None,
)
58 changes: 58 additions & 0 deletions tests/resources/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ def test_find_message(self, http_client_response):
overrides=None,
)

def test_find_message_encoded_id(self, http_client_response):
messages = Messages(http_client_response)

messages.find(
identifier="abc-123",
message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_response._execute.assert_called_once_with(
"GET",
"/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

def test_find_message_with_query_params(self, http_client_response):
messages = Messages(http_client_response)

Expand Down Expand Up @@ -151,6 +168,30 @@ def test_update_message(self, http_client_response):
overrides=None,
)

def test_update_message_encoded_id(self, http_client_response):
messages = Messages(http_client_response)
request_body = {
"starred": True,
"unread": False,
"folders": ["folder-123"],
"metadata": {"foo": "bar"},
}

messages.update(
identifier="abc-123",
message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
request_body=request_body,
)

http_client_response._execute.assert_called_once_with(
"PUT",
"/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
request_body,
overrides=None,
)

def test_destroy_message(self, http_client_delete_response):
messages = Messages(http_client_delete_response)

Expand All @@ -165,6 +206,23 @@ def test_destroy_message(self, http_client_delete_response):
overrides=None,
)

def test_destroy_message_encoded_id(self, http_client_delete_response):
messages = Messages(http_client_delete_response)

messages.destroy(
identifier="abc-123",
message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_delete_response._execute.assert_called_once_with(
"DELETE",
"/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

def test_send_message(self, http_client_response):
messages = Messages(http_client_response)
request_body = {
Expand Down
57 changes: 57 additions & 0 deletions tests/resources/test_threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ def test_find_thread(self, http_client_response):
overrides=None,
)

def test_find_thread_encoded_id(self, http_client_response):
threads = Threads(http_client_response)

threads.find(
identifier="abc-123",
thread_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_response._execute.assert_called_once_with(
"GET",
"/v3/grants/abc-123/threads/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

def test_update_thread(self, http_client_response):
threads = Threads(http_client_response)
request_body = {
Expand All @@ -182,6 +199,29 @@ def test_update_thread(self, http_client_response):
overrides=None,
)

def test_update_thread_encoded_id(self, http_client_response):
threads = Threads(http_client_response)
request_body = {
"starred": True,
"unread": False,
"folders": ["folder-123"],
}

threads.update(
identifier="abc-123",
thread_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
request_body=request_body,
)

http_client_response._execute.assert_called_once_with(
"PUT",
"/v3/grants/abc-123/threads/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
request_body,
overrides=None,
)

def test_destroy_thread(self, http_client_delete_response):
threads = Threads(http_client_delete_response)

Expand All @@ -195,3 +235,20 @@ def test_destroy_thread(self, http_client_delete_response):
None,
overrides=None,
)

def test_destroy_thread_encode_id(self, http_client_delete_response):
threads = Threads(http_client_delete_response)

threads.destroy(
identifier="abc-123",
thread_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/[email protected]>",
)

http_client_delete_response._execute.assert_called_once_with(
"DELETE",
"/v3/grants/abc-123/threads/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
None,
None,
None,
overrides=None,
)

0 comments on commit 9c8cead

Please sign in to comment.