Skip to content

Commit 20ad6ad

Browse files
committed
set more information for both attachment types and update readme and tests
1 parent 10d760d commit 20ad6ad

3 files changed

Lines changed: 91 additions & 116 deletions

File tree

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ EmailParameter.Email.builder()
4040
// @formatter:on
4141
```
4242

43-
Attachments without a `contentId` are added as regular attachments. Each `contentId` must be referenced in the
44-
`htmlBody` as `cid:contentId`, otherwise validation fails.
43+
Attachments without a `contentId` are added as regular attachments. Each `contentId` must be unique and referenced
44+
in the `htmlBody` as `cid:contentId`, otherwise validation fails.
4545

4646
## Usage
4747

src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.transaction.TransactionDefinition;
2323
import org.springframework.transaction.annotation.Transactional;
2424
import org.springframework.transaction.support.TransactionTemplate;
25+
import org.springframework.util.FileCopyUtils;
2526
import org.springframework.validation.annotation.Validated;
2627

2728
import java.io.IOException;
@@ -209,16 +210,15 @@ private void sendMail(
209210
}
210211

211212
for (var attachment : attachments) {
212-
ByteArrayResource resource;
213-
try (var payload = attachmentDataSource.getAttachmentPayload(attachment.getFileReference())) {
214-
resource = new ByteArrayResource(payload.readAllBytes());
215-
}
213+
var resource = new ByteArrayResource(FileCopyUtils.copyToByteArray(
214+
attachmentDataSource.getAttachmentPayload(attachment.getFileReference())
215+
));
216216

217217
var contentId = attachment.getContentId();
218218
if (contentId != null) {
219-
helper.addInline(contentId, resource, attachment.getContentType());
219+
helper.addInline(contentId, attachment.getFileName(), resource, attachment.getContentType());
220220
} else {
221-
helper.addAttachment(attachment.getFileName(), resource);
221+
helper.addAttachment(attachment.getFileName(), resource, attachment.getContentType());
222222
}
223223
}
224224

src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java

Lines changed: 83 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
import jakarta.mail.internet.MimeMultipart;
1212
import jakarta.validation.ConstraintViolationException;
1313
import org.jspecify.annotations.NullMarked;
14+
import org.jspecify.annotations.Nullable;
1415
import org.junit.jupiter.api.BeforeEach;
1516
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.params.ParameterizedTest;
18+
import org.junit.jupiter.params.provider.CsvSource;
1619
import org.mockito.ArgumentCaptor;
1720
import org.springframework.beans.factory.annotation.Autowired;
1821
import org.springframework.boot.test.context.SpringBootTest;
@@ -164,25 +167,10 @@ void givenRequiredParameterWithAttachedFiles_sendOrFail_shouldSendImmediately()
164167
void givenInlineAttachment_schedule_shouldPersistContentId() throws EmailException, AttachmentException {
165168
when(attachmentDataSource.storeAttachmentPayload(any())).thenReturn(33L);
166169

167-
var parameter = EmailParameter.builder()
168-
.scheduledAt(OffsetDateTime.now())
169-
.email(EmailParameter.Email.builder()
170-
.subject("Example email subject")
171-
.textBody("Email body")
172-
.htmlBody("<h1>Html email body</h1><img src=\"cid:header-logo\">")
173-
.recipient("person1@example.com")
174-
.attachment(
175-
EmailParameter.Email.Attachment.builder()
176-
.contentType("image/png")
177-
.fileName("logo.png")
178-
.contentId("header-logo")
179-
.payload(new ByteArrayInputStream(new byte[]{1, 2, 3}))
180-
.build()
181-
)
182-
.fromAddress("somebody@aboutbits.it")
183-
.fromName("somebody")
184-
.build()
185-
).build();
170+
var parameter = getValidParameterWithInlineAttachment(
171+
"<h1>Html email body</h1><img src=\"cid:header-logo\">",
172+
inlineAttachment("header-logo")
173+
);
186174

187175
var result = manageEmail.schedule(parameter);
188176

@@ -198,32 +186,11 @@ void givenInlineAndRegularAttachment_sendOrFail_shouldAddInlineAndRegularMimePar
198186
when(attachmentDataSource.getAttachmentPayload(anyLong()))
199187
.thenAnswer(_ -> new ByteArrayInputStream(new byte[]{1, 2, 3}));
200188

201-
var parameter = EmailParameter.builder()
202-
.scheduledAt(OffsetDateTime.now())
203-
.email(EmailParameter.Email.builder()
204-
.subject("Example email subject")
205-
.textBody("Email body")
206-
.htmlBody("<h1>Html email body</h1><img src=\"cid:header-logo\">")
207-
.recipient("person1@example.com")
208-
.attachment(
209-
EmailParameter.Email.Attachment.builder()
210-
.contentType("image/png")
211-
.fileName("logo.png")
212-
.contentId("header-logo")
213-
.payload(new ByteArrayInputStream(new byte[]{1, 2, 3}))
214-
.build()
215-
)
216-
.attachment(
217-
EmailParameter.Email.Attachment.builder()
218-
.contentType("image/png")
219-
.fileName("x.png")
220-
.payload(new ByteArrayInputStream(new byte[]{1, 2, 3}))
221-
.build()
222-
)
223-
.fromAddress("somebody@aboutbits.it")
224-
.fromName("somebody")
225-
.build()
226-
).build();
189+
var parameter = getValidParameterWithInlineAttachment(
190+
"<h1>Html email body</h1><img src=\"cid:header-logo\">",
191+
inlineAttachment("header-logo"),
192+
regularAttachment("x.png")
193+
);
227194

228195
var result = manageEmail.sendOrFail(parameter);
229196

@@ -244,6 +211,7 @@ void givenInlineAndRegularAttachment_sendOrFail_shouldAddInlineAndRegularMimePar
244211
.orElseThrow();
245212
assertThat(inlinePart.getContentID()).isEqualTo("<header-logo>");
246213
assertThat(inlinePart.getContentType()).startsWith("image/png");
214+
assertThat(inlinePart.getFileName()).isEqualTo("logo.png");
247215

248216
var attachmentPart = parts.stream()
249217
.filter(part -> hasDisposition(part, Part.ATTACHMENT))
@@ -253,80 +221,42 @@ void givenInlineAndRegularAttachment_sendOrFail_shouldAddInlineAndRegularMimePar
253221
}
254222

255223
@Test
256-
void givenInlineAttachmentWithoutHtmlBody_schedule_shouldFail() {
257-
var parameter = EmailParameter.builder()
258-
.scheduledAt(OffsetDateTime.now())
259-
.email(EmailParameter.Email.builder()
260-
.subject("Example email subject")
261-
.textBody("Email body")
262-
.htmlBody("")
263-
.recipient("person1@example.com")
264-
.attachment(
265-
EmailParameter.Email.Attachment.builder()
266-
.contentType("image/png")
267-
.fileName("logo.png")
268-
.contentId("header-logo")
269-
.payload(new ByteArrayInputStream(new byte[0]))
270-
.build()
271-
)
272-
.fromAddress("somebody@aboutbits.it")
273-
.fromName("somebody")
274-
.build()
275-
).build();
224+
void givenUppercaseCidReference_schedule_shouldSucceed() throws EmailException, AttachmentException {
225+
when(attachmentDataSource.storeAttachmentPayload(any())).thenReturn(33L);
276226

277-
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
278-
() -> manageEmail.schedule(parameter)
227+
var parameter = getValidParameterWithInlineAttachment(
228+
"<h1>Html email body</h1><img src=\"CID:header-logo\">",
229+
inlineAttachment("header-logo")
279230
);
231+
232+
var result = manageEmail.schedule(parameter);
233+
234+
assertThat(result.state()).isEqualTo(EmailState.PENDING);
280235
}
281236

282-
@Test
283-
void givenContentIdNotReferencedInHtmlBody_schedule_shouldFail() {
284-
var parameter = EmailParameter.builder()
285-
.scheduledAt(OffsetDateTime.now())
286-
.email(EmailParameter.Email.builder()
287-
.subject("Example email subject")
288-
.textBody("Email body")
289-
.htmlBody("<h1>Html email body</h1>")
290-
.recipient("person1@example.com")
291-
.attachment(
292-
EmailParameter.Email.Attachment.builder()
293-
.contentType("image/png")
294-
.fileName("logo.png")
295-
.contentId("header-logo")
296-
.payload(new ByteArrayInputStream(new byte[0]))
297-
.build()
298-
)
299-
.fromAddress("somebody@aboutbits.it")
300-
.fromName("somebody")
301-
.build()
302-
).build();
237+
@ParameterizedTest
238+
@CsvSource({
239+
"'', header-logo",
240+
", header-logo",
241+
"<h1>Html email body</h1>, header-logo",
242+
"<img src=cid:header-logo-big>, header-logo",
243+
"<h1>Html email body</h1>, ' '"
244+
})
245+
void givenInvalidInlineAttachment_schedule_shouldFail(@Nullable String htmlBody, String contentId) {
246+
var parameter = getValidParameterWithInlineAttachment(htmlBody, inlineAttachment(contentId));
303247

304248
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
305249
() -> manageEmail.schedule(parameter)
306250
);
307251
}
308252

309253
@Test
310-
void givenBlankContentId_schedule_shouldFail() {
311-
var parameter = EmailParameter.builder()
312-
.scheduledAt(OffsetDateTime.now())
313-
.email(EmailParameter.Email.builder()
314-
.subject("Example email subject")
315-
.textBody("Email body")
316-
.htmlBody("<h1>Html email body</h1>")
317-
.recipient("person1@example.com")
318-
.attachment(
319-
EmailParameter.Email.Attachment.builder()
320-
.contentType("image/png")
321-
.fileName("logo.png")
322-
.contentId(" ")
323-
.payload(new ByteArrayInputStream(new byte[0]))
324-
.build()
325-
)
326-
.fromAddress("somebody@aboutbits.it")
327-
.fromName("somebody")
328-
.build()
329-
).build();
254+
void givenDuplicateContentIds_schedule_shouldFail() {
255+
var parameter = getValidParameterWithInlineAttachment(
256+
"<h1>Html email body</h1><img src=\"cid:header-logo\">",
257+
inlineAttachment("header-logo"),
258+
inlineAttachment("header-logo", "other.png")
259+
);
330260

331261
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
332262
() -> manageEmail.schedule(parameter)
@@ -399,6 +329,51 @@ private static EmailParameter getValidParameterWithAttachment() {
399329
).build();
400330
}
401331

332+
private static EmailParameter getValidParameterWithInlineAttachment(
333+
@Nullable String htmlBody,
334+
EmailParameter.Email.Attachment... attachments
335+
) {
336+
var emailBuilder = EmailParameter.Email.builder()
337+
.subject("Example email subject")
338+
.textBody("Email body")
339+
.recipient("person1@example.com")
340+
.fromAddress("somebody@aboutbits.it")
341+
.fromName("somebody");
342+
343+
if (htmlBody != null) {
344+
emailBuilder.htmlBody(htmlBody);
345+
}
346+
for (var attachment : attachments) {
347+
emailBuilder.attachment(attachment);
348+
}
349+
350+
return EmailParameter.builder()
351+
.scheduledAt(OffsetDateTime.now())
352+
.email(emailBuilder.build())
353+
.build();
354+
}
355+
356+
private static EmailParameter.Email.Attachment inlineAttachment(String contentId) {
357+
return inlineAttachment(contentId, "logo.png");
358+
}
359+
360+
private static EmailParameter.Email.Attachment inlineAttachment(String contentId, String fileName) {
361+
return EmailParameter.Email.Attachment.builder()
362+
.contentType("image/png")
363+
.fileName(fileName)
364+
.contentId(contentId)
365+
.payload(new ByteArrayInputStream(new byte[]{1, 2, 3}))
366+
.build();
367+
}
368+
369+
private static EmailParameter.Email.Attachment regularAttachment(String fileName) {
370+
return EmailParameter.Email.Attachment.builder()
371+
.contentType("image/png")
372+
.fileName(fileName)
373+
.payload(new ByteArrayInputStream(new byte[]{1, 2, 3}))
374+
.build();
375+
}
376+
402377
private static List<MimeBodyPart> flattenParts(Object content) throws Exception {
403378
var parts = new ArrayList<MimeBodyPart>();
404379
if (content instanceof MimeMultipart multipart) {

0 commit comments

Comments
 (0)