-
Notifications
You must be signed in to change notification settings - Fork 0
Implement /batch method for sending/testing/bulk emails
#38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds end-to-end batch email support: new batch request/response models and examples, client routing method MailtrapClient.batchSend, API interface/implementation methods for /api/batch (and /api/batch/{inboxId}), consolidated validation adapters/views, tests, fixtures, and an InvalidRequestBodyException constructor that accepts a cause. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Application
participant Client as MailtrapClient
participant Bulk as BulkSendingApi
participant Send as SendingApi
participant Test as TestingApi
App->>Client: batchSend(MailtrapBatchMail)
alt Bulk mode
Client->>Bulk: emails().batchSend(batchMail)
Bulk-->>Client: BatchSendResponse
else Sandbox/Testing mode
Client->>Test: emails().batchSend(batchMail, inboxId)
Test-->>Client: BatchSendResponse
else Standard mode
Client->>Send: emails().batchSend(batchMail)
Send-->>Client: BatchSendResponse
end
Client-->>App: BatchSendResponse
sequenceDiagram
autonumber
participant Impl as *EmailsImpl
participant Validator as SendApiResource
participant HTTP as HTTP Client
Impl->>Validator: assertBatchMailNotNull(batchMail)
loop For each item in batchMail.requests
Impl->>Validator: ResolvedMailView(base,item)
Validator->>Validator: validateMailPayload(resolved view)
Validator->>Validator: validateContentRules(ResolvedMailContentView.of(view))
end
Impl->>HTTP: POST /api/batch[/{inboxId}] with batchMail
HTTP-->>Impl: BatchSendResponse
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (1)
56-71: Logic bug: subject-only emails (no text/html) pass validation when templateUuid is absent.Current checks only reject when subject, text, and html are all empty. They allow subject-only payloads, contradicting the stated requirement “subject and either text or html”. Add an explicit check that at least one of text/html is present.
Apply this diff:
- private void validateWithoutTemplate(MailtrapMail mail, boolean isSubjectTextHtmlEmpty) throws InvalidRequestBodyException { + private void validateWithoutTemplate(MailtrapMail mail, boolean isSubjectTextHtmlEmpty) throws InvalidRequestBodyException { // Ensure that at least subject, text, or html is provided if templateUuid is not set - if (isSubjectTextHtmlEmpty) { - throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); - } + if (isSubjectTextHtmlEmpty) { + throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); + } // Ensure templateVariables are not used if templateUuid is not set if (MapUtils.isNotEmpty(mail.getTemplateVariables())) { throw new InvalidRequestBodyException("Mail templateVariables must only be used with templateUuid"); } // Ensure the subject is not empty - if (StringUtils.isEmpty(mail.getSubject())) { + if (StringUtils.isBlank(mail.getSubject())) { throw new InvalidRequestBodyException("Subject must not be null or empty"); } + + // Ensure at least one of text or html is present + if (StringUtils.isBlank(mail.getText()) && StringUtils.isBlank(mail.getHtml())) { + throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); + } }
🧹 Nitpick comments (27)
src/test/resources/api/emails/batchSendResponse.json (1)
1-13: Add a mixed-result case to strengthen assertions.Consider adding a second item with a failure (e.g., success=false with errors) to verify partial successes and error aggregation in parsers.
src/test/resources/api/emails/batchSendRequest.json (1)
1-26: Include multiple requests to truly exercise batching.Add at least two requests (one minimal, one with attachments/headers) to validate list handling and per-item validation.
src/test/java/io/mailtrap/testutils/BaseSendTest.java (1)
13-22: Optional: Make constants static final for canonical usage.These are immutable and shared; using static final avoids per-instance duplication.
src/main/java/io/mailtrap/api/testingemails/TestingEmails.java (1)
26-28: Add Javadoc for batchSend for parity with send.Document params, behavior, and exceptions for consistency.
SendResponse send(MailtrapMail mail, long inboxId) throws HttpException, InvalidRequestBodyException; + /** + * Sends a batch of emails to a Testing inbox. + * + * @param mail Batch payload (list of requests). + * @param inboxId ID of the testing inbox. + * @return Batch send response with per-item results. + * @throws HttpException on HTTP errors. + * @throws InvalidRequestBodyException on invalid payload. + */ BatchSendResponse batchSend(MailtrapBatchMail mail, long inboxId) throws HttpException, InvalidRequestBodyException;src/main/java/io/mailtrap/api/bulkemails/BulkEmails.java (1)
26-27: Add Javadoc for batchSend to match interface style.Keeps API surface consistently documented.
SendResponse send(MailtrapMail mail) throws HttpException, InvalidRequestBodyException; + /** + * Sends a batch of emails via Bulk API. + * + * @param mail Batch payload (list of requests). + * @return Batch send response. + * @throws HttpException + * @throws InvalidRequestBodyException + */ BatchSendResponse batchSend(MailtrapBatchMail mail) throws HttpException, InvalidRequestBodyException;src/main/java/io/mailtrap/api/sendingemails/SendingEmails.java (1)
26-27: Add Javadoc for batchSend for completeness.Aligns with send() documentation and other interfaces.
SendResponse send(MailtrapMail mail) throws HttpException, InvalidRequestBodyException; + /** + * Sends a batch of emails via Sending API. + * + * @param mail Batch payload (list of requests). + * @return Batch send response. + * @throws HttpException + * @throws InvalidRequestBodyException + */ BatchSendResponse batchSend(MailtrapBatchMail mail) throws HttpException, InvalidRequestBodyException;examples/java/io/mailtrap/examples/bulk/Batch.java (1)
11-11: Remove unused import.The
Mapimport is not used in this example class.import java.util.List; -import java.util.Map;src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java (2)
35-46: Consider consistent error handling approach.The
batchSendmethod declaresthrows HttpException, InvalidRequestBodyExceptionin its signature, but these exceptions are already runtime exceptions that don't need to be declared. This is inconsistent with thesendmethod which doesn't declare any exceptions.@Override -public BatchSendResponse batchSend(MailtrapBatchMail mail, long inboxId) throws HttpException, InvalidRequestBodyException { +public BatchSendResponse batchSend(MailtrapBatchMail mail, long inboxId) {
43-45: Remove unnecessary line breaks.The return statement has unnecessary line breaks that hurt readability.
- return - httpClient.post(String.format(apiHost + "/api/batch/%d", inboxId), mail, new RequestData(), BatchSendResponse.class); - + return httpClient.post(String.format(apiHost + "/api/batch/%d", inboxId), mail, new RequestData(), BatchSendResponse.class);examples/java/io/mailtrap/examples/sending/Batch.java (1)
11-11: Remove unused import.The
Mapimport is not used in this example class.import java.util.List; -import java.util.Map;src/main/java/io/mailtrap/client/MailtrapClient.java (1)
81-86: Fix Javadoc description.The Javadoc comment incorrectly states "Sends an email" (singular) while the method sends multiple emails in a batch.
/** - * Sends an email based on the specified sending configuration. + * Sends a batch of emails based on the specified sending configuration. * * @param mailtrapBatchMail emails to send * @return the response from the sending operation */src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java (2)
35-46: Consider consistent error handling approach.The
batchSendmethod declaresthrows HttpException, InvalidRequestBodyExceptionin its signature, but these are runtime exceptions that don't need to be declared. This is inconsistent with thesendmethod which doesn't declare any exceptions.@Override -public BatchSendResponse batchSend(MailtrapBatchMail mail) throws HttpException, InvalidRequestBodyException { +public BatchSendResponse batchSend(MailtrapBatchMail mail) {
43-45: Remove unnecessary line breaks.The return statement has unnecessary line breaks that reduce readability.
- return - httpClient.post(apiHost + "/api/batch", mail, new RequestData(), BatchSendResponse.class); - + return httpClient.post(apiHost + "/api/batch", mail, new RequestData(), BatchSendResponse.class);examples/java/io/mailtrap/examples/testing/Batch.java (1)
26-27: Showcase the new client-level batchSend API (clearer, single entrypoint)Switch to testing context once, then call client.batchSend(...).
final var client = MailtrapClientFactory.createMailtrapClient(config); @@ - System.out.println(client.testingApi().emails().batchSend(batchMail, config.getInboxId())); + client.switchToEmailTestingApi(config.getInboxId()); + System.out.println(client.batchSend(batchMail));Also applies to: 45-46
src/main/java/io/mailtrap/model/response/emails/BatchSendResponse.java (1)
3-16: Harden JSON mapping against forward-compatible fieldsIgnore unknown JSON to avoid breakage if the API adds properties.
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -@Data +@Data +@JsonIgnoreProperties(ignoreUnknown = true) public class BatchSendResponse {src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (1)
137-178: Add tests for base+template conflict and empty batch requestsCoverage is good; add two edge cases:
- requests use templateUuid while base sets subject → expect TEMPLATE_UUID_IS_USED_SUBJECT_AND_TEXT_AND_HTML_SHOULD_BE_EMPTY.
- empty requests list → expect an InvalidRequestBodyException (align with suggested DTO validation).
Example additions:
@Test void batchSend_BaseSubjectWithTemplateUuid_ThrowsInvalidRequestBodyException() { var mail = createTestMailFromTemplate(); // has templateUuid + templateVariables var batchMail = MailtrapBatchMail.builder() .base(BatchEmailBase.builder().subject("Base Subject").build()) .requests(List.of(mail)) .build(); var ex = assertThrows(InvalidRequestBodyException.class, () -> bulkEmails.batchSend(batchMail)); assertEquals(TEMPLATE_UUID_IS_USED_SUBJECT_AND_TEXT_AND_HTML_SHOULD_BE_EMPTY, ex.getMessage()); } @Test void batchSend_EmptyRequests_ThrowsInvalidRequestBodyException() { var batchMail = MailtrapBatchMail.builder().requests(List.of()).build(); var ex = assertThrows(InvalidRequestBodyException.class, () -> bulkEmails.batchSend(batchMail)); // choose appropriate constant if available; otherwise assert on message substring }src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
137-178: Mirror bulk tests: add base+template conflict and empty requests casesKeep API parity between sending and bulk tests.
@Test void batchSend_BaseSubjectWithTemplateUuid_ThrowsInvalidRequestBodyException() { var mail = createTestMailFromTemplate(); var batchMail = MailtrapBatchMail.builder() .base(BatchEmailBase.builder().subject("Base Subject").build()) .requests(List.of(mail)) .build(); var ex = assertThrows(InvalidRequestBodyException.class, () -> sendApi.batchSend(batchMail)); assertEquals(TEMPLATE_UUID_IS_USED_SUBJECT_AND_TEXT_AND_HTML_SHOULD_BE_EMPTY, ex.getMessage()); } @Test void batchSend_EmptyRequests_ThrowsInvalidRequestBodyException() { var batchMail = MailtrapBatchMail.builder().requests(List.of()).build(); var ex = assertThrows(InvalidRequestBodyException.class, () -> sendApi.batchSend(batchMail)); }src/test/java/io/mailtrap/client/MailtrapClientTest.java (1)
126-137: Add a client-level null batch testEnsure MailtrapClient.batchSend(null) propagates validation consistently across contexts.
@Test void test_batchSend_Null_throwsInvalidRequestBodyException() { assertThrows(InvalidRequestBodyException.class, () -> mailtrapClient.batchSend(null)); }src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (4)
36-43: Good addition of batch endpoint mocks; consider adding negative-path mocks.Add one 4xx/5xx mock (e.g., 400 invalid payload) to exercise error handling on batch requests.
139-151: Solid happy-path coverage; assert collection sizes too.Add assertions on responses size to guard against accidental extra items:
- assertEquals(1, response.getResponses().size());
- assertEquals(1, response.getResponses().get(0).getMessageIds().size());
152-164: Mirror the same size assertions in the template-based batch test.Replicate the collection size checks here for symmetry.
172-181: Great invalid-with-template coverage; add one more negative: base-only template + per-item text/html.Add a test where BatchEmailBase has templateUuid set but a request provides text/html, expecting the same validation error. This catches merge conflicts between base and per-request fields.
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (2)
74-77: Align template validation with isBlank().Prevent whitespace-only subject/text/html when templateUuid is used.
Apply this diff:
- if (StringUtils.isNotEmpty(mail.getText()) || StringUtils.isNotEmpty(mail.getHtml()) || StringUtils.isNotEmpty(mail.getSubject())) { + if (StringUtils.isNotBlank(mail.getText()) || StringUtils.isNotBlank(mail.getHtml()) || StringUtils.isNotBlank(mail.getSubject())) { throw new InvalidRequestBodyException("When templateUuid is used, subject, text, and html must not be used"); }
52-54: Comment nit: remove “assumed to be provided by the user”.This reads like a placeholder in production code.
Apply this diff:
- // Additional validation logic (assumed to be provided by the user) + // Additional generic validations common to all email payloadssrc/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java (3)
13-17: Consider omitting nulls in JSON.Add @JsonInclude(Include.NON_NULL) to reduce payload size and avoid sending meaningless nulls.
Apply this diff:
+import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -@Getter -@Setter -@Builder -public class BatchEmailBase { +@Getter +@Setter +@Builder +@JsonInclude(Include.NON_NULL) +public class BatchEmailBase {
49-54: Type check: should custom properties allow non-string values?If the API allows numeric/boolean values here, switch to Map<String, Object> for parity with templateVariables.
26-36: Cross-field constraints are documented but not enforced here.Relying on SendApiResource for enforcement is fine, but consider adding a bean-level validator for BatchEmailBase if base-only sends are allowed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
examples/java/io/mailtrap/examples/bulk/Batch.java(1 hunks)examples/java/io/mailtrap/examples/sending/Batch.java(1 hunks)examples/java/io/mailtrap/examples/testing/Batch.java(1 hunks)src/main/java/io/mailtrap/api/apiresource/SendApiResource.java(2 hunks)src/main/java/io/mailtrap/api/bulkemails/BulkEmails.java(2 hunks)src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/api/sendingemails/SendingEmails.java(2 hunks)src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/api/testingemails/TestingEmails.java(2 hunks)src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/client/MailtrapClient.java(2 hunks)src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java(1 hunks)src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java(1 hunks)src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java(1 hunks)src/main/java/io/mailtrap/model/response/emails/BatchSendResponse.java(1 hunks)src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java(3 hunks)src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java(3 hunks)src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java(3 hunks)src/test/java/io/mailtrap/client/MailtrapClientTest.java(4 hunks)src/test/java/io/mailtrap/testutils/BaseSendTest.java(1 hunks)src/test/resources/api/emails/batchSendRequest.json(1 hunks)src/test/resources/api/emails/batchSendRequestFromTemplate.json(1 hunks)src/test/resources/api/emails/batchSendResponse.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
examples/java/io/mailtrap/examples/testing/Batch.java (1)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
MailtrapClientFactory(32-124)
src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java (1)
src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java (1)
InvalidRequestBodyException(6-12)
src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java (2)
src/main/java/io/mailtrap/model/AbstractModel.java (1)
AbstractModel(10-29)src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java (1)
Getter(13-67)
examples/java/io/mailtrap/examples/bulk/Batch.java (2)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
MailtrapClientFactory(32-124)examples/java/io/mailtrap/examples/sending/Batch.java (1)
Bulk(13-43)
src/main/java/io/mailtrap/model/response/emails/BatchSendResponse.java (1)
src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java (1)
Data(8-17)
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java (1)
src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java (1)
Getter(10-19)
src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java (1)
src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java (1)
InvalidRequestBodyException(6-12)
src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java (1)
src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java (1)
InvalidRequestBodyException(6-12)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/client/MailtrapClientTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
examples/java/io/mailtrap/examples/sending/Batch.java (2)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
MailtrapClientFactory(32-124)examples/java/io/mailtrap/examples/bulk/Batch.java (1)
Bulk(13-43)
src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java (1)
src/main/java/io/mailtrap/model/response/emails/BatchSendResponse.java (1)
Data(7-16)
🔇 Additional comments (8)
src/test/java/io/mailtrap/testutils/BaseSendTest.java (1)
21-21: LGTM: Null-message assertion string added for batch.Matches existing style and improves test clarity.
src/test/resources/api/emails/batchSendRequestFromTemplate.json (1)
1-25: Mapping correct: no action needed BatchEmailBase and MailtrapMail both annotatetemplate_uuidandtemplate_variableswith @JsonProperty, and Address.name is correctly mapped by default.src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java (2)
27-33: LGTM: Reuse central mail validation.Switch to validateMailPayload(mail) simplifies and unifies validation.
43-45: Confirm HTTP header propagation in batchSend. The batchSend call instantiatesnew RequestData()without forwarding any headers fromBatchEmailBase#getHeaders()(accessible viamail.getBase().getHeaders()). If batch requests should honor custom headers assend()does, pullmail.getBase().getHeaders()into aRequestDataand pass it tohttpClient.post().src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java (1)
1-18: LGTM!The implementation of
BatchSendDetailsdata class is correct and follows best practices. The use of Lombok's@Dataannotation provides all necessary methods, and the Jackson annotation properly maps the JSON property name.src/main/java/io/mailtrap/client/MailtrapClient.java (1)
87-95: LGTM!The
batchSendmethod implementation is clean and follows the same context-driven dispatch pattern as the existingsendmethod. The routing logic correctly delegates to the appropriate API based on the sending context.src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (1)
165-170: Null batch guard test looks good.Covers the new null-check helper clearly.
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (1)
32-54: Rename successful: no remaining calls tovalidateRequestBodyOrThrowException.
src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java
Outdated
Show resolved
Hide resolved
src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (1)
22-32: Batch guard strengthened — nice follow-through on prior feedback.Null envelope, empty requests, and null items are all blocked; solid fail-fast.
🧹 Nitpick comments (11)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (4)
28-43: Batch endpoint mocks wired to the correct host and paths.
Good coverage for both raw and template-based batch requests. Consider adding one negative/edge mock (e.g., partial success or 400 validation error) to exercise error mapping.
104-113: Great: validates “subject required with either text or html”.
This complements the existing “missing subject/text/html” case.You may also add a counterpart test for “text or html present but subject missing” to fully cover the rule.
147-158: Solid happy-path batch assertions.
To tighten expectations, assert the response list size matches the request count.Apply this minimal assertion in both tests:
assertTrue(response.isSuccess()); +assertEquals(1, response.getResponses().size()); assertEquals("22222", response.getResponses().get(0).getMessageIds().get(0));Also applies to: 160-171
180-188: Good: batch validation rejects template UUID with text.
For parity with single-send tests, consider adding:
- template UUID + html (invalid)
- template variables + html (invalid)
- empty requests array, and requests containing a null element
- multi-item batch to check per-request validation and ordering guarantees (all-or-nothing vs partial)
Do the API semantics require non-empty requests and reject null items? If yes, I can propose concrete tests and fixtures.
src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (3)
36-43: Mocks for testing (sandbox) batch endpoints look right.
Correctly target EMAIL_TESTING_SEND_HOST with inbox-scoped paths.Add a second mock with a different INBOX_ID and a test to verify the method honors the parameter even when a default inboxId exists in config.
149-160: Happy-path batch tests read well.
Add a small extra assertion on response count to strengthen expectations.assertTrue(response.isSuccess()); +assertEquals(1, response.getResponses().size()); assertEquals("22222", response.getResponses().get(0).getMessageIds().get(0));Also consider a multi-request batch test (e.g., 2 requests) to confirm ordering and aggregation.
Also applies to: 162-173
182-191: Batch validation case mirrored here—good.
Recommend adding:
- template UUID + html (invalid)
- empty requests and requests with a null element
- mixed-result payload (one valid, one invalid) to validate error aggregation strategy
If mixed results are possible from the upstream API, I can draft assertions aligned with the expected contract (overall success flag vs per-item statuses).
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (4)
64-83: Reduce duplication in non-template validation.Collapse the three checks into one condition; keeps behavior, trims redundancy.
- // Ensure that at least subject, text, or html is provided if templateUuid is not set - if (isSubjectTextHtmlEmpty) { - throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); - } + // Ensure subject is present and either text or html is provided when templateUuid is not set + if (StringUtils.isBlank(mail.getSubject()) || + (StringUtils.isBlank(mail.getText()) && StringUtils.isBlank(mail.getHtml()))) { + throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); + } @@ - // Ensure the subject is not empty - if (StringUtils.isBlank(mail.getSubject())) { - throw new InvalidRequestBodyException("Subject must not be null or empty"); - } - - // Ensure at least one of text or html is present - if (StringUtils.isBlank(mail.getText()) && StringUtils.isBlank(mail.getHtml())) { - throw new InvalidRequestBodyException("Mail must have subject and either text or html when templateUuid is not provided"); - } + // Note: Subject/text/html validated above.
88-91: Treat whitespace-only fields as empty in template branch.Use
isNotBlankso " " doesn’t trigger a false-positive violation.- if (StringUtils.isNotEmpty(mail.getText()) || StringUtils.isNotEmpty(mail.getHtml()) || StringUtils.isNotEmpty(mail.getSubject())) { + if (StringUtils.isNotBlank(mail.getText()) || StringUtils.isNotBlank(mail.getHtml()) || StringUtils.isNotBlank(mail.getSubject())) {
40-62: Use isBlank for templateUuid; validator method name confirmed
- Replace isEmpty→isBlank so whitespace-only templateUuid is treated as absent. Validator validateRequestBodyAndThrowException exists in src/main/java/io/mailtrap/api/apiresource/ApiResourceWithValidation.java.
Apply:
- if (StringUtils.isEmpty(mail.getTemplateUuid())) { + if (StringUtils.isBlank(mail.getTemplateUuid())) {
22-32: Enforce Mailtrap batch caps and clarify method intent
- Validate max batch size: Mailtrap’s /batch allows up to 500 messages (payload ~50 MB). Add a check:
if (batchMail.getRequests().size() > 500) throw new InvalidRequestBodyException("BatchMail.requests must not contain more than 500 items");- Rename assertBatchMailNotNull(...) → assertValidBatchMail(...) to reflect broader validation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
examples/java/io/mailtrap/examples/bulk/Batch.java(1 hunks)examples/java/io/mailtrap/examples/sending/Batch.java(1 hunks)examples/java/io/mailtrap/examples/testing/Batch.java(1 hunks)src/main/java/io/mailtrap/api/apiresource/SendApiResource.java(3 hunks)src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java(1 hunks)src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java(1 hunks)src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/testutils/BaseSendTest.java(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- examples/java/io/mailtrap/examples/bulk/Batch.java
- examples/java/io/mailtrap/examples/testing/Batch.java
- src/test/java/io/mailtrap/testutils/BaseSendTest.java
- examples/java/io/mailtrap/examples/sending/Batch.java
- src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java
- src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java
- src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
🔇 Additional comments (8)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (3)
7-9: Imports for batch models look correct.
Needed types are imported without expanding visibility more than necessary.
47-49: Config initialization is minimal and focused.
Injecting the TestHttpClient and token is sufficient for these tests.
173-178: Null batch request validation covered.
Ensures top-level null guard behaves as expected.src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (3)
7-9: Imports for batch models are appropriate.
Matches usage below.
106-114: Nice addition: subject-without-body validation on sandbox path.
Parity with sending tests maintained.
175-180: Null batch guard covered for sandbox flow.
Consistent with sending tests.src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (2)
6-6: Import addition acknowledged.No concerns.
11-12: Good: Objects import for null-item checks.
|
|
||
| // Ensure the subject is not empty | ||
| if (StringUtils.isEmpty(mail.getSubject())) { | ||
| if (StringUtils.isBlank(mail.getSubject())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just making sure: is it going to be possible to send a batch like this? subject on requests is only required if there's no subject in base. Same goes for other attributes that are usually required for single mail.
{
"base": {
"subject": "My Subject"
},
"requests": [
{
"subject": "My Other Subject"
},
{
"comment": "No subject here, so for this email API will use the base subject"
}
]
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, good catch.
I've updated the validation process a bit to take into account the base object for the batch send.
For a single send, it should remain as it is, for a batch - take subject/text/etc field from the particular mail from requests, if it's missing - from base
…unit-tests with `base`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (1)
75-85: Stronger batch guard implemented as previously suggested.Validates null, empty list, and null elements. Nice.
🧹 Nitpick comments (16)
src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java (1)
12-14: Use Throwable as the cause type for broader compatibility.Standard Java exception chaining accepts Throwable; restricting to Exception excludes Errors and other Throwables.
- public InvalidRequestBodyException(String errorMessage, Exception cause) { + public InvalidRequestBodyException(String errorMessage, Throwable cause) { super(errorMessage, cause); }src/main/java/io/mailtrap/model/mailvalidation/ContentView.java (1)
11-19: Clarify nullability contract on getters.Downstream rules rely on null/blank checks; make it explicit via brief Javadoc per getter or @nullable annotations (if used in this codebase).
String getSubject(); + // May be null/blank when relying on a base value. String getText(); + // May be null/blank when relying on a base value. String getHtml(); + // May be null/blank when relying on a base value. String getTemplateUuid(); + // May be null/blank when raw content is used. Map<String, Object> getTemplateVariables(); + // May be null/empty when no template is used.src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (2)
212-223: Rename test to reflect expected exception, not success.Name says “SuccessResponse” but the test asserts an exception.
- void batchSend_InvalidMailWithNoSubjectAndTextNoBase_SuccessResponse() { + void batchSend_InvalidMailWithNoSubjectAndTextNoBase_ThrowsInvalidRequestBodyException() {
164-167: Also assert response count matches request count.Minor safety net for regressions in response mapping.
assertTrue(response.isSuccess()); assertEquals("22222", response.getResponses().get(0).getMessageIds().get(0)); + assertEquals(1, response.getResponses().size());src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (2)
214-224: Rename test to reflect exception expectation.- void batchSend_InvalidMailWithNoSubjectAndTextNoBase_SuccessResponse() { + void batchSend_InvalidMailWithNoSubjectAndTextNoBase_ThrowsInvalidRequestBodyException() {
176-181: Add response-size assertion for completeness.assertTrue(response.isSuccess()); assertEquals("22222", response.getResponses().get(0).getMessageIds().get(0)); + assertEquals(1, response.getResponses().size());src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailView.java (2)
81-89: Prefer merging template variables and returning an unmodifiable view.Preserves “read-only overlay” intent and lets item keys override base keys.
- public Map<String, Object> getTemplateVariables() { - Map<String, Object> templateVariables = item.getTemplateVariables(); - - if ((templateVariables == null || templateVariables.isEmpty()) && base != null) { - templateVariables = base.getTemplateVariables(); - } - - return templateVariables; - } + public Map<String, Object> getTemplateVariables() { + Map<String, Object> itemVars = item.getTemplateVariables(); + Map<String, Object> baseVars = (base != null) ? base.getTemplateVariables() : null; + + if ((baseVars == null || baseVars.isEmpty()) && (itemVars == null || itemVars.isEmpty())) { + return null; + } + + Map<String, Object> merged = new HashMap<>(); + if (baseVars != null) merged.putAll(baseVars); + if (itemVars != null) merged.putAll(itemVars); // item overrides base + return Collections.unmodifiableMap(merged); + }Add required imports:
import java.util.List; -import java.util.Map; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections;
15-19: Doc nit: emphasize immutability/read-only contract.Since getters expose model instances (e.g., Map), clarifying that callers must not mutate results avoids misuse.
src/test/java/io/mailtrap/testutils/BaseSendTest.java (2)
20-20: Deduplicate identical error-message constants.This string duplicates Line 17. Reference the earlier constant to avoid drift.
- protected final String MAIL_MUST_HAVE_SUBJECT_AND_EITHER_TEXT_OR_HTML = "Mail must have subject and either text or html when templateUuid is not provided"; + protected final String MAIL_MUST_HAVE_SUBJECT_AND_EITHER_TEXT_OR_HTML = + TEMPLATE_UUID_OR_SUBJECT_AND_TEXT_OR_HTML_MUST_NOT_BE_EMPTY;
105-116: Fix minor typo in subject text.Clean up the duplicated word.
- .subject("Sample invalidvalid mail subject") + .subject("Sample invalid mail subject")src/main/java/io/mailtrap/model/mailvalidation/MailContentView.java (1)
1-40: Null-guard the wrapped MailtrapMail.Defensive check prevents accidental NPEs if the factory is ever used without prior null validation.
package io.mailtrap.model.mailvalidation; import io.mailtrap.model.request.emails.MailtrapMail; +import java.util.Objects; import java.util.Map; @@ public static ContentView of(MailtrapMail content) { - return new MailContentView(content); + return new MailContentView(Objects.requireNonNull(content, "mail content must not be null")); } @@ - private MailContentView(MailtrapMail content) { - this.mail = content; + private MailContentView(MailtrapMail content) { + this.mail = Objects.requireNonNull(content, "mail content must not be null"); }src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (2)
212-222: Rename test to reflect exception outcome.Method name says SuccessResponse but it asserts an exception.
- void batchSend_InvalidMailWithNoSubjectAndTextNoBase_SuccessResponse() { + void batchSend_InvalidMailWithNoSubjectAndTextNoBase_ThrowsInvalidRequestBodyException() {
25-61: Add tests for empty/nullable requests list.You already validate these in SendApiResource; add assertions to prevent regressions.
@@ public void init() { @@ } @@ + @Test + void batchSend_EmptyRequests_ThrowsInvalidRequestBodyException() { + MailtrapBatchMail batchMail = MailtrapBatchMail.builder().requests(List.of()).build(); + InvalidRequestBodyException ex = assertThrows(InvalidRequestBodyException.class, () -> bulkEmails.batchSend(batchMail)); + assertEquals("BatchMail.requests must not be null or empty", ex.getMessage()); + } + + @Test + void batchSend_NullItemInRequests_ThrowsInvalidRequestBodyException() { + MailtrapBatchMail batchMail = MailtrapBatchMail.builder().requests(List.of(createValidTestMail(), null)).build(); + InvalidRequestBodyException ex = assertThrows(InvalidRequestBodyException.class, () -> bulkEmails.batchSend(batchMail)); + assertEquals("BatchMail.requests must not contain null items", ex.getMessage()); + }src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailContentView.java (1)
1-39: Null-guard the wrapped ResolvedMailView.Mirror the MailContentView suggestion to avoid hidden NPEs.
package io.mailtrap.model.mailvalidation; import java.util.Map; +import java.util.Objects; @@ public static ContentView of(ResolvedMailView mailView) { - return new ResolvedMailContentView(mailView); + return new ResolvedMailContentView(Objects.requireNonNull(mailView, "resolved mail view must not be null")); } @@ - private ResolvedMailContentView(ResolvedMailView mailView) { - this.mailView = mailView; + private ResolvedMailContentView(ResolvedMailView mailView) { + this.mailView = Objects.requireNonNull(mailView, "resolved mail view must not be null"); }src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (2)
51-73: Batch validation flow is sound; consider redundant from-check.Bean validation on ResolvedMailView.getFrom() (annotated @NotNull) should already catch missing from. The explicit check on Line 69 will never execute if the validator throws first. If you want the friendlier message, consider removing @NotNull and keeping the explicit check, or keep both but note the redundancy.
88-115: Content rules read clearly; tiny consistency nit.
- Minor: use isNotBlank for symmetry with earlier isBlank checks (still rejects whitespace-only).
- Consider extracting error messages into shared constants to prevent drift with tests.
- if (StringUtils.isNotEmpty(v.getSubject()) - || StringUtils.isNotEmpty(v.getText()) - || StringUtils.isNotEmpty(v.getHtml())) + if (StringUtils.isNotBlank(v.getSubject()) + || StringUtils.isNotBlank(v.getText()) + || StringUtils.isNotBlank(v.getHtml())) throw new InvalidRequestBodyException("When templateUuid is used, subject, text, and html must not be used");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java(2 hunks)src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java(2 hunks)src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java(1 hunks)src/main/java/io/mailtrap/model/mailvalidation/ContentView.java(1 hunks)src/main/java/io/mailtrap/model/mailvalidation/MailContentView.java(1 hunks)src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailContentView.java(1 hunks)src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailView.java(1 hunks)src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java(4 hunks)src/test/java/io/mailtrap/testutils/BaseSendTest.java(3 hunks)src/test/resources/api/emails/batchSendWithBaseSubjectAndTextRequest.json(1 hunks)src/test/resources/api/emails/batchSendWithBaseSubjectRequest.json(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/test/resources/api/emails/batchSendWithBaseSubjectAndTextRequest.json
- src/test/resources/api/emails/batchSendWithBaseSubjectRequest.json
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java
- src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java
- src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (3)
src/main/java/io/mailtrap/model/mailvalidation/MailContentView.java (1)
MailContentView(10-40)src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailContentView.java (1)
ResolvedMailContentView(9-39)src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailView.java (1)
ResolvedMailView(18-90)
src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
🔇 Additional comments (16)
src/main/java/io/mailtrap/model/mailvalidation/ContentView.java (1)
5-21: Good, minimal adapter surface for content validation.src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (2)
37-52: Batch endpoint mocks look correct and cover key scenarios.
113-121: Nice addition to single-send validation coverage.src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java (2)
38-52: Inbox-scoped batch endpoint mocks look correct.
115-123: Good parity check: subject without text/html should fail in sandbox as well.src/main/java/io/mailtrap/model/mailvalidation/ResolvedMailView.java (1)
41-69: Confirm “blank means fallback” semantics.Using StringUtils.isBlank to fall back to base implies an intentionally empty subject/text/html on an item cannot override the base. Verify this matches API intent.
src/test/java/io/mailtrap/testutils/BaseSendTest.java (3)
22-24: New constants align with validation messages.Messages match SendApiResource.validateContentRules; good for assertion stability.
52-64: Good batch helper without subject.Appropriate for scenarios using base.subject.
66-76: Good batch helper without subject and text.Pairs well with base.subject + base.text test.
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (6)
28-51: Batch mocks registered against correct host and path.Endpoints and fixtures look consistent with /api/batch contract.
113-121: Single-send validation case is correct.Covers subject-only scenario; message assertion matches validator.
156-180: Happy-path batch test (single request) reads well.Validates basic batch flow and response mapping.
182-210: Happy-path batch with base overrides is solid.Good coverage for base.subject and base.text resolution.
224-229: Null batch guard covered.Matches the new validation error message.
231-239: Template + text conflict covered for batch.Good negative case mirroring single-send rules.
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java (1)
33-43: Centralized single-send validation looks good.Null guard + bean validation + content rules via ContentView is a clear flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
156-168: Broaden batch coverage: multi-item, precedence, empty/null elements.Consider adding:
- success with multiple requests (>1) to validate aggregation semantics,
- empty requests list -> expected error or behavior,
- list containing a null item -> expected error,
- precedence when both base.subject/text and request-level subject/text are provided (request-level should win; verify),
- request with templateUuid while base supplies subject/text -> should be invalid per single-send rules.
If helpful, add a helper to reduce duplication:
+private MailtrapBatchMail buildBatch(List<MailtrapMail> requests, BatchEmailBase base) { + return MailtrapBatchMail.builder().base(base).requests(requests).build(); +}Also applies to: 169-181, 182-195, 197-210, 212-223, 224-229, 231-239
src/test/java/io/mailtrap/testutils/BaseSendTest.java (1)
104-115: Typo in subject string."Sample invalidvalid mail subject" has a duplicated word.
Apply:
- .subject("Sample invalidvalid mail subject") + .subject("Sample invalid mail subject")src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (1)
156-239: Mirror the expanded batch coverage suggested in SendingEmails tests.Add the same multi-item, empty/null, precedence, and base+templateUuid conflict scenarios for bulk to ensure parity.
I can draft parameterized tests to DRY send vs bulk across a common suite if you want.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java(5 hunks)src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java(5 hunks)src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java(5 hunks)src/test/java/io/mailtrap/testutils/BaseSendTest.java(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (1)
src/main/java/io/mailtrap/Constants.java (1)
Constants(6-15)
🔇 Additional comments (13)
src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java (5)
7-11: Imports for batch DTOs and response look correct.They match the new batch API usage in this test.
56-58: Client config wiring LGTM.Injecting the httpClient and token for test isolation is correct.
113-121: New negative test covers subject-without-body rule.Good addition aligned with validator semantics.
80-80: Old constant absent; new constant used only in tests.Search found no occurrences of TEMPLATE_UUID_OR_SUBJECT_AND_TEXT_OR_HTML_MUST_NOT_BE_EMPTY; MAIL_MUST_HAVE_SUBJECT_AND_EITHER_TEXT_OR_HTML is defined at src/test/java/io/mailtrap/testutils/BaseSendTest.java:19 and referenced only in tests (BulkEmailsImplTest, TestingEmailsImplTest, SendingEmailsImplTest).
29-52: No action needed — fixtures present and matching is by JSON bodyTestHttpClient groups mocks by method+URL and iterates through the list comparing query params and JSON body equality (Mapper.readTree); all referenced request/response fixtures exist in src/test/resources/api/emails. See src/test/java/io/mailtrap/testutils/TestHttpClient.java and src/test/java/io/mailtrap/testutils/DataMock.java.
src/test/java/io/mailtrap/testutils/BaseSendTest.java (3)
19-23: New/renamed validation messages are clear and consistent.These align with the new tests and read well.
51-63: Helper without subject is handy for base-subject cases.All required fields present; no text/subject by design. LGTM.
65-75: Helper without subject and text supports base-subject+text cases.Looks correct.
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java (5)
7-11: Batch imports for bulk path look correct.Models match usage in tests.
55-58: bulk(true) configuration is correctly set for BulkEmails.This ensures routing to the bulk endpoint in the client factory.
113-121: Negative test for subject-without-body on bulk path is good.
80-80: Updated assertion constant LGTM — confirm deprecations are fully applied.
rg search for the literal message in src (excluding tests) returned no matches; confirm production validators were updated to use MAIL_MUST_HAVE_SUBJECT_AND_EITHER_TEXT_OR_HTML or update remaining occurrences.
28-51: Verified — TestHttpClient disambiguates by body and fixtures are present.TestHttpClient groups mocks by method+URL and then matches query params and request payload using Jackson equality (see src/test/java/io/mailtrap/testutils/TestHttpClient.java); all fixtures referenced in BulkEmailsImplTest exist under src/test/resources/api/emails.
Motivation
Implement the
/batchendpoint for the Sending/Testing/Bulk Emails API to keep the SDK up-to-dateChanges
Summary by CodeRabbit
New Features
Bug Fixes / Validation
Documentation
Tests