Giter VIP home page Giter VIP logo

postmarker's Introduction

Hi, I am Dmitry ๐Ÿ‘‹

Software Engineer with more than 12 years of experience specializing in Rust and Python with a focus on writing parsers and fuzzing.

  • ๐ŸŒ Based in Prague, Czech Republic ๐Ÿ‡จ๐Ÿ‡ฟ
  • ๐Ÿ’ก Interested in software testing & building reliable systems
  • ๐ŸŽ“ Studied information security
  • ๐Ÿšฒ Love traveling
  • ๐Ÿ‘‹ Reach me on LinkedIn, Twitter or Telegram

postmarker's People

Contributors

avityk avatar cmabastar avatar colinhoernig avatar gregsadetsky avatar joveice avatar micthiesen avatar pyup-bot avatar robshep avatar sileht avatar stranger6667 avatar suriya avatar z33dd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

postmarker's Issues

Refactor clients

It will be better to use something simple instead of 2 client classes (and another one for status api)

E.g.

from postmarker.core import PostmarkClient

postmark = PostmarkClient(token='API_TOKEN')
postmark.emails.send(...)
postmark.bounces.all()
postmark.status
postmark.last_incident

Server pools

It could be useful feature:

>>> server_pool = ServerPool(tokens=['token1', 'token2'])
>>> server_pool.bounces.all()
[<Bounce: 1>, <Bounce: 2>]

It allows users to work with multiple server clients in the same way as with single client

Unable to handle Unicode in message body using API

Sorry to be leaving you so many issues!

Looks like there's now an issue when sending messages at least with unicode characters in the body. The message I am sending usings django.template.Template to render a template to a string and then submit it as the message HTML body.

This problem goes away if you temporarily set sys.setdefaultencoding('utf8') (which you obviously shouldn't be doing).

Here is the traceback:

/Users/../utils/emails.pyc in send_html_email(subject, from_email, to_emails, reply_to, text_body, html_body, headers, tag, metadata, subaccount, attachments, send_in, extra_params, bcc)
    128             setattr(msg, key, value)
    129
--> 130     msg.send()
    131
    132

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/django/core/mail/message.pyc in send(self, fail_silently)
    340             # send to.
    341             return 0
--> 342         return self.get_connection(fail_silently).send_messages([self])
    343
    344     def attach(self, filename=None, content=None, mimetype=None):

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/django.pyc in send_messages(self, email_messages)
     55             client_created = self.open()
     56             prepared_messages = [self.prepare_message(message) for message in email_messages]
---> 57             response = self.client.emails.send_batch(*prepared_messages, TrackOpens=self.get_option('TRACK_OPENS'))
     58             msg_count = len(response)
     59             if client_created:

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send_batch(self, *emails, **extra)
    322         :param extra: dictionary with extra arguments for every message in the batch.
    323         """
--> 324         return self.EmailBatch(*emails).send(**extra)
    325
    326     # NOTE. The following methods are included here to expose better interface without need to import relevant classes.

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send(self, **extra)
    242         :rtype: `list`
    243         """
--> 244         emails = self.as_dict(**extra)
    245         responses = [self._manager._send_batch(*batch) for batch in chunks(emails, self.MAX_SIZE)]
    246         return sum(responses, [])

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in as_dict(self, **extra)
    220         :return: List of dictionaries.
    221         """
--> 222         return [self._construct_email(email, **extra) for email in self.emails]
    223
    224     def _construct_email(self, email, **extra):

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in _construct_email(self, email, **extra)
    229             email = Email(manager=self._manager, **email)
    230         elif isinstance(email, (MIMEText, MIMEMultipart)):
--> 231             email = Email.from_mime(email, self._manager)
    232         elif not isinstance(email, Email):
    233             raise ValueError

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in from_mime(cls, message, manager)
    178         """
    179         if isinstance(message, MIMEMultipart):
--> 180             text, html, attachments = deconstruct_multipart(message)
    181         else:
    182             text, html, attachments = message.get_payload(decode=True).decode(), None, []

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in deconstruct_multipart(message)
     85             text = part.get_payload(decode=True).decode()
     86         elif content_type == 'text/html':
---> 87             html = part.get_payload(decode=True).decode()
     88         elif content_type != 'multipart/alternative':
     89             attachments.append(part)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 3495: ordinal not in range(128)

I'm currently getting around this with the following context manager, but obviously this cannot be the case long-term:

@contextlib.contextmanager
def set_default_encoding(encoding='ascii'):
    """\
    Temporarily set the default encoding within the span of the context.

    NOTE: Using `sys.setdefaultencoding()` is HIGHLY DISCOURAGED and this context
    manager should only be used when working with third party libraries that incorrectly
    handle unicode/strings/bytestrings and cannot easily be patched, updated, or worked around.
    """
    import sys
    reload(sys)
    original_encoding = sys.getdefaultencoding()
    sys.setdefaultencoding(encoding)
    yield
    sys.setdefaultencoding(original_encoding)

Library incorrectly quotes long lines when sent via API

If large HTML files (particularly those with large lines, for example anything produced using MJML with the --min minification flag enabled) are sent using Postmarker, the HTML is escaped using quoted-printable and sent to Postmark's API that way, when it should actually be sent unquoted.

The reason this happens is under the hood, Postmarker calls the Django email backend's message.message() within EmailBackend.prepare_message(). This has the message turn itself into MIME, which then is later deconstructed back to a dict using Email.from_mime(). Since Django thinks it's serializing the email for an SMTP send, it automatically quotes the email or alternative attachment under certain circumstances (utf-8 + long lines, see django.core.mail.message.py:220) and sets the Content-Transfer-Encoding to quoted-printable. When Postmarker turns that back into a dict, it loses the Content-Transfer-Encoding header and does not unquote, leading to the quoted version being sent directly via Postmark's API and completely incorrect emails being sent, particularly if they include any Outlook-specific markup that uses XML namespaces and colons.

One way to fix this is when converting the MIME email back to a dictionary, to detect if it has been quoted and then to unquote using email.quoprimime.body_decode(...). Alternatively, the better way to fix this is to refactor simply not to let Django's mailing backend create the MIME email at all, and simply send the body and headers to Postmark via the API without doing any unnecessary processing. This is how djrill for Mandrill works, and emails I sent with Postmarker that got mangled were not mangled with djrill.

Send Tag information via the Django interface

I can't figure out a way to send Tag information to Postmark when using the Django EmailBackend. I did dive deep into the code but unfortunately had difficulty figuring out where the right place even to patch it would be.

I know I could use the regular PostmarkClient, but it's important for us to continue using the built-in Django mechanism so that we can stay vendor agnostic.

Can you point me in the right direction on where a patch would go?

Bounce manager

This feature allows users to access to Bounce API in this way:

server_client.bounces.deliverystats

or

bounce = server_client.bounces.get(1)  # Get bounce with ID=1

Extension points

E.g. signals for some events like email sending. It could be useful

Bounce model

Instance returned by Bounce manager:

>>> bounce = server_client.bounces.get(1)

Should provide the following:

>>> bounce.dump  # /bounces/{bounceid}/dump
>>> bounce.activate()  # /bounces/{bounceid}/activate

along with basic fields like Type, TypeCode, etc.

The methods above should also be available for Bounce manager:

>>> server_client.bounces.activate(1)
>>> server_client.bounces.get_dump(1)

Testing feature

Another useful feature - use Postmark test API key - POSTMARK_API_TEST.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.