SendGridV4Sender.java

1
package fr.sii.ogham.email.sendgrid.v4.sender.impl;
2
3
import static fr.sii.ogham.core.util.LogUtils.logString;
4
import static fr.sii.ogham.email.sendgrid.SendGridConstants.DEFAULT_SENDGRID_IMPLEMENTATION_PRIORITY;
5
import static fr.sii.ogham.email.sendgrid.sender.EmailValidator.validate;
6
7
import java.io.ByteArrayInputStream;
8
import java.io.IOException;
9
import java.util.regex.Pattern;
10
11
import org.slf4j.Logger;
12
import org.slf4j.LoggerFactory;
13
14
import fr.sii.ogham.core.builder.priority.Priority;
15
import fr.sii.ogham.core.exception.MessageException;
16
import fr.sii.ogham.core.exception.mimetype.MimeTypeDetectionException;
17
import fr.sii.ogham.core.mimetype.MimeTypeProvider;
18
import fr.sii.ogham.core.sender.AbstractSpecializedSender;
19
import fr.sii.ogham.core.util.Base64Utils;
20
import fr.sii.ogham.core.util.IOUtils;
21
import fr.sii.ogham.email.attachment.Attachment;
22
import fr.sii.ogham.email.exception.handler.ContentHandlerException;
23
import fr.sii.ogham.email.message.Email;
24
import fr.sii.ogham.email.message.EmailAddress;
25
import fr.sii.ogham.email.message.Recipient;
26
import fr.sii.ogham.email.sendgrid.sender.SendGridSender;
27
import fr.sii.ogham.email.sendgrid.sender.exception.AttachmentReadException;
28
import fr.sii.ogham.email.sendgrid.sender.exception.SendGridException;
29
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.client.SendGridClient;
30
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.client.SendGridInterceptor;
31
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.compat.AttachmentsCompat;
32
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.compat.CompatFactory;
33
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.compat.CompatUtil;
34
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.compat.MailCompat;
35
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.compat.PersonalizationCompat;
36
import fr.sii.ogham.email.sendgrid.v4.sender.impl.sendgrid.handler.SendGridContentHandler;
37
38
/**
39
 * SendGrid-backed implementation of the email sender.
40
 */
41
@Priority(properties = "${ogham.email.implementation-priority.sendgrid}", defaultValue = DEFAULT_SENDGRID_IMPLEMENTATION_PRIORITY)
42
public final class SendGridV4Sender extends AbstractSpecializedSender<Email> implements SendGridSender {
43
	private static final Logger LOG = LoggerFactory.getLogger(SendGridV4Sender.class);
44
	private static final Pattern CID = Pattern.compile("^<(.+)>$");
45
46
	private final SendGridClient delegate;
47
	private final SendGridContentHandler handler;
48
	private final MimeTypeProvider mimetypeProvider;
49
	private final CompatFactory objectsFactory;
50
	private final SendGridInterceptor interceptor;
51
52
	/**
53
	 * Uses the default {@link CompatFactory} ({@link CompatUtil#getDefaultCompatFactory()}).
54
	 * 
55
	 * @param service
56
	 *            the underlying SendGrid service
57
	 * @param handler
58
	 *            the content handler, in change of converting the email content
59
	 *            into something the {@link SendGridClient} can work with
60
	 * @param mimetypeProvider
61
	 *            determines mimetype for attachments
62
	 */
63
	public SendGridV4Sender(final SendGridClient service, final SendGridContentHandler handler, MimeTypeProvider mimetypeProvider) {
64
		this(service, handler, mimetypeProvider, CompatUtil.getDefaultCompatFactory(), null);
65
	}
66
67
	/**
68
	 * Constructor.
69
	 * 
70
	 * @param service
71
	 *            the underlying SendGrid service
72
	 * @param handler
73
	 *            the content handler, in change of converting the email content
74
	 *            into something the {@link SendGridClient} can work with
75
	 * @param mimetypeProvider
76
	 *            determines mimetype for attachments
77
	 * @param objectsFactory
78
	 *            factory that creates instances of {@code sendgrid-java}
79
	 *            objects. This is needed due to issue in package naming with
80
	 *            {@code sendgrid-java} 4.3.0
81
	 */
82
	public SendGridV4Sender(final SendGridClient service, final SendGridContentHandler handler, MimeTypeProvider mimetypeProvider, CompatFactory objectsFactory) {
83
		this(service, handler, mimetypeProvider, objectsFactory, null);
84
	}
85
86
	/**
87
	 * Constructor.
88
	 * 
89
	 * @param service
90
	 *            the underlying SendGrid service
91
	 * @param handler
92
	 *            the content handler, in change of converting the email content
93
	 *            into something the {@link SendGridClient} can work with
94
	 * @param mimetypeProvider
95
	 *            determines mimetype for attachments
96
	 * @param interceptor
97
	 *            an extension point for customizing the email to send
98
	 * @param objectsFactory
99
	 *            factory that creates instances of {@code sendgrid-java}
100
	 *            objects. This is needed due to issue in package naming with
101
	 *            {@code sendgrid-java} 4.3.0
102
	 */
103
	public SendGridV4Sender(final SendGridClient service, final SendGridContentHandler handler, MimeTypeProvider mimetypeProvider, CompatFactory objectsFactory, SendGridInterceptor interceptor) {
104 1 1. <init> : negated conditional → RUN_ERROR
		if (service == null) {
105
			throw new IllegalArgumentException("[service] cannot be null");
106
		}
107 1 1. <init> : negated conditional → RUN_ERROR
		if (handler == null) {
108
			throw new IllegalArgumentException("[handler] cannot be null");
109
		}
110 1 1. <init> : negated conditional → RUN_ERROR
		if (mimetypeProvider == null) {
111
			throw new IllegalArgumentException("[mimetypeProvider] cannot be null");
112
		}
113
114
		this.delegate = service;
115
		this.handler = handler;
116
		this.mimetypeProvider = mimetypeProvider;
117
		this.objectsFactory = objectsFactory;
118
		this.interceptor = interceptor;
119
	}
120
121
	@Override
122
	public void send(final Email message) throws MessageException {
123 1 1. send : negated conditional → RUN_ERROR
		if (message == null) {
124
			throw new IllegalArgumentException("[message] cannot be null");
125
		}
126 1 1. send : removed call to fr/sii/ogham/email/sendgrid/sender/EmailValidator::validate → RUN_ERROR
		validate(message);
127
128
		try {
129
			LOG.debug("Preparing to send email using SendGrid: {}", message);
130
			final MailCompat sgEmail = intercept(toSendGridEmail(message), message);
131
132
			LOG.debug("Sending email {}", logString(message));
133
			LOG.trace("SendGrid email: {}", sgEmail);
134 1 1. send : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/client/SendGridClient::send → RUN_ERROR
			delegate.send(sgEmail);
135
			LOG.debug("Email has been successfully sent");
136
		} catch (ContentHandlerException e) {
137
			throw new MessageException("A content-related error occurred when trying to build an email", message, e);
138
		} catch (AttachmentReadException e) {
139
			throw new MessageException("Attaching file to email failed when trying to send an email", message, e);
140
		} catch (SendGridException e) {
141
			throw new MessageException("A SendGrid-related error occurred when trying to send an email", message, e);
142
		}
143
	}
144
145
	private MailCompat intercept(MailCompat sendGridEmail, Email source) {
146 1 1. intercept : negated conditional → RUN_ERROR
		if (interceptor == null) {
147 1 1. intercept : replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::intercept → RUN_ERROR
			return sendGridEmail;
148
		}
149 1 1. intercept : replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::intercept → NO_COVERAGE
		return interceptor.intercept(sendGridEmail, source);
150
	}
151
152
	private MailCompat toSendGridEmail(final Email message) throws ContentHandlerException, AttachmentReadException {
153
		final MailCompat sendGridMail = objectsFactory.newMail();
154 1 1. toSendGridEmail : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::setSubject → RUN_ERROR
		sendGridMail.setSubject(message.getSubject());
155
156 1 1. toSendGridEmail : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::setFrom → RUN_ERROR
		sendGridMail.setFrom(message.getFrom().getAddress(), message.getFrom().getPersonal());
157
158 1 1. toSendGridEmail : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::addPersonalization → RUN_ERROR
		sendGridMail.addPersonalization(toPersonalization(message));
159
160 1 1. toSendGridEmail : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/handler/SendGridContentHandler::setContent → RUN_ERROR
		handler.setContent(message, sendGridMail, message.getContent());
161
162
		for (Attachment attachment : message.getAttachments()) {
163 1 1. toSendGridEmail : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::addAttachment → RUN_ERROR
			addAttachment(sendGridMail, attachment);
164
		}
165
166 1 1. toSendGridEmail : replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toSendGridEmail → RUN_ERROR
		return sendGridMail;
167
	}
168
169
	private PersonalizationCompat toPersonalization(final Email message) {
170
		PersonalizationCompat personalization = objectsFactory.newPersonalization();
171
		for (Recipient recipient : message.getRecipients()) {
172 1 1. toPersonalization : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::addRecipient → RUN_ERROR
			addRecipient(personalization, recipient);
173
		}
174 1 1. toPersonalization : replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toPersonalization → RUN_ERROR
		return personalization;
175
	}
176
177
	private static void addRecipient(PersonalizationCompat personalization, Recipient recipient) {
178
		final EmailAddress address = recipient.getAddress();
179
		switch (recipient.getType()) {
180
			case TO:
181 1 1. addRecipient : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addTo → RUN_ERROR
				personalization.addTo(address.getAddress(), address.getPersonal());
182
				break;
183
			case CC:
184 1 1. addRecipient : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addCc → NO_COVERAGE
				personalization.addCc(address.getAddress(), address.getPersonal());
185
				break;
186
			case BCC:
187 1 1. addRecipient : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addBcc → NO_COVERAGE
				personalization.addBcc(address.getAddress(), address.getPersonal());
188
				break;
189
		}
190
	}
191
192
	private void addAttachment(final MailCompat sendGridMail, final Attachment attachment) throws AttachmentReadException {
193
		try {
194
			AttachmentsCompat sendGridAttachment = objectsFactory.newAttachments();
195
			byte[] bytes = IOUtils.toByteArray(attachment.getResource().getInputStream());
196 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setContent → RUN_ERROR
			sendGridAttachment.setContent(Base64Utils.encodeToString(bytes));
197 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setContentId → RUN_ERROR
			sendGridAttachment.setContentId(toCid(attachment.getContentId()));
198 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setDisposition → RUN_ERROR
			sendGridAttachment.setDisposition(attachment.getDisposition());
199 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setFilename → RUN_ERROR
			sendGridAttachment.setFilename(attachment.getResource().getName());
200 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setType → RUN_ERROR
			sendGridAttachment.setType(getMimetype(attachment, bytes));
201 1 1. addAttachment : removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::addAttachments → RUN_ERROR
			sendGridMail.addAttachments(sendGridAttachment);
202
		} catch (IOException e) {
203
			throw new AttachmentReadException("Failed to attach email attachment named " + attachment.getResource().getName(), attachment, e);
204
		} catch (MimeTypeDetectionException e) {
205
			throw new AttachmentReadException("Failed to determine mimetype for email attachment named " + attachment.getResource().getName(), attachment, e);
206
		}
207
	}
208
209
	private String getMimetype(Attachment attachment, byte[] bytes) throws MimeTypeDetectionException {
210 1 1. getMimetype : negated conditional → RUN_ERROR
		if (attachment.getContentType() != null) {
211 1 1. getMimetype : replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getMimetype → RUN_ERROR
			return attachment.getContentType();
212
		}
213 1 1. getMimetype : replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getMimetype → RUN_ERROR
		return mimetypeProvider.detect(new ByteArrayInputStream(bytes)).toString();
214
	}
215
216
	private static String toCid(final String contentId) {
217 1 1. toCid : negated conditional → RUN_ERROR
		if (contentId == null) {
218 1 1. toCid : replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toCid → RUN_ERROR
			return null;
219
		}
220 1 1. toCid : replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toCid → NO_COVERAGE
		return CID.matcher(contentId).replaceAll("$1");
221
	}
222
223
	public SendGridClient getDelegate() {
224 1 1. getDelegate : replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getDelegate → NO_COVERAGE
		return delegate;
225
	}
226
}

Mutations

104

1.1
Location : <init>
Killed by :
negated conditional → RUN_ERROR

107

1.1
Location : <init>
Killed by :
negated conditional → RUN_ERROR

110

1.1
Location : <init>
Killed by :
negated conditional → RUN_ERROR

123

1.1
Location : send
Killed by :
negated conditional → RUN_ERROR

126

1.1
Location : send
Killed by :
removed call to fr/sii/ogham/email/sendgrid/sender/EmailValidator::validate → RUN_ERROR

134

1.1
Location : send
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/client/SendGridClient::send → RUN_ERROR

146

1.1
Location : intercept
Killed by :
negated conditional → RUN_ERROR

147

1.1
Location : intercept
Killed by :
replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::intercept → RUN_ERROR

149

1.1
Location : intercept
Killed by :
replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::intercept → NO_COVERAGE

154

1.1
Location : toSendGridEmail
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::setSubject → RUN_ERROR

156

1.1
Location : toSendGridEmail
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::setFrom → RUN_ERROR

158

1.1
Location : toSendGridEmail
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::addPersonalization → RUN_ERROR

160

1.1
Location : toSendGridEmail
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/handler/SendGridContentHandler::setContent → RUN_ERROR

163

1.1
Location : toSendGridEmail
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::addAttachment → RUN_ERROR

166

1.1
Location : toSendGridEmail
Killed by :
replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toSendGridEmail → RUN_ERROR

172

1.1
Location : toPersonalization
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::addRecipient → RUN_ERROR

174

1.1
Location : toPersonalization
Killed by :
replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toPersonalization → RUN_ERROR

181

1.1
Location : addRecipient
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addTo → RUN_ERROR

184

1.1
Location : addRecipient
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addCc → NO_COVERAGE

187

1.1
Location : addRecipient
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/PersonalizationCompat::addBcc → NO_COVERAGE

196

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setContent → RUN_ERROR

197

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setContentId → RUN_ERROR

198

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setDisposition → RUN_ERROR

199

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setFilename → RUN_ERROR

200

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/AttachmentsCompat::setType → RUN_ERROR

201

1.1
Location : addAttachment
Killed by :
removed call to fr/sii/ogham/email/sendgrid/v4/sender/impl/sendgrid/compat/MailCompat::addAttachments → RUN_ERROR

210

1.1
Location : getMimetype
Killed by :
negated conditional → RUN_ERROR

211

1.1
Location : getMimetype
Killed by :
replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getMimetype → RUN_ERROR

213

1.1
Location : getMimetype
Killed by :
replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getMimetype → RUN_ERROR

217

1.1
Location : toCid
Killed by :
negated conditional → RUN_ERROR

218

1.1
Location : toCid
Killed by :
replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toCid → RUN_ERROR

220

1.1
Location : toCid
Killed by :
replaced return value with "" for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::toCid → NO_COVERAGE

224

1.1
Location : getDelegate
Killed by :
replaced return value with null for fr/sii/ogham/email/sendgrid/v4/sender/impl/SendGridV4Sender::getDelegate → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.13.1