fix: properly unpack _execute() tuple in drafts.create() and drafts.update() for multipart requests#459
Conversation
…pdate() for multipart requests When creating or updating a draft with attachments >= 3MB (multipart/form-data), _http_client._execute() returns a (json_response, headers) tuple. The multipart code paths in create() and update() assigned the entire tuple to a single variable instead of unpacking it, causing TypeError. Applied the same tuple-unpacking pattern already used in send() to both create() and update() multipart code paths. Fixes nylas#454
|
@pengfeiye @benjaminwhtan @AaronDDM |
There was a problem hiding this comment.
@dudegladiator sorry for the delay and thank you for your contribution!
The fix is correct — _execute() returns (json_response, headers) and drafts.create()/drafts.update() were treating the tuple as a dict. Aligning these two paths with the existing send() pattern is the right call, and forwarding headers to Response.from_dict matches that pattern.
Two things missing before this can merge:
-
No CHANGELOG entry. The repo has an
Unreleasedsection at the top ofCHANGELOG.md— please add a bullet there, e.g.:* Fixed TypeError in `drafts.create()` and `drafts.update()` when attachments trigger the multipart code path (>3MB) -
No tests committed. The PR body describes 8 unit tests and 2 integration tests, but
changedFiles: 1and the diff only touchesnylas/resources/drafts.py. The existingtest_create_draft_with_special_characters_large_attachmentintests/resources/test_drafts.pymocks_executein a way that didn't catch this regression — it only asserts on call args, not on processing of the return value. Please commit at least one regression test where the mocked_executereturns a realistic(dict, headers)tuple and asserts the call returns aResponse[Draft]without raising. Same forupdate().
Once those two are in I'm happy to approve.
|
@AaronDDM thanks for the review — both points addressed in the latest commit:
PR description updated to reflect what's actually in the diff. Ready for another look. |
Fixes #454
Summary
When creating or updating a draft with attachments larger than 3MB (which triggers
multipart/form-dataupload), the SDK throws aTypeError: tuple indices must be integers or slices, not str.Root Cause
_http_client._execute()returns a tuple(json_response, headers)(via_validate_responseinhttp_client.pyline 48). However, the multipart code paths indrafts.create()anddrafts.update()assigned the entire tuple to a single variable instead of unpacking it:The
send()method already did this correctly:Fix
Applied the same tuple-unpacking pattern to both
create()andupdate()multipart code paths innylas/resources/drafts.py:create():update():Changes
nylas/resources/drafts.py— 4 lines changed (2 increate(), 2 inupdate())CHANGELOG.md— entry added underUnreleasedtests/resources/test_drafts.py— 2 regression tests added (see below)Testing
Regression tests (added in this PR)
Two new tests in
tests/resources/test_drafts.pyexercise the realResponse.from_dictcode path (no patch onResponse) with_executereturning a realistic(dict, headers)tuple, then assert the call returns aResponse[Draft]with the expecteddata,request_id, andheaders:test_create_draft_large_attachment_unpacks_execute_tupletest_update_draft_large_attachment_unpacks_execute_tupleI verified that reverting the fix in
nylas/resources/drafts.pycauses both new tests to fail with the exactTypeError: tuple indices must be integers or slices, not strfrom issue #454, confirming they regression-cover the bug. Reapplying the fix → both pass.The pre-existing
test_create_draft_with_special_characters_large_attachmentdid not catch this bug because it patchedResponse.from_dictand only asserted on_executecall args, not on processing of the return value.Full suite
Manual verification against Nylas sandbox
Earlier verified end-to-end against a real Microsoft (Outlook) grant by creating and sending a draft with a real 7MB PDF attachment via
drafts.create()(and via the create → send flow). Both succeeded withoutTypeError, confirming the fix works on the wire.I confirm that this contribution is made under the terms of the MIT license and that I have the authority necessary to make this contribution on behalf of its copyright owner.