Skip to content

Conversation

@vitalii-t
Copy link
Collaborator

@vitalii-t vitalii-t commented Oct 6, 2025

Motivation

Next steps implementing Mailtrap API - Contact Exports

Changes

  • Added the ContactExports interface and implementation
  • Added request and response models
  • Covered with unit tests
  • Added an example class

Summary by CodeRabbit

  • New Features

    • Added contact export capability: create exports with filters (list ID, subscription status) and retrieve export status; statuses: created, started, finished; responses include id, status, timestamps, and download URL when available.
  • Documentation

    • Added a Java example demonstrating creating a contact export with filters and retrieving its status.
  • Tests

    • Added unit tests, fixtures, and sample responses validating create and get contact export flows.

@coderabbitai
Copy link

coderabbitai bot commented Oct 6, 2025

Walkthrough

Adds a contact-exports feature: new API interface and implementation, request/response models and enums, client wiring updates, an example, unit tests, and JSON fixtures for create/get contact export endpoints.

Changes

Cohort / File(s) Summary
Contacts Exports API
src/main/java/io/mailtrap/api/contactexports/ContactExports.java, src/main/java/io/mailtrap/api/contactexports/ContactExportsImpl.java
New interface and implementation providing createContactExport(long accountId, CreateContactsExportRequest) (POST) and getContactExport(long accountId, long exportId) (GET) at /api/accounts/{accountId}/contacts/exports.
Client Wiring
src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java, src/main/java/io/mailtrap/factory/MailtrapClientFactory.java
Adds contactExports dependency to MailtrapContactsApi and wires ContactExportsImpl in MailtrapClientFactory; constructor signatures updated accordingly.
Request Models
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java, .../ContactExportFilterName.java, .../ContactExportFilterOperator.java, .../ContactExportFilterSubscriptionStatus.java, .../CreateContactsExportRequest.java
New filter/value enums and factory helpers (listIds, subscriptionStatus) plus CreateContactsExportRequest with List<ContactExportFilter>.
Response Models
src/main/java/io/mailtrap/model/response/contactexports/ContactExportResponse.java, .../ContactExportStatus.java
New DTO ContactExportResponse (id, status, created_at, updated_at, url) and ContactExportStatus enum with JSON mapping and factory method.
Example
examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java
New Java example demonstrating building filters, creating a contact export, printing the response, and retrieving it by ID.
Tests & Fixtures
src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java, src/test/java/io/mailtrap/testutils/BaseTest.java, src/test/resources/api/contactexports/*
Adds unit tests for create/get flows, new test constants in BaseTest, and JSON fixtures for request and responses.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Dev as Developer
    participant Factory as MailtrapClientFactory
    participant ContactsApi as MailtrapContactsApi
    participant Exports as ContactExportsImpl
    participant HTTP as Mailtrap HTTP API

    Dev->>Factory: build(config)
    Factory->>ContactsApi: construct(..., ContactExportsImpl)
    ContactsApi-->>Dev: contactsApi

    rect rgba(173,216,230,0.12)
    Dev->>ContactsApi: contactExports().createContactExport(accountId, request)
    ContactsApi->>Exports: createContactExport(accountId, request)
    Exports->>HTTP: POST /api/accounts/{id}/contacts/exports
    HTTP-->>Exports: 201 ContactExportResponse
    Exports-->>ContactsApi: ContactExportResponse
    ContactsApi-->>Dev: ContactExportResponse
    end

    rect rgba(220,248,198,0.12)
    Dev->>ContactsApi: contactExports().getContactExport(accountId, exportId)
    ContactsApi->>Exports: getContactExport(accountId, exportId)
    Exports->>HTTP: GET /api/accounts/{id}/contacts/exports/{exportId}
    HTTP-->>Exports: 200 ContactExportResponse
    Exports-->>ContactsApi: ContactExportResponse
    ContactsApi-->>Dev: ContactExportResponse
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • vittorius
  • mklocek

Poem

I nibble code and dig a route,
Filters set — a tidy boot 🥕,
POST hops forth, GET brings reply,
Exports roll under moonlit sky.
Hop, hop, the data’s spry!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description includes Motivation and Changes sections but omits the required How to test section and does not follow the template’s structure for providing verification steps, and it also lacks the Images and GIFs section defined in the repository template. Please add a “## How to test” section with specific steps or a checklist to verify the Contact Exports API and include or remove the “## Images and GIFs” section as appropriate according to the repository template.
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 concisely states the primary addition of the Contact Exports API, matching the main change in this pull request.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/contact-exports

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de4a519 and d4faba5.

📒 Files selected for processing (1)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java

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.

@vitalii-t vitalii-t requested a review from mklocek October 6, 2025 21:25
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: 2

🧹 Nitpick comments (6)
src/test/resources/api/contactexports/getContactExportResponse.json (1)

1-7: Consider adding a “finished” response case.

Add a second fixture with status "finished" and a non-null url to exercise final-state mapping.

src/test/resources/api/contactexports/createContactExportResponse.json (1)

1-7: Vary creation vs retrieval fixtures to broaden coverage.

Consider using a different status (e.g., "queued" or "finished") here to validate enum and URL handling across states.

src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1)

9-14: Optional: add a builder (with singular) for easier construction.

Improves call-site ergonomics without breaking existing ctor usage.

 package io.mailtrap.model.request.contactexports;
 
 import io.mailtrap.model.AbstractModel;
+import lombok.Builder;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 import java.util.List;
 
 @Getter
 @AllArgsConstructor
+@Builder
 public class CreateContactsExportRequest extends AbstractModel {
 
-    private List<ContactExportFilter> filters;
+    @lombok.Singular
+    private List<ContactExportFilter> filters;
 }
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterName.java (1)

5-9: Remove unnecessary @requiredargsconstructor annotation.

The @RequiredArgsConstructor annotation is redundant here because the enum has no fields. Enums automatically provide constructors for their constants, and Lombok's @RequiredArgsConstructor generates a no‑arg constructor when there are no final fields, which adds no value.

Apply this diff:

-@RequiredArgsConstructor
 public enum ContactExportFilterName {
     subscription_status,
     list_id
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java (1)

6-6: Remove unused import.

The java.util.Arrays import is not used in this file.

Apply this diff:

 import lombok.RequiredArgsConstructor;
 
-import java.util.Arrays;
-
 @Getter
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1)

5-6: Consider using getValue() pattern for filter names.

Lines 22 and 26 use enum.name() to obtain filter name strings ("list_id", "subscription_status"). While this works, it tightly couples the enum constant names to the API contract. If enum constants are renamed, the API contract breaks.

The ContactExportFilterOperator and ContactExportFilterSubscriptionStatus enums use getValue() methods (see lines 22, 26-27 where they're called). Consider applying the same pattern to ContactExportFilterName for consistency and encapsulation:

// In ContactExportFilterName enum
list_id("list_id"),
subscription_status("subscription_status");

private final String value;
// ... constructor and getValue()

Then use:

list_id.getValue()  // instead of list_id.name()

This is optional if the enum names are carefully managed to match API requirements.

Also applies to: 21-27

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b9583d and f9319ae.

📒 Files selected for processing (17)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java (1 hunks)
  • src/main/java/io/mailtrap/api/contactexports/ContactExports.java (1 hunks)
  • src/main/java/io/mailtrap/api/contactexports/ContactExportsImpl.java (1 hunks)
  • src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java (2 hunks)
  • src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (2 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterName.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterOperator.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1 hunks)
  • src/main/java/io/mailtrap/model/response/contactexports/ContactExportResponse.java (1 hunks)
  • src/main/java/io/mailtrap/model/response/contactexports/ContactExportStatus.java (1 hunks)
  • src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (1 hunks)
  • src/test/java/io/mailtrap/testutils/BaseTest.java (1 hunks)
  • src/test/resources/api/contactexports/createContactExportRequest.json (1 hunks)
  • src/test/resources/api/contactexports/createContactExportResponse.json (1 hunks)
  • src/test/resources/api/contactexports/getContactExportResponse.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (3)
src/main/java/io/mailtrap/model/AbstractModel.java (1)
  • AbstractModel (10-29)
src/main/java/io/mailtrap/model/request/sendingdomains/SendingDomainsSetupInstructionsRequest.java (1)
  • AllArgsConstructor (7-13)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1)
  • Getter (8-28)
examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java (3)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
  • MailtrapClientFactory (33-130)
examples/java/io/mailtrap/examples/contacts/ContactsExample.java (2)
  • ContactsExample (14-49)
  • main (23-48)
examples/java/io/mailtrap/examples/contactimports/ContactImportsExample.java (2)
  • ContactImportsExample (11-39)
  • main (19-38)
src/main/java/io/mailtrap/api/contactexports/ContactExportsImpl.java (2)
src/main/java/io/mailtrap/Constants.java (1)
  • Constants (6-15)
src/main/java/io/mailtrap/api/apiresource/ApiResource.java (1)
  • ApiResource (10-32)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterOperator.java (2)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1)
  • Getter (8-28)
src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1)
  • Getter (9-14)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java (2)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1)
  • Getter (8-28)
src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1)
  • Getter (9-14)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (2)
src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java (1)
  • Getter (15-24)
src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1)
  • Getter (9-14)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
src/main/java/io/mailtrap/api/contactexports/ContactExportsImpl.java (1)
  • ContactExportsImpl (10-35)
src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (4)
src/main/java/io/mailtrap/Constants.java (1)
  • Constants (6-15)
src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)
  • MailtrapClientFactory (33-130)
src/test/java/io/mailtrap/testutils/BaseTest.java (1)
  • BaseTest (6-29)
src/test/java/io/mailtrap/testutils/TestHttpClient.java (1)
  • TestHttpClient (19-160)
🔇 Additional comments (16)
src/test/resources/api/contactexports/createContactExportRequest.json (1)

1-16: Fixture shape matches request model.

Values and operators align with ContactExportFilter factories. No issues.

src/test/java/io/mailtrap/testutils/BaseTest.java (1)

21-22: LGTM.

New IDs align with fixtures and improve test readability.

src/main/java/io/mailtrap/factory/MailtrapClientFactory.java (1)

73-81: Wiring looks correct.

ContactExportsImpl is instantiated and passed into MailtrapContactsApi as expected. No issues spotted.

src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java (1)

15-24: Constructor change safe — only the internal factory instantiates this class.
No external new MailtrapContactsApi(...) usages found; binary compatibility is preserved.

src/main/java/io/mailtrap/api/contactexports/ContactExportsImpl.java (2)

18-25: LGTM!

The method correctly constructs the URL and delegates to the HTTP client. The implementation follows the established pattern used in other API classes.


28-34: LGTM!

The method correctly constructs the URL and delegates to the HTTP client. The implementation follows the established pattern used in other API classes.

src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterOperator.java (1)

6-12: LGTM!

The enum is well‑defined with a single EQUAL operator and uses appropriate Lombok annotations for the value field.

src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java (1)

8-15: LGTM!

The enum is well‑defined with appropriate constants and uses Lombok annotations correctly.

src/main/java/io/mailtrap/api/contactexports/ContactExports.java (1)

6-25: LGTM!

The interface is well‑defined with clear method signatures and comprehensive Javadoc. It follows the established pattern used in other API interfaces in the codebase.

src/main/java/io/mailtrap/model/response/contactexports/ContactExportResponse.java (1)

6-21: LGTM!

The response DTO is well‑structured with appropriate Jackson annotations for JSON mapping and uses Lombok's @Data for standard accessors. The implementation follows the established pattern used in other response models.

src/main/java/io/mailtrap/model/response/contactexports/ContactExportStatus.java (1)

7-30: LGTM!

The enum is well‑implemented with proper Jackson annotations for JSON (de)serialization. The fromValue method correctly handles case‑insensitive matching and throws an appropriate exception for unknown values. This follows the established pattern used in the codebase.

src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (3)

1-43: LGTM! Test structure follows established patterns.

The test setup is well-structured and consistent with other API tests in the codebase. The mock configuration correctly maps POST and GET endpoints with appropriate request/response fixtures.


58-64: LGTM! Clean and focused test.

The GET endpoint test is straightforward and correctly validates the response structure.


45-56: Resolved varargs serialization format: The mock fixture’s value is [101], matching the varargs output. No changes needed.

src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (2)

8-19: LGTM! Clean immutable value object design.

The class follows best practices with:

  • Final fields ensuring immutability
  • Private constructor restricting instantiation to factory methods
  • Lombok @Getter for concise accessor generation
  • Generic Object type for value allowing different filter types

21-23: Varargs design allows flexible ID filtering.

The ids() method accepts Long... ids (varargs), meaning:

  • Single value: ids(op, 101L) → creates array [101L]
  • Multiple values: ids(op, 101L, 102L) → creates array [101L, 102L]
  • Array: ids(op, new Long[]{101L, 102L}) → passes array through

This design is appropriate for the method name (plural "ids"), enabling both single and multiple ID filters consistently.

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 (1)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterName.java (1)

3-6: Consider adding Javadoc to document the enum's purpose.

The enum is functional and likely uses snake_case naming intentionally to match the API specification. However, adding Javadoc would improve maintainability by explaining the purpose of this enum and clarifying the unconventional naming convention.

Example:

+/**
+ * Predefined filter names for contact export requests.
+ * <p>
+ * Note: Enum constants use snake_case to match the Mailtrap API field names.
+ */
 public enum ContactExportFilterName {
+    /** Filter by subscription status */
     subscription_status,
+    /** Filter by list ID */
     list_id
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9319ae and 857bf40.

📒 Files selected for processing (5)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterName.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java (1 hunks)
  • src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (1 hunks)
  • src/test/resources/api/contactexports/getContactExportResponse.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/resources/api/contactexports/getContactExportResponse.json
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilterSubscriptionStatus.java
  • src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java

Copy link
Contributor

@mklocek mklocek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good, I'd just use more explicit naming for clarity

@mklocek mklocek requested a review from piobeny October 7, 2025 09:48
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 (2)
src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (2)

55-55: Prefer assertEquals for enum comparison.

While assertSame works for enums due to singleton instances, assertEquals is more idiomatic and explicitly conveys value equality rather than identity checking.

Apply this diff:

-        assertSame(ContactExportStatus.STARTED, contactExportResponse.getStatus());
+        assertEquals(ContactExportStatus.STARTED, contactExportResponse.getStatus());

63-63: Prefer assertEquals for enum comparison.

As with the previous test, assertEquals is more idiomatic than assertSame for comparing enum values.

Apply this diff:

-        assertSame(ContactExportStatus.FINISHED, contactExport.getStatus());
+        assertEquals(ContactExportStatus.FINISHED, contactExport.getStatus());
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 857bf40 and 6508bc3.

📒 Files selected for processing (4)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java (1 hunks)
  • src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (1 hunks)
  • src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (1 hunks)
  • src/test/java/io/mailtrap/testutils/BaseTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/java/io/mailtrap/examples/contactexports/ContactExportsExample.java
  • src/test/java/io/mailtrap/testutils/BaseTest.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (3)
src/main/java/io/mailtrap/Constants.java (1)
  • Constants (6-15)
src/test/java/io/mailtrap/testutils/BaseTest.java (1)
  • BaseTest (6-29)
src/test/java/io/mailtrap/testutils/TestHttpClient.java (1)
  • TestHttpClient (19-160)
src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (2)
src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java (1)
  • Getter (15-24)
src/main/java/io/mailtrap/model/request/contactexports/CreateContactsExportRequest.java (1)
  • Getter (9-14)
🔇 Additional comments (4)
src/test/java/io/mailtrap/api/contactexports/ContactExportsImplTest.java (1)

27-43: LGTM!

The test setup correctly initializes the mock HTTP client with the expected endpoints and obtains the ContactExports API instance through the proper factory chain.

src/main/java/io/mailtrap/model/request/contactexports/ContactExportFilter.java (3)

21-23: LGTM!

The parameter naming clearly indicates these are list IDs, addressing the previous review feedback. The factory method correctly constructs the filter using the enum name and operator value.


25-27: LGTM!

The factory method correctly constructs the subscription status filter, following the same pattern as listIDs() and properly extracting the enum value.


8-19: Ignore AbstractModel extension for nested models. ContactExportFilter is a plain POJO like other nested request types (e.g., Contact, Permission) and is correctly serialized by Jackson without extending AbstractModel.

Likely an incorrect or invalid review comment.

@vitalii-t vitalii-t merged commit b404b6c into main Oct 9, 2025
2 checks passed
@vitalii-t vitalii-t deleted the feature/contact-exports branch October 9, 2025 20:53
@vitalii-t vitalii-t mentioned this pull request Oct 9, 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.

3 participants