diff --git a/postmark/core.py b/postmark/core.py index fce4f5c..f7167a1 100644 --- a/postmark/core.py +++ b/postmark/core.py @@ -504,7 +504,7 @@ def to_json_message(self): return json_message - def send(self, test=None): + def send(self, test=None, return_json=None): ''' Send the email through the Postmark system. Pass test=True to just print out the resulting @@ -537,6 +537,21 @@ def send(self, test=None): else: endpoint_url = __POSTMARK_URL__ + 'email' + """ + Args: + return_json (bool | None): + True -> return parsed JSON + False -> return True/False + None -> fallback to settings.POSTMARK_RETURN_JSON (default False) + """ + + if return_json is None: + try: + from django.conf import settings as django_settings + return_json = getattr(django_settings, "POSTMARK_RETURN_JSON", False) + except ImportError: + return_json = False + # Set up the url Request req = Request( endpoint_url, @@ -556,8 +571,9 @@ def send(self, test=None): jsontxt = result.read().decode('utf8') result.close() if result.code == 200: - self.message_id = json.loads(jsontxt).get('MessageID', None) - return True + parsed = json.loads(jsontxt) + self.message_id = parsed.get("MessageID", None) + return parsed if return_json else True else: raise PMMailSendException('Return code %d: %s' % (result.code, result.msg)) except HTTPError as err: @@ -676,7 +692,7 @@ def _check_values(self): message._check_values() - def send(self, test=None): + def send(self, test=None, return_json=None): # Has one of the messages caused an inactive recipient error? inactive_recipient = False @@ -691,6 +707,21 @@ def send(self, test=None): except ImportError: pass + """ + Args: + return_json (bool | None): + True -> return parsed JSON + False -> return True/False + None -> fallback to settings.POSTMARK_RETURN_JSON (default False) + """ + + if return_json is None: + try: + from django.conf import settings as django_settings + return_json = getattr(django_settings, "POSTMARK_RETURN_JSON", False) + except ImportError: + return_json = False + # Split up into groups of 500 messages for sending for messages in _chunks(self.messages, PMBatchMail.MAX_MESSAGES): json_message = [] @@ -729,6 +760,7 @@ def send(self, test=None): results = json.loads(jsontxt) for i, res in enumerate(results): self.__messages[i].message_id = res.get("MessageID", None) + return results if return_json else True else: raise PMMailSendException('Return code %d: %s' % (result.code, result.msg)) except HTTPError as err: diff --git a/tests.py b/tests.py index e97ddf3..6498a46 100644 --- a/tests.py +++ b/tests.py @@ -20,6 +20,8 @@ import mock +from unittest.mock import MagicMock + from postmark import ( PMBatchMail, PMMail, PMMailInactiveRecipientException, PMMailUnprocessableEntityException, PMMailServerErrorException, @@ -29,6 +31,15 @@ from django.conf import settings +def make_fake_response(payload, code=200): + """Helper to fake an HTTP response object.""" + mock = MagicMock() + mock.code = code + mock.read.return_value = json.dumps(payload).encode("utf-8") + mock.close.return_value = None + return mock + + class PMMailTests(unittest.TestCase): def test_406_error_inactive_recipient(self): json_payload = BytesIO() @@ -181,6 +192,37 @@ def test_send_metadata_invalid_format(self): self.assertRaises(TypeError, PMMail, api_key='test', sender='from@example.com', to='to@example.com', subject='test', text_body='test', metadata={'test': {}}) + def test_mail_returns_result(self): + fake_payload = { + "To": "receiver@example.com", + "SubmittedAt": "2025-09-18T10:00:00Z", + "MessageID": "abc-123", + "ErrorCode": 0, + "Message": "OK" + } + + mail = PMMail( + api_key="test-api-key", + sender="sender@example.com", + to="receiver@example.com", + subject="Hello", + text_body="Testing single mail return", + ) + + with mock.patch("postmark.core.urlopen") as mock_urlopen: + mock_urlopen.return_value = make_fake_response(fake_payload) + + # Test boolean return + result = mail.send() + self.assertTrue(result) + + # Test JSON return explicitly + result_json = mail.send(return_json=True) + self.assertIsInstance(result_json, dict) + self.assertEqual(result_json["ErrorCode"], 0) + self.assertEqual(result_json["Message"], "OK") + + class PMBatchMailTests(unittest.TestCase): def test_406_error_inactive_recipient(self): @@ -245,6 +287,40 @@ def test_500_error_server_error(self): 500, '', {}, None)): self.assertRaises(PMMailServerErrorException, batch.send) + def test_batch_mail_returns_results(self): + fake_payload = [ + { + "To": "receiver@example.com", + "SubmittedAt": "2025-09-18T10:00:00Z", + "MessageID": "abc-123", + "ErrorCode": 0, + "Message": "OK", + } + ] + + message = PMMail( + api_key="test-api-key", + sender="sender@example.com", + to="receiver@example.com", + subject="Hello", + text_body="Testing batch return", + ) + # pass list of PMMail objects to PMBatchMail + batch = PMBatchMail(api_key="test-api-key", messages=[message]) + + with mock.patch("postmark.core.urlopen") as mock_urlopen: + mock_urlopen.return_value = make_fake_response(fake_payload) + + # Test boolean return + results = batch.send() + self.assertTrue(results) + + # Test JSON return explicitly + results_json = batch.send(return_json=True) + self.assertIsInstance(results_json, list) + self.assertEqual(results_json[0]["ErrorCode"], 0) + + class PMBounceManagerTests(unittest.TestCase): def test_activate(self):