diff --git a/.envrc b/.envrc index d217aee..8b808bb 100644 --- a/.envrc +++ b/.envrc @@ -1,2 +1,4 @@ -export GPG_TTY=$(tty) -export JAVA_TOOL_OPTIONS='-Duser.language=en -Duser.country=US' +export MAILTRAP_ACCOUNT_ID="op://Mailtrap Dev/Mailtrap SDK Dev API Key/account_id" +export MAILTRAP_ORGANIZATION_ID="op://Mailtrap Dev/Mailtrap SDK Dev API Key/organization_id" +export MAILTRAP_API_KEY="op://Mailtrap Dev/Mailtrap SDK Dev API Key/account_api_token" +export MAILTRAP_ORGANIZATION_API_KEY="op://Mailtrap Dev/Mailtrap SDK Dev API Key/organization_api_token" diff --git a/.gitignore b/.gitignore index af665ab..869b9b1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ build/ ### Mac OS ### .DS_Store +.idea/ diff --git a/src/main/java/io/mailtrap/api/apitokens/ApiTokens.java b/src/main/java/io/mailtrap/api/apitokens/ApiTokens.java new file mode 100644 index 0000000..597921e --- /dev/null +++ b/src/main/java/io/mailtrap/api/apitokens/ApiTokens.java @@ -0,0 +1,56 @@ +package io.mailtrap.api.apitokens; + +import io.mailtrap.model.request.apitokens.CreateApiTokenRequest; +import io.mailtrap.model.response.apitokens.ApiToken; +import io.mailtrap.model.response.apitokens.ApiTokenWithToken; + +import java.util.List; + +public interface ApiTokens { + + /** + * List all API tokens visible to the current API token. + * + * @param accountId unique account ID + * @return list of API tokens + */ + List getAllApiTokens(long accountId); + + /** + * Create a new API token for the account with the given name and resource permissions. + * The full token value is returned only on creation. + * + * @param accountId unique account ID + * @param request token name and resource permissions + * @return created token, including the full token value + */ + ApiTokenWithToken createApiToken(long accountId, CreateApiTokenRequest request); + + /** + * Get a single API token by id. + * + * @param accountId unique account ID + * @param id API token ID + * @return API token + */ + ApiToken getApiToken(long accountId, long id); + + /** + * Permanently delete an API token. + * + * @param accountId unique account ID + * @param id API token ID + */ + void deleteApiToken(long accountId, long id); + + /** + * Reset an API token. Expires the requested token and creates a new one with the same + * permissions; the new token value is returned only once. + * + * @param accountId unique account ID + * @param id API token ID + * @return new token, including the full token value + */ + ApiTokenWithToken resetApiToken(long accountId, long id); + +} diff --git a/src/main/java/io/mailtrap/api/apitokens/ApiTokensImpl.java b/src/main/java/io/mailtrap/api/apitokens/ApiTokensImpl.java new file mode 100644 index 0000000..9a67eae --- /dev/null +++ b/src/main/java/io/mailtrap/api/apitokens/ApiTokensImpl.java @@ -0,0 +1,67 @@ +package io.mailtrap.api.apitokens; + +import io.mailtrap.Constants; +import io.mailtrap.api.apiresource.ApiResource; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.AbstractModel; +import io.mailtrap.model.request.apitokens.CreateApiTokenRequest; +import io.mailtrap.model.response.apitokens.ApiToken; +import io.mailtrap.model.response.apitokens.ApiTokenWithToken; + +import java.util.List; + +public class ApiTokensImpl extends ApiResource implements ApiTokens { + + public ApiTokensImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public List getAllApiTokens(final long accountId) { + return httpClient.getList( + String.format(apiHost + "/api/accounts/%d/api_tokens", accountId), + new RequestData(), + ApiToken.class + ); + } + + @Override + public ApiTokenWithToken createApiToken(final long accountId, final CreateApiTokenRequest request) { + return httpClient.post( + String.format(apiHost + "/api/accounts/%d/api_tokens", accountId), + request, + new RequestData(), + ApiTokenWithToken.class + ); + } + + @Override + public ApiToken getApiToken(final long accountId, final long id) { + return httpClient.get( + String.format(apiHost + "/api/accounts/%d/api_tokens/%d", accountId, id), + new RequestData(), + ApiToken.class + ); + } + + @Override + public void deleteApiToken(final long accountId, final long id) { + httpClient.delete( + String.format(apiHost + "/api/accounts/%d/api_tokens/%d", accountId, id), + new RequestData(), + Void.class + ); + } + + @Override + public ApiTokenWithToken resetApiToken(final long accountId, final long id) { + return httpClient.post( + String.format(apiHost + "/api/accounts/%d/api_tokens/%d/reset", accountId, id), + (AbstractModel) null, + new RequestData(), + ApiTokenWithToken.class + ); + } +} diff --git a/src/main/java/io/mailtrap/api/contacts/Contacts.java b/src/main/java/io/mailtrap/api/contacts/Contacts.java index 41cba94..7079fb9 100644 --- a/src/main/java/io/mailtrap/api/contacts/Contacts.java +++ b/src/main/java/io/mailtrap/api/contacts/Contacts.java @@ -3,6 +3,7 @@ import io.mailtrap.model.request.contacts.CreateContactRequest; import io.mailtrap.model.request.contacts.UpdateContactRequest; import io.mailtrap.model.response.contacts.CreateContactResponse; +import io.mailtrap.model.response.contacts.GetContactResponse; import io.mailtrap.model.response.contacts.UpdateContactResponse; public interface Contacts { @@ -16,6 +17,15 @@ public interface Contacts { */ CreateContactResponse createContact(long accountId, CreateContactRequest request); + /** + * Get contact using id or email (URL encoded) + * + * @param accountId unique account ID + * @param idOrEmail contact ID or Email + * @return contact details + */ + GetContactResponse getContact(long accountId, String idOrEmail); + /** * Delete contact using id or email (URL encoded) * diff --git a/src/main/java/io/mailtrap/api/contacts/ContactsImpl.java b/src/main/java/io/mailtrap/api/contacts/ContactsImpl.java index 7a448ee..5fbea0f 100644 --- a/src/main/java/io/mailtrap/api/contacts/ContactsImpl.java +++ b/src/main/java/io/mailtrap/api/contacts/ContactsImpl.java @@ -7,6 +7,7 @@ import io.mailtrap.model.request.contacts.CreateContactRequest; import io.mailtrap.model.request.contacts.UpdateContactRequest; import io.mailtrap.model.response.contacts.CreateContactResponse; +import io.mailtrap.model.response.contacts.GetContactResponse; import io.mailtrap.model.response.contacts.UpdateContactResponse; import java.net.URLEncoder; @@ -29,6 +30,16 @@ public CreateContactResponse createContact(final long accountId, final CreateCon ); } + @Override + public GetContactResponse getContact(final long accountId, final String idOrEmail) { + return httpClient.get( + String.format(apiHost + "/api/accounts/%d/contacts/%s", accountId, + URLEncoder.encode(idOrEmail, StandardCharsets.UTF_8)), + new RequestData(), + GetContactResponse.class + ); + } + @Override public void deleteContact(final long accountId, final String idOrEmail) { httpClient.delete( diff --git a/src/main/java/io/mailtrap/api/subaccounts/SubAccounts.java b/src/main/java/io/mailtrap/api/subaccounts/SubAccounts.java new file mode 100644 index 0000000..69e16de --- /dev/null +++ b/src/main/java/io/mailtrap/api/subaccounts/SubAccounts.java @@ -0,0 +1,29 @@ +package io.mailtrap.api.subaccounts; + +import io.mailtrap.model.request.subaccounts.CreateSubAccountRequest; +import io.mailtrap.model.response.subaccounts.SubAccount; + +import java.util.List; + +public interface SubAccounts { + + /** + * Get a list of sub accounts for the specified organization. + * Requires sub account management permissions for the organization. + * + * @param organizationId unique organization ID + * @return list of sub accounts + */ + List getSubAccounts(long organizationId); + + /** + * Create a new sub account under the specified organization. + * Requires sub account management permissions for the organization. + * + * @param organizationId unique organization ID + * @param request sub account data + * @return created sub account + */ + SubAccount createSubAccount(long organizationId, CreateSubAccountRequest request); + +} diff --git a/src/main/java/io/mailtrap/api/subaccounts/SubAccountsImpl.java b/src/main/java/io/mailtrap/api/subaccounts/SubAccountsImpl.java new file mode 100644 index 0000000..dd64994 --- /dev/null +++ b/src/main/java/io/mailtrap/api/subaccounts/SubAccountsImpl.java @@ -0,0 +1,37 @@ +package io.mailtrap.api.subaccounts; + +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.subaccounts.CreateSubAccountRequest; +import io.mailtrap.model.response.subaccounts.SubAccount; + +import java.util.List; + +public class SubAccountsImpl extends ApiResource implements SubAccounts { + + public SubAccountsImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public List getSubAccounts(final long organizationId) { + return httpClient.getList( + String.format(apiHost + "/api/organizations/%d/sub_accounts", organizationId), + new RequestData(), + SubAccount.class + ); + } + + @Override + public SubAccount createSubAccount(final long organizationId, final CreateSubAccountRequest request) { + return httpClient.post( + String.format(apiHost + "/api/organizations/%d/sub_accounts", organizationId), + request, + new RequestData(), + SubAccount.class + ); + } +} diff --git a/src/main/java/io/mailtrap/api/webhooks/Webhooks.java b/src/main/java/io/mailtrap/api/webhooks/Webhooks.java new file mode 100644 index 0000000..c631ae5 --- /dev/null +++ b/src/main/java/io/mailtrap/api/webhooks/Webhooks.java @@ -0,0 +1,57 @@ +package io.mailtrap.api.webhooks; + +import io.mailtrap.model.request.webhooks.CreateWebhookRequest; +import io.mailtrap.model.request.webhooks.UpdateWebhookRequest; +import io.mailtrap.model.response.webhooks.CreateWebhookResponse; +import io.mailtrap.model.response.webhooks.ListWebhooksResponse; +import io.mailtrap.model.response.webhooks.WebhookResponse; + +public interface Webhooks { + + /** + * Create a new webhook for the account. The response includes the + * {@code signing_secret} used to verify webhook signatures (only returned on creation). + * + * @param accountId unique account ID + * @param request webhook configuration + * @return created webhook including the signing secret + */ + CreateWebhookResponse createWebhook(long accountId, CreateWebhookRequest request); + + /** + * List all webhooks for the account. + * + * @param accountId unique account ID + * @return webhooks + */ + ListWebhooksResponse getAllWebhooks(long accountId); + + /** + * Get a single webhook by ID. + * + * @param accountId unique account ID + * @param webhookId webhook ID + * @return webhook details + */ + WebhookResponse getWebhook(long accountId, long webhookId); + + /** + * Update an existing webhook. + * + * @param accountId unique account ID + * @param webhookId webhook ID + * @param request fields to update + * @return updated webhook + */ + WebhookResponse updateWebhook(long accountId, long webhookId, UpdateWebhookRequest request); + + /** + * Permanently delete a webhook. + * + * @param accountId unique account ID + * @param webhookId webhook ID + * @return the deleted webhook + */ + WebhookResponse deleteWebhook(long accountId, long webhookId); + +} diff --git a/src/main/java/io/mailtrap/api/webhooks/WebhooksImpl.java b/src/main/java/io/mailtrap/api/webhooks/WebhooksImpl.java new file mode 100644 index 0000000..981ebc6 --- /dev/null +++ b/src/main/java/io/mailtrap/api/webhooks/WebhooksImpl.java @@ -0,0 +1,66 @@ +package io.mailtrap.api.webhooks; + +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.webhooks.CreateWebhookRequest; +import io.mailtrap.model.request.webhooks.UpdateWebhookRequest; +import io.mailtrap.model.response.webhooks.CreateWebhookResponse; +import io.mailtrap.model.response.webhooks.ListWebhooksResponse; +import io.mailtrap.model.response.webhooks.WebhookResponse; + +public class WebhooksImpl extends ApiResource implements Webhooks { + + public WebhooksImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public CreateWebhookResponse createWebhook(final long accountId, final CreateWebhookRequest request) { + return httpClient.post( + String.format(apiHost + "/api/accounts/%d/webhooks", accountId), + request, + new RequestData(), + CreateWebhookResponse.class + ); + } + + @Override + public ListWebhooksResponse getAllWebhooks(final long accountId) { + return httpClient.get( + String.format(apiHost + "/api/accounts/%d/webhooks", accountId), + new RequestData(), + ListWebhooksResponse.class + ); + } + + @Override + public WebhookResponse getWebhook(final long accountId, final long webhookId) { + return httpClient.get( + String.format(apiHost + "/api/accounts/%d/webhooks/%d", accountId, webhookId), + new RequestData(), + WebhookResponse.class + ); + } + + @Override + public WebhookResponse updateWebhook(final long accountId, final long webhookId, final UpdateWebhookRequest request) { + return httpClient.patch( + String.format(apiHost + "/api/accounts/%d/webhooks/%d", accountId, webhookId), + request, + new RequestData(), + WebhookResponse.class + ); + } + + @Override + public WebhookResponse deleteWebhook(final long accountId, final long webhookId) { + return httpClient.delete( + String.format(apiHost + "/api/accounts/%d/webhooks/%d", accountId, webhookId), + new RequestData(), + WebhookResponse.class + ); + } +} diff --git a/src/main/java/io/mailtrap/client/MailtrapClient.java b/src/main/java/io/mailtrap/client/MailtrapClient.java index 28c75b5..bb9505f 100644 --- a/src/main/java/io/mailtrap/client/MailtrapClient.java +++ b/src/main/java/io/mailtrap/client/MailtrapClient.java @@ -56,6 +56,12 @@ public class MailtrapClient { @Getter private final MailtrapEmailTemplatesApi emailTemplatesApi; + /** + * API for Mailtrap.io Organizations functionality + */ + @Getter + private final MailtrapOrganizationsApi organizationsApi; + /** * Utility class which holds sending context (which API to use: Email Sending API, Bulk Sending API or * Email Testing API, inbox id for Email Testing API) to make it possible to perform send directly from MailtrapClient diff --git a/src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java b/src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java index 67a01e0..592e3ee 100644 --- a/src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java +++ b/src/main/java/io/mailtrap/client/api/MailtrapGeneralApi.java @@ -2,8 +2,10 @@ import io.mailtrap.api.accountaccesses.AccountAccesses; import io.mailtrap.api.accounts.Accounts; +import io.mailtrap.api.apitokens.ApiTokens; import io.mailtrap.api.billing.Billing; import io.mailtrap.api.permissions.Permissions; +import io.mailtrap.api.webhooks.Webhooks; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; @@ -17,6 +19,8 @@ public class MailtrapGeneralApi { private final AccountAccesses accountAccesses; private final Accounts accounts; + private final ApiTokens apiTokens; private final Billing billing; private final Permissions permissions; + private final Webhooks webhooks; } diff --git a/src/main/java/io/mailtrap/client/api/MailtrapOrganizationsApi.java b/src/main/java/io/mailtrap/client/api/MailtrapOrganizationsApi.java new file mode 100644 index 0000000..0c1f297 --- /dev/null +++ b/src/main/java/io/mailtrap/client/api/MailtrapOrganizationsApi.java @@ -0,0 +1,16 @@ +package io.mailtrap.client.api; + +import io.mailtrap.api.subaccounts.SubAccounts; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Represents an API for Mailtrap Organizations functionality + */ +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +public class MailtrapOrganizationsApi { + private final SubAccounts subAccounts; +} diff --git a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java index 2eaf397..9edf03a 100644 --- a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java +++ b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java @@ -3,6 +3,7 @@ import io.mailtrap.MailtrapValidator; import io.mailtrap.api.accountaccesses.AccountAccessesImpl; import io.mailtrap.api.accounts.AccountsImpl; +import io.mailtrap.api.apitokens.ApiTokensImpl; import io.mailtrap.api.attachments.AttachmentsImpl; import io.mailtrap.api.billing.BillingImpl; import io.mailtrap.api.bulkemails.BulkEmailsImpl; @@ -21,8 +22,10 @@ import io.mailtrap.api.sendingdomains.SendingDomainsImpl; import io.mailtrap.api.sendingemails.SendingEmailsImpl; import io.mailtrap.api.stats.StatsImpl; +import io.mailtrap.api.subaccounts.SubAccountsImpl; import io.mailtrap.api.suppressions.SuppressionsImpl; import io.mailtrap.api.testingemails.TestingEmailsImpl; +import io.mailtrap.api.webhooks.WebhooksImpl; import io.mailtrap.client.MailtrapClient; import io.mailtrap.client.api.*; import io.mailtrap.config.MailtrapConfig; @@ -69,11 +72,18 @@ public static MailtrapClient createMailtrapClient(final MailtrapConfig config) { final var generalApi = createGeneralApi(config); final var contactsApi = createContactsApi(config); final var emailTemplatesApi = createEmailTemplatesApi(config); + final var organizationsApi = createOrganizationsApi(config); final var sendingContextHolder = configureSendingContext(config); return new MailtrapClient(sendingApi, testingApi, bulkSendingApi, generalApi, contactsApi, emailTemplatesApi, - sendingContextHolder); + organizationsApi, sendingContextHolder); + } + + private static MailtrapOrganizationsApi createOrganizationsApi(final MailtrapConfig config) { + final var subAccounts = new SubAccountsImpl(config); + + return new MailtrapOrganizationsApi(subAccounts); } private static MailtrapContactsApi createContactsApi(final MailtrapConfig config) { @@ -91,10 +101,12 @@ private static MailtrapContactsApi createContactsApi(final MailtrapConfig config private static MailtrapGeneralApi createGeneralApi(final MailtrapConfig config) { final var accountAccess = new AccountAccessesImpl(config); final var accounts = new AccountsImpl(config); + final var apiTokens = new ApiTokensImpl(config); final var billing = new BillingImpl(config); final var permissions = new PermissionsImpl(config); + final var webhooks = new WebhooksImpl(config); - return new MailtrapGeneralApi(accountAccess, accounts, billing, permissions); + return new MailtrapGeneralApi(accountAccess, accounts, apiTokens, billing, permissions, webhooks); } private static MailtrapEmailSendingApi createSendingApi(final MailtrapConfig config) { diff --git a/src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java b/src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java index f653d6e..c39d128 100644 --- a/src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java +++ b/src/main/java/io/mailtrap/http/impl/DefaultMailtrapHttpClient.java @@ -140,6 +140,9 @@ private T handleResponseInternal(final HttpResponse response, final final int statusCode = response.statusCode(); if (statusCode >= 200 && statusCode < 300) { + if (response.body().isEmpty()) { + return null; + } if (responseClassType != null) { if (String.class.equals(responseClassType)) { return responseClassType.cast(response.body()); diff --git a/src/main/java/io/mailtrap/model/ResourceType.java b/src/main/java/io/mailtrap/model/ResourceType.java index 6bfac4a..780e752 100644 --- a/src/main/java/io/mailtrap/model/ResourceType.java +++ b/src/main/java/io/mailtrap/model/ResourceType.java @@ -9,7 +9,8 @@ public enum ResourceType { PROJECT("project"), INBOX("inbox"), SENDING_DOMAIN("sending_domain"), - EMAIL_CAMPAIGN_PERMISSION_SCOPE("email_campaign_permission_scope"); + EMAIL_CAMPAIGN_PERMISSION_SCOPE("email_campaign_permission_scope"), + EMAIL_TEMPLATE_PERMISSION_SCOPE("email_template_permission_scope"); private final String value; diff --git a/src/main/java/io/mailtrap/model/WebhookEventType.java b/src/main/java/io/mailtrap/model/WebhookEventType.java new file mode 100644 index 0000000..d1e8f70 --- /dev/null +++ b/src/main/java/io/mailtrap/model/WebhookEventType.java @@ -0,0 +1,42 @@ +package io.mailtrap.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum WebhookEventType { + DELIVERY("delivery"), + SOFT_BOUNCE("soft_bounce"), + BOUNCE("bounce"), + SUSPENSION("suspension"), + UNSUBSCRIBE("unsubscribe"), + OPEN("open"), + SPAM_COMPLAINT("spam_complaint"), + CLICK("click"), + REJECT("reject"); + + private final String value; + + WebhookEventType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } + + @JsonCreator + public static WebhookEventType fromValue(String value) { + for (WebhookEventType type : WebhookEventType.values()) { + if (type.value.equalsIgnoreCase(value)) { + return type; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/WebhookPayloadFormat.java b/src/main/java/io/mailtrap/model/WebhookPayloadFormat.java new file mode 100644 index 0000000..7f9d563 --- /dev/null +++ b/src/main/java/io/mailtrap/model/WebhookPayloadFormat.java @@ -0,0 +1,35 @@ +package io.mailtrap.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum WebhookPayloadFormat { + JSON("json"), + JSONLINES("jsonlines"); + + private final String value; + + WebhookPayloadFormat(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } + + @JsonCreator + public static WebhookPayloadFormat fromValue(String value) { + for (WebhookPayloadFormat format : WebhookPayloadFormat.values()) { + if (format.value.equalsIgnoreCase(value)) { + return format; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/WebhookType.java b/src/main/java/io/mailtrap/model/WebhookType.java new file mode 100644 index 0000000..d6a6a8b --- /dev/null +++ b/src/main/java/io/mailtrap/model/WebhookType.java @@ -0,0 +1,36 @@ +package io.mailtrap.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum WebhookType { + EMAIL_SENDING("email_sending"), + AUDIT_LOG("audit_log"), + CAMPAIGNS("campaigns"); + + private final String value; + + WebhookType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } + + @JsonCreator + public static WebhookType fromValue(String value) { + for (WebhookType type : WebhookType.values()) { + if (type.value.equalsIgnoreCase(value)) { + return type; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/request/apitokens/ApiTokenResource.java b/src/main/java/io/mailtrap/model/request/apitokens/ApiTokenResource.java new file mode 100644 index 0000000..a3b462d --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/apitokens/ApiTokenResource.java @@ -0,0 +1,24 @@ +package io.mailtrap.model.request.apitokens; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AccessLevel; +import io.mailtrap.model.ResourceType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ApiTokenResource { + + @JsonProperty("resource_type") + private ResourceType resourceType; + + @JsonProperty("resource_id") + private Long resourceId; + + @JsonProperty("access_level") + private AccessLevel accessLevel; + +} diff --git a/src/main/java/io/mailtrap/model/request/apitokens/CreateApiTokenRequest.java b/src/main/java/io/mailtrap/model/request/apitokens/CreateApiTokenRequest.java new file mode 100644 index 0000000..e2c9a7f --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/apitokens/CreateApiTokenRequest.java @@ -0,0 +1,21 @@ +package io.mailtrap.model.request.apitokens; + +import io.mailtrap.model.AbstractModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CreateApiTokenRequest extends AbstractModel { + + private String name; + + private List resources; + +} diff --git a/src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java b/src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java new file mode 100644 index 0000000..ff21fde --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/subaccounts/CreateSubAccountRequest.java @@ -0,0 +1,17 @@ +package io.mailtrap.model.request.subaccounts; + +import io.mailtrap.model.AbstractModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CreateSubAccountRequest extends AbstractModel { + + private SubAccountInput account; + +} diff --git a/src/main/java/io/mailtrap/model/request/subaccounts/SubAccountInput.java b/src/main/java/io/mailtrap/model/request/subaccounts/SubAccountInput.java new file mode 100644 index 0000000..9322a17 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/subaccounts/SubAccountInput.java @@ -0,0 +1,14 @@ +package io.mailtrap.model.request.subaccounts; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SubAccountInput { + + private String name; + +} diff --git a/src/main/java/io/mailtrap/model/request/webhooks/CreateWebhookRequest.java b/src/main/java/io/mailtrap/model/request/webhooks/CreateWebhookRequest.java new file mode 100644 index 0000000..f67613b --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/webhooks/CreateWebhookRequest.java @@ -0,0 +1,17 @@ +package io.mailtrap.model.request.webhooks; + +import io.mailtrap.model.AbstractModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CreateWebhookRequest extends AbstractModel { + + private WebhookInput webhook; + +} diff --git a/src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java b/src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java new file mode 100644 index 0000000..9056d0d --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/webhooks/UpdateWebhookRequest.java @@ -0,0 +1,17 @@ +package io.mailtrap.model.request.webhooks; + +import io.mailtrap.model.AbstractModel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class UpdateWebhookRequest extends AbstractModel { + + private WebhookInput webhook; + +} diff --git a/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java b/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java new file mode 100644 index 0000000..f6c339e --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java @@ -0,0 +1,42 @@ +package io.mailtrap.model.request.webhooks; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.WebhookEventType; +import io.mailtrap.model.WebhookPayloadFormat; +import io.mailtrap.model.WebhookType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class WebhookInput { + + private String url; + + @JsonProperty("webhook_type") + private WebhookType webhookType; + + private Boolean active; + + @JsonProperty("payload_format") + private WebhookPayloadFormat payloadFormat; + + @JsonProperty("sending_stream") + private SendingStream sendingStream; + + @JsonProperty("event_types") + private List eventTypes; + + @JsonProperty("domain_id") + private Long domainId; + +} diff --git a/src/main/java/io/mailtrap/model/response/apitokens/ApiToken.java b/src/main/java/io/mailtrap/model/response/apitokens/ApiToken.java new file mode 100644 index 0000000..84b3443 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/apitokens/ApiToken.java @@ -0,0 +1,27 @@ +package io.mailtrap.model.response.apitokens; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.OffsetDateTime; +import java.util.List; + +@Data +public class ApiToken { + + private long id; + + private String name; + + @JsonProperty("last_4_digits") + private String last4Digits; + + @JsonProperty("created_by") + private String createdBy; + + @JsonProperty("expires_at") + private OffsetDateTime expiresAt; + + private List resources; + +} diff --git a/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenResource.java b/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenResource.java new file mode 100644 index 0000000..4894160 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenResource.java @@ -0,0 +1,20 @@ +package io.mailtrap.model.response.apitokens; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AccessLevel; +import io.mailtrap.model.ResourceType; +import lombok.Data; + +@Data +public class ApiTokenResource { + + @JsonProperty("resource_type") + private ResourceType resourceType; + + @JsonProperty("resource_id") + private Long resourceId; + + @JsonProperty("access_level") + private AccessLevel accessLevel; + +} diff --git a/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java b/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java new file mode 100644 index 0000000..bdefd20 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/apitokens/ApiTokenWithToken.java @@ -0,0 +1,12 @@ +package io.mailtrap.model.response.apitokens; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ApiTokenWithToken extends ApiToken { + + private String token; + +} diff --git a/src/main/java/io/mailtrap/model/response/contacts/GetContactData.java b/src/main/java/io/mailtrap/model/response/contacts/GetContactData.java new file mode 100644 index 0000000..0da8a91 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/contacts/GetContactData.java @@ -0,0 +1,28 @@ +package io.mailtrap.model.response.contacts; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import lombok.Data; + +@Data +public class GetContactData { + + private String id; + + private String email; + + private Map fields; + + @JsonProperty("list_ids") + private List listIds; + + private ContactStatus status; + + @JsonProperty("created_at") + private Long createdAt; + + @JsonProperty("updated_at") + private Long updatedAt; + +} diff --git a/src/main/java/io/mailtrap/model/response/contacts/GetContactResponse.java b/src/main/java/io/mailtrap/model/response/contacts/GetContactResponse.java new file mode 100644 index 0000000..a6491cd --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/contacts/GetContactResponse.java @@ -0,0 +1,10 @@ +package io.mailtrap.model.response.contacts; + +import lombok.Data; + +@Data +public class GetContactResponse { + + private GetContactData data; + +} diff --git a/src/main/java/io/mailtrap/model/response/subaccounts/SubAccount.java b/src/main/java/io/mailtrap/model/response/subaccounts/SubAccount.java new file mode 100644 index 0000000..9f57d50 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/subaccounts/SubAccount.java @@ -0,0 +1,12 @@ +package io.mailtrap.model.response.subaccounts; + +import lombok.Data; + +@Data +public class SubAccount { + + private long id; + + private String name; + +} diff --git a/src/main/java/io/mailtrap/model/response/webhooks/CreateWebhookResponse.java b/src/main/java/io/mailtrap/model/response/webhooks/CreateWebhookResponse.java new file mode 100644 index 0000000..ff509b9 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/webhooks/CreateWebhookResponse.java @@ -0,0 +1,10 @@ +package io.mailtrap.model.response.webhooks; + +import lombok.Data; + +@Data +public class CreateWebhookResponse { + + private WebhookWithSecret data; + +} diff --git a/src/main/java/io/mailtrap/model/response/webhooks/ListWebhooksResponse.java b/src/main/java/io/mailtrap/model/response/webhooks/ListWebhooksResponse.java new file mode 100644 index 0000000..6fcab85 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/webhooks/ListWebhooksResponse.java @@ -0,0 +1,12 @@ +package io.mailtrap.model.response.webhooks; + +import lombok.Data; + +import java.util.List; + +@Data +public class ListWebhooksResponse { + + private List data; + +} diff --git a/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java b/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java new file mode 100644 index 0000000..e29b1d3 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java @@ -0,0 +1,36 @@ +package io.mailtrap.model.response.webhooks; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.WebhookEventType; +import io.mailtrap.model.WebhookPayloadFormat; +import io.mailtrap.model.WebhookType; +import lombok.Data; + +import java.util.List; + +@Data +public class Webhook { + + private long id; + + private String url; + + private boolean active; + + @JsonProperty("webhook_type") + private WebhookType webhookType; + + @JsonProperty("payload_format") + private WebhookPayloadFormat payloadFormat; + + @JsonProperty("sending_stream") + private SendingStream sendingStream; + + @JsonProperty("domain_id") + private Long domainId; + + @JsonProperty("event_types") + private List eventTypes; + +} diff --git a/src/main/java/io/mailtrap/model/response/webhooks/WebhookResponse.java b/src/main/java/io/mailtrap/model/response/webhooks/WebhookResponse.java new file mode 100644 index 0000000..7062b60 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/webhooks/WebhookResponse.java @@ -0,0 +1,10 @@ +package io.mailtrap.model.response.webhooks; + +import lombok.Data; + +@Data +public class WebhookResponse { + + private Webhook data; + +} diff --git a/src/main/java/io/mailtrap/model/response/webhooks/WebhookWithSecret.java b/src/main/java/io/mailtrap/model/response/webhooks/WebhookWithSecret.java new file mode 100644 index 0000000..39183fc --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/webhooks/WebhookWithSecret.java @@ -0,0 +1,14 @@ +package io.mailtrap.model.response.webhooks; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WebhookWithSecret extends Webhook { + + @JsonProperty("signing_secret") + private String signingSecret; + +} diff --git a/src/test/java/io/mailtrap/api/apitokens/ApiTokensImplTest.java b/src/test/java/io/mailtrap/api/apitokens/ApiTokensImplTest.java new file mode 100644 index 0000000..ffff7cf --- /dev/null +++ b/src/test/java/io/mailtrap/api/apitokens/ApiTokensImplTest.java @@ -0,0 +1,111 @@ +package io.mailtrap.api.apitokens; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.AccessLevel; +import io.mailtrap.model.ResourceType; +import io.mailtrap.model.request.apitokens.ApiTokenResource; +import io.mailtrap.model.request.apitokens.CreateApiTokenRequest; +import io.mailtrap.model.response.apitokens.ApiToken; +import io.mailtrap.model.response.apitokens.ApiTokenWithToken; +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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +class ApiTokensImplTest extends BaseTest { + + private final long apiTokenId = 12345L; + + private ApiTokens api; + + @BeforeEach + public void init() { + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/api_tokens", + "GET", null, "api/apitokens/listApiTokensResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/api_tokens", + "POST", "api/apitokens/createApiTokenRequest.json", "api/apitokens/createApiTokenResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/api_tokens/" + apiTokenId, + "GET", null, "api/apitokens/getApiTokenResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/api_tokens/" + apiTokenId, + "DELETE", null, null), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/api_tokens/" + apiTokenId + "/reset", + "POST", null, "api/apitokens/resetApiTokenResponse.json") + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).generalApi().apiTokens(); + } + + @Test + void test_getAllApiTokens() { + final List tokens = api.getAllApiTokens(accountId); + + assertEquals(2, tokens.size()); + assertEquals(12345L, tokens.get(0).getId()); + assertEquals("x7k9", tokens.get(0).getLast4Digits()); + assertEquals(ResourceType.ACCOUNT, tokens.get(0).getResources().get(0).getResourceType()); + assertEquals(AccessLevel.ADMIN, tokens.get(0).getResources().get(0).getAccessLevel()); + assertEquals(ResourceType.INBOX, tokens.get(1).getResources().get(0).getResourceType()); + assertEquals(AccessLevel.VIEWER, tokens.get(1).getResources().get(0).getAccessLevel()); + assertNotNull(tokens.get(1).getExpiresAt()); + } + + @Test + void test_createApiToken() { + final CreateApiTokenRequest request = new CreateApiTokenRequest( + "Scratch test token", + List.of(new ApiTokenResource(ResourceType.ACCOUNT, accountId, AccessLevel.ADMIN)) + ); + + final ApiTokenWithToken response = api.createApiToken(accountId, request); + + assertNotNull(response); + assertEquals(12345L, response.getId()); + assertEquals("a1b2c3d4e5f6", response.getToken()); + assertEquals(AccessLevel.ADMIN, response.getResources().get(0).getAccessLevel()); + assertNull(response.getExpiresAt()); + } + + @Test + void test_getApiToken() { + final ApiToken token = api.getApiToken(accountId, apiTokenId); + + assertNotNull(token); + assertEquals(apiTokenId, token.getId()); + assertEquals("My API Token", token.getName()); + assertEquals(ResourceType.ACCOUNT, token.getResources().get(0).getResourceType()); + } + + @Test + void test_deleteApiToken() { + api.deleteApiToken(accountId, apiTokenId); + } + + @Test + void test_resetApiToken() { + final ApiTokenWithToken response = api.resetApiToken(accountId, apiTokenId); + + assertNotNull(response); + assertEquals(apiTokenId, response.getId()); + assertEquals("newtoken123", response.getToken()); + assertEquals("n3w0", response.getLast4Digits()); + } +} diff --git a/src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java b/src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java index 2fb4061..215d4d0 100644 --- a/src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java +++ b/src/test/java/io/mailtrap/api/contacts/ContactsImplTest.java @@ -10,6 +10,7 @@ import io.mailtrap.model.response.contacts.ContactAction; import io.mailtrap.model.response.contacts.ContactStatus; import io.mailtrap.model.response.contacts.CreateContactResponse; +import io.mailtrap.model.response.contacts.GetContactResponse; import io.mailtrap.model.response.contacts.UpdateContactResponse; import io.mailtrap.testutils.BaseTest; import io.mailtrap.testutils.DataMock; @@ -32,6 +33,12 @@ public void init() { DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts", "POST", "api/contacts/createContactRequest.json", "api/contacts/createContactResponse.json"), + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/" + emailEncoded, + "GET", null, "api/contacts/getContactResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/" + contactUUIDEncoded, + "GET", null, "api/contacts/getContactResponse.json"), + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/" + emailEncoded, "DELETE", null, null), @@ -63,6 +70,30 @@ void test_createContact() { assertEquals(ContactStatus.SUBSCRIBED, response.getStatus()); } + @Test + void test_getContact_byEmail() { + final GetContactResponse response = api.getContact(accountId, email); + + assertNotNull(response); + assertNotNull(response.getData()); + assertEquals("018dd5e3-f6d2-7c00-8f9b-e5c3f2d8a132", response.getData().getId()); + assertEquals("john.smith@example.com", response.getData().getEmail()); + assertEquals(ContactStatus.SUBSCRIBED, response.getData().getStatus()); + assertEquals(List.of(1L, 2L, 3L), response.getData().getListIds()); + assertEquals(1742820600230L, response.getData().getCreatedAt()); + assertEquals(1742820600230L, response.getData().getUpdatedAt()); + } + + @Test + void test_getContact_byId() { + final GetContactResponse response = api.getContact(accountId, contactUUID); + + assertNotNull(response); + assertNotNull(response.getData()); + assertEquals(contactUUID, response.getData().getId()); + assertEquals(ContactStatus.SUBSCRIBED, response.getData().getStatus()); + } + @Test void test_deleteContact_byEmail() { api.deleteContact(accountId, email); diff --git a/src/test/java/io/mailtrap/api/subaccounts/SubAccountsImplTest.java b/src/test/java/io/mailtrap/api/subaccounts/SubAccountsImplTest.java new file mode 100644 index 0000000..39b685a --- /dev/null +++ b/src/test/java/io/mailtrap/api/subaccounts/SubAccountsImplTest.java @@ -0,0 +1,64 @@ +package io.mailtrap.api.subaccounts; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.subaccounts.CreateSubAccountRequest; +import io.mailtrap.model.request.subaccounts.SubAccountInput; +import io.mailtrap.model.response.subaccounts.SubAccount; +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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class SubAccountsImplTest extends BaseTest { + + private final long organizationId = 1001L; + + private SubAccounts api; + + @BeforeEach + public void init() { + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(Constants.GENERAL_HOST + "/api/organizations/" + organizationId + "/sub_accounts", + "GET", null, "api/subaccounts/getSubAccountsResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/organizations/" + organizationId + "/sub_accounts", + "POST", "api/subaccounts/createSubAccountRequest.json", "api/subaccounts/createSubAccountResponse.json") + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).organizationsApi().subAccounts(); + } + + @Test + void test_getSubAccounts() { + final List subAccounts = api.getSubAccounts(organizationId); + + assertEquals(2, subAccounts.size()); + assertEquals(12345L, subAccounts.get(0).getId()); + assertEquals("Development Team Account", subAccounts.get(0).getName()); + assertEquals(12346L, subAccounts.get(1).getId()); + } + + @Test + void test_createSubAccount() { + final CreateSubAccountRequest request = new CreateSubAccountRequest(new SubAccountInput("New Team Account")); + + final SubAccount response = api.createSubAccount(organizationId, request); + + assertNotNull(response); + assertEquals(12347L, response.getId()); + assertEquals("New Team Account", response.getName()); + } +} \ No newline at end of file diff --git a/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java b/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java new file mode 100644 index 0000000..a9f3d7d --- /dev/null +++ b/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java @@ -0,0 +1,127 @@ +package io.mailtrap.api.webhooks; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.SendingStream; +import io.mailtrap.model.WebhookEventType; +import io.mailtrap.model.WebhookPayloadFormat; +import io.mailtrap.model.WebhookType; +import io.mailtrap.model.request.webhooks.CreateWebhookRequest; +import io.mailtrap.model.request.webhooks.UpdateWebhookRequest; +import io.mailtrap.model.request.webhooks.WebhookInput; +import io.mailtrap.model.response.webhooks.CreateWebhookResponse; +import io.mailtrap.model.response.webhooks.ListWebhooksResponse; +import io.mailtrap.model.response.webhooks.WebhookResponse; +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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class WebhooksImplTest extends BaseTest { + + private final long webhookId = 1L; + + private Webhooks api; + + @BeforeEach + public void init() { + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks", + "POST", "api/webhooks/createWebhookRequest.json", "api/webhooks/createWebhookResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks", + "GET", null, "api/webhooks/listWebhooksResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks/" + webhookId, + "GET", null, "api/webhooks/getWebhookResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks/" + webhookId, + "PATCH", "api/webhooks/updateWebhookRequest.json", "api/webhooks/updateWebhookResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks/" + webhookId, + "DELETE", null, "api/webhooks/deleteWebhookResponse.json") + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).generalApi().webhooks(); + } + + @Test + void test_createWebhook() { + final CreateWebhookRequest request = new CreateWebhookRequest( + WebhookInput.builder() + .url("https://example.com/mailtrap/webhooks") + .webhookType(WebhookType.EMAIL_SENDING) + .payloadFormat(WebhookPayloadFormat.JSON) + .sendingStream(SendingStream.TRANSACTIONAL) + .eventTypes(List.of(WebhookEventType.DELIVERY, WebhookEventType.BOUNCE)) + .domainId(435L) + .build() + ); + + final CreateWebhookResponse response = api.createWebhook(accountId, request); + + assertNotNull(response); + assertNotNull(response.getData()); + assertEquals(webhookId, response.getData().getId()); + assertEquals("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", response.getData().getSigningSecret()); + assertEquals(WebhookType.EMAIL_SENDING, response.getData().getWebhookType()); + assertEquals(SendingStream.TRANSACTIONAL, response.getData().getSendingStream()); + assertTrue(response.getData().isActive()); + } + + @Test + void test_getAllWebhooks() { + final ListWebhooksResponse response = api.getAllWebhooks(accountId); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals(WebhookType.EMAIL_SENDING, response.getData().get(0).getWebhookType()); + assertEquals(WebhookType.AUDIT_LOG, response.getData().get(1).getWebhookType()); + } + + @Test + void test_getWebhook() { + final WebhookResponse response = api.getWebhook(accountId, webhookId); + + assertNotNull(response); + assertEquals(webhookId, response.getData().getId()); + assertEquals(435L, response.getData().getDomainId()); + assertEquals(List.of(WebhookEventType.DELIVERY, WebhookEventType.BOUNCE), response.getData().getEventTypes()); + } + + @Test + void test_updateWebhook() { + final UpdateWebhookRequest request = new UpdateWebhookRequest( + WebhookInput.builder().active(false).build() + ); + + final WebhookResponse response = api.updateWebhook(accountId, webhookId, request); + + assertNotNull(response); + assertEquals(webhookId, response.getData().getId()); + assertFalse(response.getData().isActive()); + } + + @Test + void test_deleteWebhook() { + final WebhookResponse response = api.deleteWebhook(accountId, webhookId); + + assertNotNull(response); + assertEquals(webhookId, response.getData().getId()); + assertEquals(WebhookType.AUDIT_LOG, response.getData().getWebhookType()); + } +} diff --git a/src/test/resources/api/apitokens/createApiTokenRequest.json b/src/test/resources/api/apitokens/createApiTokenRequest.json new file mode 100644 index 0000000..853392b --- /dev/null +++ b/src/test/resources/api/apitokens/createApiTokenRequest.json @@ -0,0 +1,10 @@ +{ + "name": "Scratch test token", + "resources": [ + { + "resource_type": "account", + "resource_id": 1, + "access_level": "admin" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/api/apitokens/createApiTokenResponse.json b/src/test/resources/api/apitokens/createApiTokenResponse.json new file mode 100644 index 0000000..e053738 --- /dev/null +++ b/src/test/resources/api/apitokens/createApiTokenResponse.json @@ -0,0 +1,15 @@ +{ + "id": 12345, + "name": "Scratch test token", + "last_4_digits": "x7k9", + "created_by": "user@example.com", + "expires_at": null, + "resources": [ + { + "resource_type": "account", + "resource_id": 1, + "access_level": 100 + } + ], + "token": "a1b2c3d4e5f6" +} diff --git a/src/test/resources/api/apitokens/getApiTokenResponse.json b/src/test/resources/api/apitokens/getApiTokenResponse.json new file mode 100644 index 0000000..554b494 --- /dev/null +++ b/src/test/resources/api/apitokens/getApiTokenResponse.json @@ -0,0 +1,14 @@ +{ + "id": 12345, + "name": "My API Token", + "last_4_digits": "x7k9", + "created_by": "user@example.com", + "expires_at": null, + "resources": [ + { + "resource_type": "account", + "resource_id": 1, + "access_level": 100 + } + ] +} diff --git a/src/test/resources/api/apitokens/listApiTokensResponse.json b/src/test/resources/api/apitokens/listApiTokensResponse.json new file mode 100644 index 0000000..a4a4f90 --- /dev/null +++ b/src/test/resources/api/apitokens/listApiTokensResponse.json @@ -0,0 +1,30 @@ +[ + { + "id": 12345, + "name": "My API Token", + "last_4_digits": "x7k9", + "created_by": "user@example.com", + "expires_at": null, + "resources": [ + { + "resource_type": "account", + "resource_id": 1, + "access_level": 100 + } + ] + }, + { + "id": 67890, + "name": "Inbox-only token", + "last_4_digits": "ab12", + "created_by": "ci-bot", + "expires_at": "2027-01-01T00:00:00Z", + "resources": [ + { + "resource_type": "inbox", + "resource_id": 2, + "access_level": 10 + } + ] + } +] diff --git a/src/test/resources/api/apitokens/resetApiTokenResponse.json b/src/test/resources/api/apitokens/resetApiTokenResponse.json new file mode 100644 index 0000000..65d649e --- /dev/null +++ b/src/test/resources/api/apitokens/resetApiTokenResponse.json @@ -0,0 +1,15 @@ +{ + "id": 12345, + "name": "My API Token", + "last_4_digits": "n3w0", + "created_by": "user@example.com", + "expires_at": null, + "resources": [ + { + "resource_type": "account", + "resource_id": 1, + "access_level": 100 + } + ], + "token": "newtoken123" +} diff --git a/src/test/resources/api/contacts/getContactResponse.json b/src/test/resources/api/contacts/getContactResponse.json new file mode 100644 index 0000000..b6dd3d8 --- /dev/null +++ b/src/test/resources/api/contacts/getContactResponse.json @@ -0,0 +1,18 @@ +{ + "data": { + "id": "018dd5e3-f6d2-7c00-8f9b-e5c3f2d8a132", + "status": "subscribed", + "email": "john.smith@example.com", + "fields": { + "first_name": "John", + "last_name": "Smith" + }, + "list_ids": [ + 1, + 2, + 3 + ], + "created_at": 1742820600230, + "updated_at": 1742820600230 + } +} diff --git a/src/test/resources/api/subaccounts/createSubAccountRequest.json b/src/test/resources/api/subaccounts/createSubAccountRequest.json new file mode 100644 index 0000000..b3c23df --- /dev/null +++ b/src/test/resources/api/subaccounts/createSubAccountRequest.json @@ -0,0 +1,5 @@ +{ + "account": { + "name": "New Team Account" + } +} diff --git a/src/test/resources/api/subaccounts/createSubAccountResponse.json b/src/test/resources/api/subaccounts/createSubAccountResponse.json new file mode 100644 index 0000000..12943ed --- /dev/null +++ b/src/test/resources/api/subaccounts/createSubAccountResponse.json @@ -0,0 +1,4 @@ +{ + "id": 12347, + "name": "New Team Account" +} diff --git a/src/test/resources/api/subaccounts/getSubAccountsResponse.json b/src/test/resources/api/subaccounts/getSubAccountsResponse.json new file mode 100644 index 0000000..aa105cf --- /dev/null +++ b/src/test/resources/api/subaccounts/getSubAccountsResponse.json @@ -0,0 +1,10 @@ +[ + { + "id": 12345, + "name": "Development Team Account" + }, + { + "id": 12346, + "name": "QA Team Account" + } +] diff --git a/src/test/resources/api/webhooks/createWebhookRequest.json b/src/test/resources/api/webhooks/createWebhookRequest.json new file mode 100644 index 0000000..e33dfa7 --- /dev/null +++ b/src/test/resources/api/webhooks/createWebhookRequest.json @@ -0,0 +1,13 @@ +{ + "webhook": { + "url": "https://example.com/mailtrap/webhooks", + "webhook_type": "email_sending", + "payload_format": "json", + "sending_stream": "transactional", + "event_types": [ + "delivery", + "bounce" + ], + "domain_id": 435 + } +} \ No newline at end of file diff --git a/src/test/resources/api/webhooks/createWebhookResponse.json b/src/test/resources/api/webhooks/createWebhookResponse.json new file mode 100644 index 0000000..f171ab4 --- /dev/null +++ b/src/test/resources/api/webhooks/createWebhookResponse.json @@ -0,0 +1,16 @@ +{ + "data": { + "id": 1, + "url": "https://example.com/mailtrap/webhooks", + "active": true, + "webhook_type": "email_sending", + "payload_format": "json", + "sending_stream": "transactional", + "domain_id": 435, + "event_types": [ + "delivery", + "bounce" + ], + "signing_secret": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" + } +} \ No newline at end of file diff --git a/src/test/resources/api/webhooks/deleteWebhookResponse.json b/src/test/resources/api/webhooks/deleteWebhookResponse.json new file mode 100644 index 0000000..d9b3f82 --- /dev/null +++ b/src/test/resources/api/webhooks/deleteWebhookResponse.json @@ -0,0 +1,9 @@ +{ + "data": { + "id": 1, + "url": "https://example.com/mailtrap/webhooks", + "active": true, + "webhook_type": "audit_log", + "payload_format": "json" + } +} diff --git a/src/test/resources/api/webhooks/getWebhookResponse.json b/src/test/resources/api/webhooks/getWebhookResponse.json new file mode 100644 index 0000000..13e1750 --- /dev/null +++ b/src/test/resources/api/webhooks/getWebhookResponse.json @@ -0,0 +1,15 @@ +{ + "data": { + "id": 1, + "url": "https://example.com/mailtrap/webhooks", + "active": true, + "webhook_type": "email_sending", + "payload_format": "json", + "sending_stream": "transactional", + "domain_id": 435, + "event_types": [ + "delivery", + "bounce" + ] + } +} \ No newline at end of file diff --git a/src/test/resources/api/webhooks/listWebhooksResponse.json b/src/test/resources/api/webhooks/listWebhooksResponse.json new file mode 100644 index 0000000..c82195b --- /dev/null +++ b/src/test/resources/api/webhooks/listWebhooksResponse.json @@ -0,0 +1,24 @@ +{ + "data": [ + { + "id": 1, + "url": "https://example.com/mailtrap/webhooks", + "active": true, + "webhook_type": "email_sending", + "payload_format": "json", + "sending_stream": "transactional", + "domain_id": 435, + "event_types": [ + "delivery", + "bounce" + ] + }, + { + "id": 2, + "url": "https://example.com/mailtrap/webhooks", + "active": true, + "webhook_type": "audit_log", + "payload_format": "json" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/api/webhooks/updateWebhookRequest.json b/src/test/resources/api/webhooks/updateWebhookRequest.json new file mode 100644 index 0000000..5807b56 --- /dev/null +++ b/src/test/resources/api/webhooks/updateWebhookRequest.json @@ -0,0 +1,5 @@ +{ + "webhook": { + "active": false + } +} \ No newline at end of file diff --git a/src/test/resources/api/webhooks/updateWebhookResponse.json b/src/test/resources/api/webhooks/updateWebhookResponse.json new file mode 100644 index 0000000..76aaa1c --- /dev/null +++ b/src/test/resources/api/webhooks/updateWebhookResponse.json @@ -0,0 +1,15 @@ +{ + "data": { + "id": 1, + "url": "https://example.com/mailtrap/webhooks", + "active": false, + "webhook_type": "email_sending", + "payload_format": "json", + "sending_stream": "transactional", + "domain_id": 435, + "event_types": [ + "delivery", + "bounce" + ] + } +}