diff --git a/README.md b/README.md index 4c8682b..88bc8e4 100644 --- a/README.md +++ b/README.md @@ -350,6 +350,7 @@ You can find the [Mailtrap Java API reference](https://mailtrap.github.io/mailtr - [Sending Domains](examples/java/io/mailtrap/examples/sendingdomains/SendingDomainsExample.java) - [Company Info](examples/java/io/mailtrap/examples/companyinfo/CompanyInfoExample.java) - [Suppressions](examples/java/io/mailtrap/examples/suppressions/SuppressionsExample.java) +- [Tracking Opt-outs](examples/java/io/mailtrap/examples/trackingoptouts/TrackingOptOutsExample.java) - [Stats](examples/java/io/mailtrap/examples/sending/StatsExample.java) - [Email Logs](examples/java/io/mailtrap/examples/emaillogs/EmailLogsExample.java) diff --git a/examples/java/io/mailtrap/examples/suppressions/SuppressionsExample.java b/examples/java/io/mailtrap/examples/suppressions/SuppressionsExample.java index 03b76e9..76c1b26 100644 --- a/examples/java/io/mailtrap/examples/suppressions/SuppressionsExample.java +++ b/examples/java/io/mailtrap/examples/suppressions/SuppressionsExample.java @@ -2,11 +2,15 @@ import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.request.suppressions.CreateSuppressionRequest; +import io.mailtrap.model.request.suppressions.SuppressionListFilter; public class SuppressionsExample { private static final String TOKEN = System.getenv("MAILTRAP_API_KEY"); private static final long ACCOUNT_ID = Long.parseLong(System.getenv("MAILTRAP_ACCOUNT_ID")); + private static final long DOMAIN_ID = Long.parseLong(System.getenv("MAILTRAP_DOMAIN_ID")); private static final String EMAIL = "example@mailtrap.io"; public static void main(String[] args) { @@ -15,14 +19,36 @@ public static void main(String[] args) { .build(); final var client = MailtrapClientFactory.createMailtrapClient(config); + final var suppressions = client.sendingApi().suppressions(); - final var searchResponse = client.sendingApi().suppressions() - .search(ACCOUNT_ID, EMAIL); + // Add an email to the suppression list. Type defaults to "manual import" when omitted. + final var created = suppressions.createSuppression( + ACCOUNT_ID, + CreateSuppressionRequest.builder() + .email(EMAIL) + .domainId(DOMAIN_ID) + .sendingStream(SendingStream.TRANSACTIONAL) + .build() + ); + + System.out.println(created); + + final var searchResponse = suppressions.search(ACCOUNT_ID, EMAIL); System.out.println(searchResponse); + // Filter by email and creation time, and page with lastId + System.out.println(suppressions.search( + ACCOUNT_ID, + SuppressionListFilter.builder() + .email(EMAIL) + .startTime("2025-01-01T00:00:00Z") + .endTime("2025-12-31T23:59:59Z") + .build() + )); + if (!searchResponse.isEmpty()) { - final var deletedSuppression = client.sendingApi().suppressions() + final var deletedSuppression = suppressions .deleteSuppression(ACCOUNT_ID, searchResponse.get(0).getId()); System.out.println(deletedSuppression); diff --git a/examples/java/io/mailtrap/examples/trackingoptouts/TrackingOptOutsExample.java b/examples/java/io/mailtrap/examples/trackingoptouts/TrackingOptOutsExample.java new file mode 100644 index 0000000..5e1abd0 --- /dev/null +++ b/examples/java/io/mailtrap/examples/trackingoptouts/TrackingOptOutsExample.java @@ -0,0 +1,56 @@ +package io.mailtrap.examples.trackingoptouts; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.trackingoptouts.CreateTrackingOptOutRequest; +import io.mailtrap.model.request.trackingoptouts.TrackingOptOutListFilter; + +public class TrackingOptOutsExample { + + private static final String TOKEN = System.getenv("MAILTRAP_API_KEY"); + private static final long DOMAIN_ID = Long.parseLong(System.getenv("MAILTRAP_DOMAIN_ID")); + private static final String EMAIL = "tracked@example.com"; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + final var trackingOptOuts = client.sendingApi().trackingOptOuts(); + + // Opt an email out of open and click tracking for a sending domain + final var created = trackingOptOuts.createTrackingOptOut( + CreateTrackingOptOutRequest.builder() + .email(EMAIL) + .domainId(DOMAIN_ID) + .build() + ); + + System.out.println(created); + + // Get tracking opt-outs (up to 1000 per request) + var page = trackingOptOuts.getTrackingOptOuts(null); + System.out.println(page.getData()); + + // Filter by email and creation time + System.out.println(trackingOptOuts.getTrackingOptOuts( + TrackingOptOutListFilter.builder() + .email(EMAIL) + .startTime("2025-01-01T00:00:00Z") + .endTime("2025-12-31T23:59:59Z") + .build() + )); + + // Page through the full list, following the cursor + while (page.getLastId() != null) { + page = trackingOptOuts.getTrackingOptOuts( + TrackingOptOutListFilter.builder().lastId(page.getLastId()).build() + ); + System.out.println(page.getData()); + } + + // Remove an email from the tracking opt-out list. Returns the deleted record. + System.out.println(trackingOptOuts.deleteTrackingOptOut(created.getId())); + } +} diff --git a/src/main/java/io/mailtrap/api/suppressions/Suppressions.java b/src/main/java/io/mailtrap/api/suppressions/Suppressions.java index a3b7b9e..02f682b 100644 --- a/src/main/java/io/mailtrap/api/suppressions/Suppressions.java +++ b/src/main/java/io/mailtrap/api/suppressions/Suppressions.java @@ -1,5 +1,7 @@ package io.mailtrap.api.suppressions; +import io.mailtrap.model.request.suppressions.CreateSuppressionRequest; +import io.mailtrap.model.request.suppressions.SuppressionListFilter; import io.mailtrap.model.response.suppressions.SuppressionsResponse; import java.util.List; @@ -15,6 +17,25 @@ public interface Suppressions { */ List search(long accountId, String email); + /** + * List and search suppressions. The endpoint returns up to 1000 suppressions per request; + * pass the last returned ID as {@code lastId} to fetch the next page. + * + * @param accountId - unique account ID + * @param filter - optional filtering and pagination parameters + * @return a list of suppressions + */ + List search(long accountId, SuppressionListFilter filter); + + /** + * Add an email address to the account's suppression list. + * + * @param accountId - unique account ID + * @param request - request data + * @return the created suppression + */ + SuppressionsResponse createSuppression(long accountId, CreateSuppressionRequest request); + /** * Delete a suppression by ID. Mailtrap will no longer prevent sending to this email unless it's recorded in suppressions again. * diff --git a/src/main/java/io/mailtrap/api/suppressions/SuppressionsImpl.java b/src/main/java/io/mailtrap/api/suppressions/SuppressionsImpl.java index 8800e96..eb1620e 100644 --- a/src/main/java/io/mailtrap/api/suppressions/SuppressionsImpl.java +++ b/src/main/java/io/mailtrap/api/suppressions/SuppressionsImpl.java @@ -4,6 +4,9 @@ import io.mailtrap.api.apiresource.ApiResource; import io.mailtrap.config.MailtrapConfig; import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.suppressions.CreateSuppressionRequest; +import io.mailtrap.model.request.suppressions.SuppressionListFilter; +import io.mailtrap.model.response.suppressions.SuppressionResponse; import io.mailtrap.model.response.suppressions.SuppressionsResponse; import java.net.URLEncoder; @@ -22,7 +25,17 @@ public SuppressionsImpl(final MailtrapConfig config) { @Override public List search(final long accountId, final String email) { - final var queryParams = RequestData.buildQueryParams(entry("email", Optional.ofNullable(email))); + return search(accountId, SuppressionListFilter.builder().email(email).build()); + } + + @Override + public List search(final long accountId, final SuppressionListFilter filter) { + final var queryParams = RequestData.buildQueryParams( + entry("email", Optional.ofNullable(filter).map(SuppressionListFilter::getEmail)), + entry("start_time", Optional.ofNullable(filter).map(SuppressionListFilter::getStartTime)), + entry("end_time", Optional.ofNullable(filter).map(SuppressionListFilter::getEndTime)), + entry("last_id", Optional.ofNullable(filter).map(SuppressionListFilter::getLastId)) + ); return httpClient.getList( @@ -32,6 +45,16 @@ public List search(final long accountId, final String emai ); } + @Override + public SuppressionsResponse createSuppression(final long accountId, final CreateSuppressionRequest request) { + return httpClient.post( + String.format(apiHost + "/api/accounts/%d/suppressions", accountId), + request, + new RequestData(), + SuppressionResponse.class + ).getData(); + } + @Override public SuppressionsResponse deleteSuppression(final long accountId, final String suppressionId) { if (suppressionId == null || suppressionId.isBlank()) { diff --git a/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOuts.java b/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOuts.java new file mode 100644 index 0000000..96544a1 --- /dev/null +++ b/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOuts.java @@ -0,0 +1,37 @@ +package io.mailtrap.api.trackingoptouts; + +import io.mailtrap.model.request.trackingoptouts.CreateTrackingOptOutRequest; +import io.mailtrap.model.request.trackingoptouts.TrackingOptOutListFilter; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOut; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOutListResponse; + +public interface TrackingOptOuts { + + /** + * List email addresses that have opted out of open and click tracking. + * The endpoint returns up to 1000 records per request; pass the previous response's + * {@code lastId} to fetch the next page. + * + * @param filter - optional filtering and pagination parameters + * @return the page of tracking opt-outs and the cursor for the next page + */ + TrackingOptOutListResponse getTrackingOptOuts(TrackingOptOutListFilter filter); + + /** + * Add an email address to the tracking opt-out list for a sending domain. + * + * @param request - request data + * @return the created tracking opt-out + */ + TrackingOptOut createTrackingOptOut(CreateTrackingOptOutRequest request); + + /** + * Remove an email address from the tracking opt-out list so open and click tracking can + * apply again. + * + * @param trackingOptOutId - unique tracking opt-out ID + * @return the attributes of the deleted tracking opt-out + */ + TrackingOptOut deleteTrackingOptOut(String trackingOptOutId); + +} diff --git a/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImpl.java b/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImpl.java new file mode 100644 index 0000000..69d819c --- /dev/null +++ b/src/main/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImpl.java @@ -0,0 +1,69 @@ +package io.mailtrap.api.trackingoptouts; + +import io.mailtrap.Constants; +import io.mailtrap.api.apiresource.ApiResource; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.trackingoptouts.CreateTrackingOptOutRequest; +import io.mailtrap.model.request.trackingoptouts.TrackingOptOutListFilter; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOut; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOutListResponse; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOutResponse; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import static io.mailtrap.http.RequestData.entry; + +public class TrackingOptOutsImpl extends ApiResource implements TrackingOptOuts { + + private static final String BASE_PATH = "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/api/tracking_opt_outs"; + + public TrackingOptOutsImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public TrackingOptOutListResponse getTrackingOptOuts(final TrackingOptOutListFilter filter) { + final var queryParams = RequestData.buildQueryParams( + entry("email", Optional.ofNullable(filter).map(TrackingOptOutListFilter::getEmail)), + entry("start_time", Optional.ofNullable(filter).map(TrackingOptOutListFilter::getStartTime)), + entry("end_time", Optional.ofNullable(filter).map(TrackingOptOutListFilter::getEndTime)), + entry("last_id", Optional.ofNullable(filter).map(TrackingOptOutListFilter::getLastId)) + ); + + return httpClient.get( + apiHost + BASE_PATH, + new RequestData(queryParams), + TrackingOptOutListResponse.class + ); + } + + @Override + public TrackingOptOut createTrackingOptOut(final CreateTrackingOptOutRequest request) { + return httpClient.post( + apiHost + BASE_PATH, + request, + new RequestData(), + TrackingOptOutResponse.class + ).getData(); + } + + @Override + public TrackingOptOut deleteTrackingOptOut(final String trackingOptOutId) { + if (trackingOptOutId == null || trackingOptOutId.isBlank()) { + throw new IllegalArgumentException("trackingOptOutId must not be null or blank"); + } + + return httpClient.delete( + String.format( + apiHost + BASE_PATH + "/%s", + URLEncoder.encode(trackingOptOutId, StandardCharsets.UTF_8) + ), + new RequestData(), + TrackingOptOut.class + ); + } +} diff --git a/src/main/java/io/mailtrap/client/api/MailtrapEmailSendingApi.java b/src/main/java/io/mailtrap/client/api/MailtrapEmailSendingApi.java index 69ee2a3..e7f9e18 100644 --- a/src/main/java/io/mailtrap/client/api/MailtrapEmailSendingApi.java +++ b/src/main/java/io/mailtrap/client/api/MailtrapEmailSendingApi.java @@ -6,6 +6,7 @@ import io.mailtrap.api.sendingemails.SendingEmails; import io.mailtrap.api.stats.Stats; import io.mailtrap.api.suppressions.Suppressions; +import io.mailtrap.api.trackingoptouts.TrackingOptOuts; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; @@ -21,6 +22,7 @@ public class MailtrapEmailSendingApi { private final SendingDomains domains; private final CompanyInfo companyInfo; private final Suppressions suppressions; + private final TrackingOptOuts trackingOptOuts; private final Stats stats; private final EmailLogs emailLogs; } diff --git a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java index 92e008e..a964d9b 100644 --- a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java +++ b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java @@ -30,6 +30,7 @@ import io.mailtrap.api.stats.StatsImpl; import io.mailtrap.api.subaccounts.SubAccountsImpl; import io.mailtrap.api.suppressions.SuppressionsImpl; +import io.mailtrap.api.trackingoptouts.TrackingOptOutsImpl; import io.mailtrap.api.testingemails.TestingEmailsImpl; import io.mailtrap.api.webhooks.WebhooksImpl; import io.mailtrap.client.MailtrapClient; @@ -137,10 +138,12 @@ private static MailtrapEmailSendingApi createSendingApi(final MailtrapConfig con final var domains = new SendingDomainsImpl(config); final var companyInfo = new CompanyInfoImpl(config); final var suppressions = new SuppressionsImpl(config); + final var trackingOptOuts = new TrackingOptOutsImpl(config); final var stats = new StatsImpl(config); final var emailLogs = new EmailLogsImpl(config); - return new MailtrapEmailSendingApi(emails, domains, companyInfo, suppressions, stats, emailLogs); + return new MailtrapEmailSendingApi(emails, domains, companyInfo, suppressions, trackingOptOuts, stats, + emailLogs); } private static MailtrapEmailTestingApi createTestingApi(final MailtrapConfig config) { diff --git a/src/main/java/io/mailtrap/model/request/suppressions/CreateSuppressionRequest.java b/src/main/java/io/mailtrap/model/request/suppressions/CreateSuppressionRequest.java new file mode 100644 index 0000000..e1af9f8 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/suppressions/CreateSuppressionRequest.java @@ -0,0 +1,31 @@ +package io.mailtrap.model.request.suppressions; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.response.suppressions.SuppressionType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateSuppressionRequest extends AbstractModel { + + private String email; + + @JsonProperty("domain_id") + private Long domainId; + + @JsonProperty("sending_stream") + private SendingStream sendingStream; + + /** + * Reason for the suppression. The API defaults to {@code manual import} when omitted. + */ + private SuppressionType type; + +} diff --git a/src/main/java/io/mailtrap/model/request/suppressions/SuppressionListFilter.java b/src/main/java/io/mailtrap/model/request/suppressions/SuppressionListFilter.java new file mode 100644 index 0000000..27a19f9 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/suppressions/SuppressionListFilter.java @@ -0,0 +1,37 @@ +package io.mailtrap.model.request.suppressions; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Filtering and pagination parameters for listing suppressions. All fields are optional; + * {@code null} fields are omitted from the query string. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SuppressionListFilter { + + /** + * Filter by exact email address (case-insensitive). + */ + private String email; + + /** + * Only suppressions created at or after this ISO 8601 timestamp. + */ + private String startTime; + + /** + * Only suppressions created at or before this ISO 8601 timestamp. + */ + private String endTime; + + /** + * Cursor: returns suppressions after this UUID. + */ + private String lastId; +} diff --git a/src/main/java/io/mailtrap/model/request/trackingoptouts/CreateTrackingOptOutRequest.java b/src/main/java/io/mailtrap/model/request/trackingoptouts/CreateTrackingOptOutRequest.java new file mode 100644 index 0000000..c9891df --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/trackingoptouts/CreateTrackingOptOutRequest.java @@ -0,0 +1,19 @@ +package io.mailtrap.model.request.trackingoptouts; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class CreateTrackingOptOutRequest extends AbstractModel { + + private String email; + + @JsonProperty("domain_id") + private Long domainId; + +} diff --git a/src/main/java/io/mailtrap/model/request/trackingoptouts/TrackingOptOutListFilter.java b/src/main/java/io/mailtrap/model/request/trackingoptouts/TrackingOptOutListFilter.java new file mode 100644 index 0000000..ea94671 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/trackingoptouts/TrackingOptOutListFilter.java @@ -0,0 +1,37 @@ +package io.mailtrap.model.request.trackingoptouts; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Filtering and pagination parameters for listing tracking opt-outs. All fields are optional; + * {@code null} fields are omitted from the query string. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TrackingOptOutListFilter { + + /** + * Filter by exact email address (case-insensitive). + */ + private String email; + + /** + * Only opt-outs created at or after this ISO 8601 timestamp. + */ + private String startTime; + + /** + * Only opt-outs created at or before this ISO 8601 timestamp. + */ + private String endTime; + + /** + * Cursor from the previous response's {@code last_id}. + */ + private String lastId; +} diff --git a/src/main/java/io/mailtrap/model/response/suppressions/SuppressionResponse.java b/src/main/java/io/mailtrap/model/response/suppressions/SuppressionResponse.java new file mode 100644 index 0000000..2c24cc6 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/suppressions/SuppressionResponse.java @@ -0,0 +1,13 @@ +package io.mailtrap.model.response.suppressions; + +import lombok.Data; + +/** + * Single suppression wrapped as {@code data}. + */ +@Data +public class SuppressionResponse { + + private SuppressionsResponse data; + +} diff --git a/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOut.java b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOut.java new file mode 100644 index 0000000..e4712a0 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOut.java @@ -0,0 +1,19 @@ +package io.mailtrap.model.response.trackingoptouts; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TrackingOptOut { + + private String id; + + private String email; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("domain_name") + private String domainName; + +} diff --git a/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutListResponse.java b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutListResponse.java new file mode 100644 index 0000000..c31e6d7 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutListResponse.java @@ -0,0 +1,20 @@ +package io.mailtrap.model.response.trackingoptouts; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * Page of tracking opt-outs wrapped as {@code data}, with the cursor for the next page. + * {@code lastId} is null when there are no more pages. + */ +@Data +public class TrackingOptOutListResponse { + + private List data; + + @JsonProperty("last_id") + private String lastId; + +} diff --git a/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutResponse.java b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutResponse.java new file mode 100644 index 0000000..fb21d77 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/trackingoptouts/TrackingOptOutResponse.java @@ -0,0 +1,13 @@ +package io.mailtrap.model.response.trackingoptouts; + +import lombok.Data; + +/** + * Single tracking opt-out wrapped as {@code data}. + */ +@Data +public class TrackingOptOutResponse { + + private TrackingOptOut data; + +} diff --git a/src/test/java/io/mailtrap/api/suppressions/SuppressionsImplTest.java b/src/test/java/io/mailtrap/api/suppressions/SuppressionsImplTest.java index 2802e29..fa502d0 100644 --- a/src/test/java/io/mailtrap/api/suppressions/SuppressionsImplTest.java +++ b/src/test/java/io/mailtrap/api/suppressions/SuppressionsImplTest.java @@ -3,6 +3,9 @@ import io.mailtrap.Constants; import io.mailtrap.config.MailtrapConfig; import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.request.suppressions.CreateSuppressionRequest; +import io.mailtrap.model.request.suppressions.SuppressionListFilter; import io.mailtrap.model.response.suppressions.SuppressionSendingStream; import io.mailtrap.model.response.suppressions.SuppressionType; import io.mailtrap.model.response.suppressions.SuppressionsResponse; @@ -30,6 +33,16 @@ public void init() { "GET", null, "api/suppressions/searchSuppressions.json", Map.of("email", email) ), + DataMock.build( + Constants.GENERAL_HOST + "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/api/accounts/" + accountId + "/suppressions", + "GET", null, "api/suppressions/searchSuppressions.json", + Map.of("email", email, "start_time", "2025-01-01T00:00:00Z", "last_id", suppressionId) + ), + DataMock.build( + Constants.GENERAL_HOST + "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/api/accounts/" + accountId + "/suppressions", + "POST", "api/suppressions/createSuppressionRequest.json", + "api/suppressions/createSuppressionResponse.json" + ), DataMock.build( Constants.GENERAL_HOST + "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/api/accounts/" + accountId + "/suppressions/" + suppressionIdEncoded, "DELETE", null, "api/suppressions/deleteSuppression.json" @@ -55,6 +68,38 @@ void test_search() { assertEquals(SuppressionType.SPAM_COMPLAINT, searchResponse.get(0).getType()); } + @Test + void test_searchWithFilter() { + final List searchResponse = api.search( + accountId, + SuppressionListFilter.builder() + .email(email) + .startTime("2025-01-01T00:00:00Z") + .lastId(suppressionId) + .build() + ); + + assertEquals(1, searchResponse.size()); + assertEquals(email, searchResponse.get(0).getEmail()); + } + + @Test + void test_createSuppression() { + final SuppressionsResponse created = api.createSuppression( + accountId, + CreateSuppressionRequest.builder() + .email("recipient@example.com") + .domainId(12345L) + .sendingStream(SendingStream.TRANSACTIONAL) + .build() + ); + + assertNotNull(created); + assertEquals("recipient@example.com", created.getEmail()); + assertEquals(SuppressionSendingStream.TRANSACTIONAL, created.getSendingStream()); + assertEquals(SuppressionType.MANUAL_IMPORT, created.getType()); + } + @Test void test_deleteSuppression() { final SuppressionsResponse deleted = api.deleteSuppression(accountId, suppressionId); diff --git a/src/test/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImplTest.java b/src/test/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImplTest.java new file mode 100644 index 0000000..017f0a8 --- /dev/null +++ b/src/test/java/io/mailtrap/api/trackingoptouts/TrackingOptOutsImplTest.java @@ -0,0 +1,116 @@ +package io.mailtrap.api.trackingoptouts; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.trackingoptouts.CreateTrackingOptOutRequest; +import io.mailtrap.model.request.trackingoptouts.TrackingOptOutListFilter; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOut; +import io.mailtrap.model.response.trackingoptouts.TrackingOptOutListResponse; +import io.mailtrap.testutils.BaseTest; +import io.mailtrap.testutils.DataMock; +import io.mailtrap.testutils.TestHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class TrackingOptOutsImplTest extends BaseTest { + + private static final String OPT_OUT_ID = "64d71bf3-1276-417b-86e1-8e66f138acfe"; + private static final String BASE_URL = Constants.GENERAL_HOST + "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/api/tracking_opt_outs"; + + private TrackingOptOuts api; + + @BeforeEach + public void init() { + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build( + BASE_URL, + "GET", null, "api/tracking_opt_outs/listTrackingOptOuts.json" + ), + DataMock.build( + BASE_URL, + "GET", null, "api/tracking_opt_outs/listTrackingOptOuts.json", + Map.of("email", "tracked@example.com", "start_time", "2025-01-01T00:00:00Z") + ), + DataMock.build( + BASE_URL, + "POST", "api/tracking_opt_outs/createTrackingOptOutRequest.json", + "api/tracking_opt_outs/createTrackingOptOutResponse.json" + ), + DataMock.build( + BASE_URL + "/" + OPT_OUT_ID, + "DELETE", null, "api/tracking_opt_outs/deleteTrackingOptOut.json" + ) + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).sendingApi().trackingOptOuts(); + } + + @Test + void test_getTrackingOptOuts() { + final TrackingOptOutListResponse response = api.getTrackingOptOuts(null); + + assertNotNull(response); + assertEquals(1, response.getData().size()); + assertNull(response.getLastId()); + assertEquals("tracked@example.com", response.getData().get(0).getEmail()); + assertEquals("example.com", response.getData().get(0).getDomainName()); + assertEquals(OPT_OUT_ID, response.getData().get(0).getId()); + } + + @Test + void test_getTrackingOptOutsWithFilter() { + final TrackingOptOutListResponse response = api.getTrackingOptOuts( + TrackingOptOutListFilter.builder() + .email("tracked@example.com") + .startTime("2025-01-01T00:00:00Z") + .build() + ); + + assertNotNull(response); + assertEquals(1, response.getData().size()); + } + + @Test + void test_createTrackingOptOut() { + final TrackingOptOut response = api.createTrackingOptOut( + CreateTrackingOptOutRequest.builder() + .email("tracked@example.com") + .domainId(12345L) + .build() + ); + + assertNotNull(response); + assertEquals(OPT_OUT_ID, response.getId()); + assertEquals("tracked@example.com", response.getEmail()); + } + + @Test + void test_deleteTrackingOptOut() { + final TrackingOptOut response = api.deleteTrackingOptOut(OPT_OUT_ID); + + assertNotNull(response); + assertEquals(OPT_OUT_ID, response.getId()); + } + + @Test + void test_deleteTrackingOptOutRejectsBlankId() { + assertThrows(IllegalArgumentException.class, () -> api.deleteTrackingOptOut(" ")); + assertThrows(IllegalArgumentException.class, () -> api.deleteTrackingOptOut(null)); + assertDoesNotThrow(() -> api.deleteTrackingOptOut(OPT_OUT_ID)); + } +} diff --git a/src/test/resources/api/suppressions/createSuppressionRequest.json b/src/test/resources/api/suppressions/createSuppressionRequest.json new file mode 100644 index 0000000..e8a8989 --- /dev/null +++ b/src/test/resources/api/suppressions/createSuppressionRequest.json @@ -0,0 +1,5 @@ +{ + "email": "recipient@example.com", + "domain_id": 12345, + "sending_stream": "transactional" +} diff --git a/src/test/resources/api/suppressions/createSuppressionResponse.json b/src/test/resources/api/suppressions/createSuppressionResponse.json new file mode 100644 index 0000000..a462a2b --- /dev/null +++ b/src/test/resources/api/suppressions/createSuppressionResponse.json @@ -0,0 +1,10 @@ +{ + "data": { + "id": "2fe148b8-b019-431f-ab3f-107663fdf868", + "type": "manual import", + "created_at": "2025-09-03T00:40:44.161Z", + "email": "recipient@example.com", + "sending_stream": "transactional", + "domain_name": "example.com" + } +} diff --git a/src/test/resources/api/tracking_opt_outs/createTrackingOptOutRequest.json b/src/test/resources/api/tracking_opt_outs/createTrackingOptOutRequest.json new file mode 100644 index 0000000..d97c6ff --- /dev/null +++ b/src/test/resources/api/tracking_opt_outs/createTrackingOptOutRequest.json @@ -0,0 +1,4 @@ +{ + "email": "tracked@example.com", + "domain_id": 12345 +} diff --git a/src/test/resources/api/tracking_opt_outs/createTrackingOptOutResponse.json b/src/test/resources/api/tracking_opt_outs/createTrackingOptOutResponse.json new file mode 100644 index 0000000..4760cfa --- /dev/null +++ b/src/test/resources/api/tracking_opt_outs/createTrackingOptOutResponse.json @@ -0,0 +1,8 @@ +{ + "data": { + "id": "64d71bf3-1276-417b-86e1-8e66f138acfe", + "email": "tracked@example.com", + "created_at": "2025-01-15T10:30:00Z", + "domain_name": "example.com" + } +} diff --git a/src/test/resources/api/tracking_opt_outs/deleteTrackingOptOut.json b/src/test/resources/api/tracking_opt_outs/deleteTrackingOptOut.json new file mode 100644 index 0000000..5fd7906 --- /dev/null +++ b/src/test/resources/api/tracking_opt_outs/deleteTrackingOptOut.json @@ -0,0 +1,6 @@ +{ + "id": "64d71bf3-1276-417b-86e1-8e66f138acfe", + "email": "tracked@example.com", + "created_at": "2025-01-15T10:30:00Z", + "domain_name": "example.com" +} diff --git a/src/test/resources/api/tracking_opt_outs/listTrackingOptOuts.json b/src/test/resources/api/tracking_opt_outs/listTrackingOptOuts.json new file mode 100644 index 0000000..4189915 --- /dev/null +++ b/src/test/resources/api/tracking_opt_outs/listTrackingOptOuts.json @@ -0,0 +1,11 @@ +{ + "data": [ + { + "id": "64d71bf3-1276-417b-86e1-8e66f138acfe", + "email": "tracked@example.com", + "created_at": "2025-01-15T10:30:00Z", + "domain_name": "example.com" + } + ], + "last_id": null +}