Skip to content

Conversation

@vitalii-t
Copy link
Collaborator

@vitalii-t vitalii-t commented Sep 9, 2025

Motivation

Implement the /batch endpoint for the Sending/Testing/Bulk Emails API to keep the SDK up-to-date

Changes

  • Added batch method to API interfaces and implementations
  • Added batchSend method to MailtrapClient
  • Covered with unit tests

Summary by CodeRabbit

  • New Features

    • Batch send: submit multiple emails in one request across standard, bulk, and sandbox modes; structured per-item results returned.
    • Shared base data for batches with per-email overrides; new public client method to trigger batch sends; new response/detail models.
  • Bug Fixes / Validation

    • Stronger content validation and clearer error messages for missing subject/text/html and invalid template combinations.
  • Documentation

    • New Java examples demonstrating batch sending.
  • Tests

    • Expanded tests and fixtures covering batch scenarios and error cases.

@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Java examples: batch sending
examples/java/io/mailtrap/examples/bulk/Batch.java, examples/java/io/mailtrap/examples/sending/Batch.java, examples/java/io/mailtrap/examples/testing/Batch.java
New example programs demonstrating batch email sending for bulk, standard, and testing (inbox) contexts.
Client: public API
src/main/java/io/mailtrap/client/MailtrapClient.java
Add public BatchSendResponse batchSend(MailtrapBatchMail) routing to bulk/sending/testing APIs based on client context.
API interfaces
src/main/java/io/mailtrap/api/bulkemails/BulkEmails.java, src/main/java/io/mailtrap/api/sendingemails/SendingEmails.java, src/main/java/io/mailtrap/api/testingemails/TestingEmails.java
Add batchSend(...) method signatures returning BatchSendResponse.
API implementations
src/main/java/io/mailtrap/api/bulkemails/BulkEmailsImpl.java, src/main/java/io/mailtrap/api/sendingemails/SendingEmailsImpl.java, src/main/java/io/mailtrap/api/testingemails/TestingEmailsImpl.java
Implement batchSend(...): validate batch payload, resolve per-request values, POST to /api/batch or /api/batch/{inboxId}, return BatchSendResponse; single-send now uses renamed validator.
Validation & resource changes
src/main/java/io/mailtrap/api/apiresource/SendApiResource.java, src/main/java/io/mailtrap/exception/InvalidRequestBodyException.java
Rename single-mail validator to validateMailPayload, add validateBatchPayload and assertBatchMailNotNull, introduce validateContentRules(ContentView) and content adapters; add InvalidRequestBodyException(String, Exception) constructor.
Mail validation adapters/views
src/main/java/io/mailtrap/model/mailvalidation/ContentView.java, .../MailContentView.java, .../ResolvedMailContentView.java, .../ResolvedMailView.java
New ContentView interface and adapter classes (MailContentView, ResolvedMailContentView) plus ResolvedMailView that merges base+item values for batch validation.
Request models: batch
src/main/java/io/mailtrap/model/request/emails/BatchEmailBase.java, src/main/java/io/mailtrap/model/request/emails/MailtrapBatchMail.java
New DTOs: BatchEmailBase (base fields) and MailtrapBatchMail (base + validated requests list) with Jackson, validation, and Lombok annotations.
Response models: batch
src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java, src/main/java/io/mailtrap/model/response/emails/BatchSendResponse.java
New DTOs modeling batch send outcome and per-item details.
Tests: API implementations & utilities
src/test/java/io/mailtrap/api/bulkemails/BulkEmailsImplTest.java, src/test/java/io/mailtrap/api/sendingemails/SendingEmailsImplTest.java, src/test/java/io/mailtrap/api/testingemails/TestingEmailsImplTest.java, src/test/java/io/mailtrap/testutils/BaseSendTest.java
Add batchSend tests (success, template, base overrides, invalid inputs, null); add constants and helpers; extend HTTP mocks for /api/batch endpoints.
Tests: client
src/test/java/io/mailtrap/client/MailtrapClientTest.java
Add batchSend client tests for bulk, sandbox, and standard contexts; extend mocks for batch endpoints.
Test fixtures
src/test/resources/api/emails/batchSendRequest.json, .../batchSendRequestFromTemplate.json, .../batchSendWithBaseSubjectRequest.json, .../batchSendWithBaseSubjectAndTextRequest.json, .../batchSendResponse.json
New JSON fixtures for batch requests (plain, template, base variants) and batch response.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • mklocek
  • vittorius

Poem

Thump-thump I bundle notes in a row,
Hopping through code where the batchers go.
One base, many mails, IDs shine anew,
Responses hop back — "22222" — woo! 🥕✉️

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description provides Motivation and a brief Changes list that align with the diff, but it does not follow the repository's required template because the "How to test" section (with checklist and concrete test steps) is missing, leaving reviewers without explicit validation steps. Please update the PR description to match the repository template: add a "How to test" section with checklist items and concrete steps (commands to run unit tests and instructions to exercise the /batch endpoints), and optionally expand the Changes section to call out key files/tests modified so reviewers can validate quickly.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title is a concise, single-sentence summary that directly reflects the primary change in the changeset (adding /batch support for sending/testing/bulk emails) and will be clear to reviewers scanning PR history. It avoids extraneous details and matches the implemented API, client, and test additions.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/batch-send

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 Map import 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 batchSend method declares throws HttpException, InvalidRequestBodyException in its signature, but these exceptions are already runtime exceptions that don't need to be declared. This is inconsistent with the send method 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 Map import 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 batchSend method declares throws HttpException, InvalidRequestBodyException in its signature, but these are runtime exceptions that don't need to be declared. This is inconsistent with the send method 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 fields

Ignore 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 requests

Coverage 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 cases

Keep 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 test

Ensure 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 payloads
src/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

📥 Commits

Reviewing files that changed from the base of the PR and between 7841c8c and 25d33e7.

📒 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 annotate template_uuid and template_variables with @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 instantiates new RequestData() without forwarding any headers from BatchEmailBase#getHeaders() (accessible via mail.getBase().getHeaders()). If batch requests should honor custom headers as send() does, pull mail.getBase().getHeaders() into a RequestData and pass it to httpClient.post().

src/main/java/io/mailtrap/model/response/emails/BatchSendDetails.java (1)

1-18: LGTM!

The implementation of BatchSendDetails data class is correct and follows best practices. The use of Lombok's @Data annotation 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 batchSend method implementation is clean and follows the same context-driven dispatch pattern as the existing send method. 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 to validateRequestBodyOrThrowException.

Copy link

@coderabbitai coderabbitai bot left a 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 isNotBlank so " " 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

📥 Commits

Reviewing files that changed from the base of the PR and between 25d33e7 and f752d2e.

📒 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())) {
Copy link
Contributor

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"
    }
  ]
}

Copy link
Collaborator Author

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

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between f752d2e and 632304f.

📒 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.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between f4a978b and 5ec9329.

📒 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 body

TestHttpClient 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.

@mklocek mklocek self-requested a review September 15, 2025 08:44
@vittorius vittorius merged commit 4d5554e into main Sep 15, 2025
2 checks passed
@vittorius vittorius deleted the feature/batch-send branch September 15, 2025 18:33
@vitalii-t vitalii-t mentioned this pull request Sep 15, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants