Email Sender

How To Run

Setup

Set the API keys in application-sendgrid.properties and application-snailgun.properties I didn't want to put these into source control but also did not have time to setup a proper secrets manager. I discuss this further in the Future Improvements section.

Run directly

Run this command in the root project directory
./mvnw spring-boot:run

Run in Docker

Run the following commands in the root project directory
./mvnw clean install
docker build -t emailsender .
docker run -p 8080:8080 emailsender:latest

API Documentation

Autogenerated API documentation is available when the service is running at http://localhost:8080/swagger-ui.html#/email-controller

Changing the email service

To change between snailgun and sendgrid change the spring.profiles.active value in applicaiton.properties to either sendgrid or snailgun

Languages, Frameworks, and Libraries

Languages

I chose to write this in Java because it's the language I have the most familiarity with and it has good frameworks for quickly making REST services. creating REST services;

Frameworks

I build this service in Springboot as that is the industry standard for Java REST services and a framework I have a great deal of familiarity with.

Libraries

Apache Commons Validator

I used this library for Email Addres Validation. The reason I decided to use a library rather than do it myself is that some cursory online research revealed that there are a ton of edge cases. I felt that this was a situation where it was better to not reinvent the wheel. I chose apache commons validator due to ease of use, but it is not completely comprehensive. I discuss some options for more comprehensive validators in the Future Improvements sections
Some references on email validation
https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression

Jsoup

I used Jsoup to help convert HTML to plaintext. I felt that, as with email address validation, There were a lot of edge cases and this is a common problem that existing libraries have already solved.

Gson

I used the Gson library for JSON parsing. Mostly in tests but also to map snailguns snake case parameters to camel case

Swagger

I used this as a quick easy way to create API documentation.

Other Technologies

Maven

I used Maven as a build tool primarily due to familiarity. I could have also chosen to use Gradle and the differences between the two on a project like this are fairly minimal

Docker

I created a Dockerfile because it is industry standard and I knew it could add it very quickly

Other decisions

EmailController

This is a pretty thin passthrough. It does a little bit of validation and then routes requests to the EmailService

Email

I created this to be a message class separate from individual messages based on Sendgrid or Snailgun

Validation

I decided to make the validation a separate method within Email rather than something that is enforced on creation because Springboot autowiring doesn't always call setter methods. So the only enforced option I was left with was to put it in the constructor but that would mean it was not possible to create a no args constructor and made testing much more difficult. So ultimately I decided that correctness should be enforced when the message was actually sent. This has the advantage of making it easier to create multiple different validations for different services.

HTML to plaintext

Because of the lack of guarantee that setters would be called discussed above, the HTML to plaintext transformation was also done at this time.

Requests

These mostly just map to what the APIs are expecting. The one notable thing here is a created an empty interface so I could use a parameterized method that could take either kind of request.

Services

I created an abstract email service that has all the common code

Send email

The actual sending is done by the abstract class and the child services just prepare the request

Get Status

Snailgun returns the actual status while sendgrid throws an exception letting the user know that the endpoint is not currently usable.

Future Improvements

Secrets Manager

This is the biggest area of improvement in my opinion. Currently, API keys must be put in manually but there are technologies that exist that could help. Two that I would look into are Spring Vault and Kubernetes secrets.

More Comprehensive Email Validation

This would require more research but there are more comprehensive validators than the one I used. The downside is a potentially cost in speed. This would have to be balanced.

500 errors on unknown ids

This is actually an improvement to the email API itself but I noticed it responds to unknown email ids with a 500 error. I think this should be replaced with a friendlier error that leaks less technical information.

Kubernetes

It would also be nice to implement Kubernetes to make deployment easier and increase reliablility One area of future exploration would be looking at the trade-offs of a more comprehensive validation. Some potential options are email-rfc2822-validator and EmailValidator4J