From 5ade3991d73cf5ac98ac2b8bfa4e5c5e1ca8a7e0 Mon Sep 17 00:00:00 2001 From: vtasun Date: Sat, 30 Aug 2025 01:49:52 +0300 Subject: [PATCH 1/3] Implemented Contact Fields API --- .../examples/contactfields/ContactFields.java | 48 ++++++++++ .../api/contactfields/ContactFields.java | 56 +++++++++++ .../api/contactfields/ContactFieldsImpl.java | 74 +++++++++++++++ .../client/api/MailtrapContactsApi.java | 2 + .../factory/MailtrapClientFactory.java | 4 +- .../mailtrap/model/ContactFieldDataType.java | 33 +++++++ .../CreateContactFieldRequest.java | 24 +++++ .../UpdateContactFieldRequest.java | 20 ++++ .../contactfields/ContactFieldResponse.java | 20 ++++ .../contactfields/ContactFieldsImplTest.java | 94 +++++++++++++++++++ .../java/io/mailtrap/testutils/BaseTest.java | 4 + .../createContactFieldRequest.json | 5 + .../createContactFieldResponse.json | 6 ++ .../getAllContactFieldsResponse.json | 20 ++++ .../getContactFieldResponse.json | 6 ++ .../updateContactFieldRequest.json | 4 + .../updateContactFieldResponse.json | 6 ++ 17 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 examples/java/io/mailtrap/examples/contactfields/ContactFields.java create mode 100644 src/main/java/io/mailtrap/api/contactfields/ContactFields.java create mode 100644 src/main/java/io/mailtrap/api/contactfields/ContactFieldsImpl.java create mode 100644 src/main/java/io/mailtrap/model/ContactFieldDataType.java create mode 100644 src/main/java/io/mailtrap/model/request/contactfields/CreateContactFieldRequest.java create mode 100644 src/main/java/io/mailtrap/model/request/contactfields/UpdateContactFieldRequest.java create mode 100644 src/main/java/io/mailtrap/model/response/contactfields/ContactFieldResponse.java create mode 100644 src/test/java/io/mailtrap/api/contactfields/ContactFieldsImplTest.java create mode 100644 src/test/resources/api/contactfields/createContactFieldRequest.json create mode 100644 src/test/resources/api/contactfields/createContactFieldResponse.json create mode 100644 src/test/resources/api/contactfields/getAllContactFieldsResponse.json create mode 100644 src/test/resources/api/contactfields/getContactFieldResponse.json create mode 100644 src/test/resources/api/contactfields/updateContactFieldRequest.json create mode 100644 src/test/resources/api/contactfields/updateContactFieldResponse.json diff --git a/examples/java/io/mailtrap/examples/contactfields/ContactFields.java b/examples/java/io/mailtrap/examples/contactfields/ContactFields.java new file mode 100644 index 0000000..b8aadcb --- /dev/null +++ b/examples/java/io/mailtrap/examples/contactfields/ContactFields.java @@ -0,0 +1,48 @@ +package io.mailtrap.examples.contactfields; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.ContactFieldDataType; +import io.mailtrap.model.request.contactfields.CreateContactFieldRequest; +import io.mailtrap.model.request.contactfields.UpdateContactFieldRequest; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class ContactFields { + + private static final String TOKEN = ""; + private static final long ACCOUNT_ID = 1L; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + + var createResponse = client.contactsApi().contactFields() + .createContactField(ACCOUNT_ID, new CreateContactFieldRequest("Contact name", ContactFieldDataType.TEXT, "merge-tag")); + + System.out.println(createResponse); + + var updateResponse = client.contactsApi().contactFields() + .updateContactField(ACCOUNT_ID, createResponse.getId(), new UpdateContactFieldRequest("Updated name", "updated-merge-tag")); + + System.out.println(updateResponse); + + var allContactFields = client.contactsApi().contactFields() + .getAllContactFields(ACCOUNT_ID); + + System.out.println(allContactFields); + + var contactField = client.contactsApi().contactFields() + .getContactField(ACCOUNT_ID, createResponse.getId()); + + System.out.println(contactField); + + client.contactsApi().contactFields() + .deleteContactField(ACCOUNT_ID, createResponse.getId()); + } +} diff --git a/src/main/java/io/mailtrap/api/contactfields/ContactFields.java b/src/main/java/io/mailtrap/api/contactfields/ContactFields.java new file mode 100644 index 0000000..66171a7 --- /dev/null +++ b/src/main/java/io/mailtrap/api/contactfields/ContactFields.java @@ -0,0 +1,56 @@ +package io.mailtrap.api.contactfields; + +import io.mailtrap.model.request.contactfields.CreateContactFieldRequest; +import io.mailtrap.model.request.contactfields.UpdateContactFieldRequest; +import io.mailtrap.model.response.contactfields.ContactFieldResponse; + +import java.util.List; + +public interface ContactFields { + + /** + * Get all Contact Fields existing in your account + * + * @param accountId unique account ID + * @return existing contact fields + */ + List getAllContactFields(long accountId); + + /** + * Create new Contact Fields (up to 40) + * + * @param accountId unique account IDC + * @param request contact field data + * @return attributes of the created contact field + */ + ContactFieldResponse createContactField(long accountId, CreateContactFieldRequest request); + + /** + * Get Contact Field by id + * + * @param accountId unique account ID + * @param fieldId Unique Contact Field ID + * @return attributes of the contact field + */ + ContactFieldResponse getContactField(long accountId, long fieldId); + + /** + * Update existing Contact Field + * + * @param accountId unique account ID + * @param fieldId Unique Contact Field ID + * @param request update data. You cannot change data_type of the field + * @return attributes of the contact field + */ + ContactFieldResponse updateContactField(long accountId, long fieldId, UpdateContactFieldRequest request); + + /** + * Delete existing Contact Field. + * You cannot delete a Contact Field which is used in Automations, Email Campaigns, and in conditions of Contact Segments + * + * @param accountId unique account ID + * @param fieldId Unique Contact Field ID + */ + void deleteContactField(long accountId, long fieldId); + +} diff --git a/src/main/java/io/mailtrap/api/contactfields/ContactFieldsImpl.java b/src/main/java/io/mailtrap/api/contactfields/ContactFieldsImpl.java new file mode 100644 index 0000000..eb4490b --- /dev/null +++ b/src/main/java/io/mailtrap/api/contactfields/ContactFieldsImpl.java @@ -0,0 +1,74 @@ +package io.mailtrap.api.contactfields; + +import io.mailtrap.CustomValidator; +import io.mailtrap.api.apiresource.ApiResourceWithValidation; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.contactfields.CreateContactFieldRequest; +import io.mailtrap.model.request.contactfields.UpdateContactFieldRequest; +import io.mailtrap.model.response.contactfields.ContactFieldResponse; + +import java.util.List; + +import static io.mailtrap.Constants.GENERAL_HOST; + +public class ContactFieldsImpl extends ApiResourceWithValidation implements ContactFields { + + public ContactFieldsImpl(MailtrapConfig config, CustomValidator customValidator) { + super(config, customValidator); + this.apiHost = GENERAL_HOST; + } + + @Override + public List getAllContactFields(long accountId) { + return httpClient.getList( + String.format(apiHost + "/api/accounts/%d/contacts/fields", accountId), + new RequestData(), + ContactFieldResponse.class + ); + } + + @Override + public ContactFieldResponse createContactField(long accountId, CreateContactFieldRequest request) { + + validateRequestBodyAndThrowException(request); + + return httpClient.post( + String.format(apiHost + "/api/accounts/%d/contacts/fields", accountId), + request, + new RequestData(), + ContactFieldResponse.class + ); + } + + @Override + public ContactFieldResponse getContactField(long accountId, long fieldId) { + return httpClient.get( + String.format(apiHost + "/api/accounts/%d/contacts/fields/%d", accountId, fieldId), + new RequestData(), + ContactFieldResponse.class + ); + } + + @Override + public ContactFieldResponse updateContactField(long accountId, long fieldId, UpdateContactFieldRequest request) { + + validateRequestBodyAndThrowException(request); + + return httpClient.patch( + String.format(apiHost + "/api/accounts/%d/contacts/fields/%d", accountId, fieldId), + request, + new RequestData(), + ContactFieldResponse.class + ); + } + + @Override + public void deleteContactField(long accountId, long fieldId) { + httpClient.delete( + String.format(apiHost + "/api/accounts/%d/contacts/fields/%d", accountId, fieldId), + new RequestData(), + Void.class + ); + } +} diff --git a/src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java b/src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java index 190664d..8369623 100644 --- a/src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java +++ b/src/main/java/io/mailtrap/client/api/MailtrapContactsApi.java @@ -1,5 +1,6 @@ package io.mailtrap.client.api; +import io.mailtrap.api.contactfields.ContactFields; import io.mailtrap.api.contactimports.ContactImports; import io.mailtrap.api.contactlists.ContactLists; import io.mailtrap.api.contacts.Contacts; @@ -17,4 +18,5 @@ public class MailtrapContactsApi { private final ContactLists contactLists; private final Contacts contacts; private final ContactImports contactImports; + private final ContactFields contactFields; } diff --git a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java index f0ad798..1a7f3c9 100644 --- a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java +++ b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java @@ -6,6 +6,7 @@ import io.mailtrap.api.attachments.AttachmentsImpl; import io.mailtrap.api.billing.BillingImpl; import io.mailtrap.api.bulkemails.BulkEmailsImpl; +import io.mailtrap.api.contactfields.ContactFieldsImpl; import io.mailtrap.api.contactimports.ContactImportsImpl; import io.mailtrap.api.contactlists.ContactListsImpl; import io.mailtrap.api.contacts.ContactsImpl; @@ -55,8 +56,9 @@ private static MailtrapContactsApi createContactsApi(MailtrapConfig config, Cust final var contactLists = new ContactListsImpl(config); final var contacts = new ContactsImpl(config); final var contactImports = new ContactImportsImpl(config, customValidator); + final var contactFields = new ContactFieldsImpl(config, customValidator); - return new MailtrapContactsApi(contactLists, contacts, contactImports); + return new MailtrapContactsApi(contactLists, contacts, contactImports, contactFields); } private static MailtrapGeneralApi createGeneralApi(MailtrapConfig config) { diff --git a/src/main/java/io/mailtrap/model/ContactFieldDataType.java b/src/main/java/io/mailtrap/model/ContactFieldDataType.java new file mode 100644 index 0000000..a5a4009 --- /dev/null +++ b/src/main/java/io/mailtrap/model/ContactFieldDataType.java @@ -0,0 +1,33 @@ +package io.mailtrap.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + + public enum ContactFieldDataType { + TEXT("text"), + INTEGER("integer"), + FLOAT("float"), + BOOLEAN("boolean"), + DATE("date"); + + private final String value; + + ContactFieldDataType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static ContactFieldDataType fromValue(String value) { + for (ContactFieldDataType type : ContactFieldDataType.values()) { + if (type.value.equalsIgnoreCase(value)) { + return type; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/request/contactfields/CreateContactFieldRequest.java b/src/main/java/io/mailtrap/model/request/contactfields/CreateContactFieldRequest.java new file mode 100644 index 0000000..4ad908d --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/contactfields/CreateContactFieldRequest.java @@ -0,0 +1,24 @@ +package io.mailtrap.model.request.contactfields; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import io.mailtrap.model.ContactFieldDataType; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CreateContactFieldRequest extends AbstractModel { + + @Size(max = 80) + private String name; + + @JsonProperty("data_type") + private ContactFieldDataType dataType; + + @Size(max = 80) + @JsonProperty("merge_tag") + private String mergeTag; + +} diff --git a/src/main/java/io/mailtrap/model/request/contactfields/UpdateContactFieldRequest.java b/src/main/java/io/mailtrap/model/request/contactfields/UpdateContactFieldRequest.java new file mode 100644 index 0000000..9c981d4 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/contactfields/UpdateContactFieldRequest.java @@ -0,0 +1,20 @@ +package io.mailtrap.model.request.contactfields; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UpdateContactFieldRequest extends AbstractModel { + + @Size(max = 80) + private String name; + + @Size(max = 80) + @JsonProperty("merge_tag") + private String mergeTag; + +} diff --git a/src/main/java/io/mailtrap/model/response/contactfields/ContactFieldResponse.java b/src/main/java/io/mailtrap/model/response/contactfields/ContactFieldResponse.java new file mode 100644 index 0000000..59682a0 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/contactfields/ContactFieldResponse.java @@ -0,0 +1,20 @@ +package io.mailtrap.model.response.contactfields; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.ContactFieldDataType; +import lombok.Data; + +@Data +public class ContactFieldResponse { + + private long id; + + private String name; + + @JsonProperty("data_type") + private ContactFieldDataType dataType; + + @JsonProperty("merge_tag") + private String mergeTag; + +} diff --git a/src/test/java/io/mailtrap/api/contactfields/ContactFieldsImplTest.java b/src/test/java/io/mailtrap/api/contactfields/ContactFieldsImplTest.java new file mode 100644 index 0000000..1d896c3 --- /dev/null +++ b/src/test/java/io/mailtrap/api/contactfields/ContactFieldsImplTest.java @@ -0,0 +1,94 @@ +package io.mailtrap.api.contactfields; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.ContactFieldDataType; +import io.mailtrap.model.request.contactfields.CreateContactFieldRequest; +import io.mailtrap.model.request.contactfields.UpdateContactFieldRequest; +import io.mailtrap.model.response.contactfields.ContactFieldResponse; +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.*; + +public class ContactFieldsImplTest extends BaseTest { + + private ContactFields api; + + @BeforeEach + public void init() { + TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/fields", + "GET", null, "api/contactfields/getAllContactFieldsResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/fields", + "POST", "api/contactfields/createContactFieldRequest.json", "api/contactfields/createContactFieldResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/fields/" + getFieldId, + "GET", null, "api/contactfields/getContactFieldResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/fields/" + updateFieldId, + "PATCH", "api/contactfields/updateContactFieldRequest.json", "api/contactfields/updateContactFieldResponse.json"), + + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/contacts/fields/" + deleteFieldId, + "DELETE", null, null) + )); + + MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).contactsApi().contactFields(); + } + + @Test + void test_getAllContactFields() { + List allContactFields = api.getAllContactFields(accountId); + + assertEquals(3, allContactFields.size()); + assertEquals("First name", allContactFields.get(0).getName()); + assertEquals(ContactFieldDataType.TEXT, allContactFields.get(0).getDataType()); + } + + @Test + void test_createContactField() { + ContactFieldResponse created = api.createContactField(accountId, new CreateContactFieldRequest("My Contact Field", ContactFieldDataType.BOOLEAN, "my_contact_field")); + + assertNotNull(created); + assertEquals(ContactFieldDataType.BOOLEAN, created.getDataType()); + assertEquals(1337, created.getId()); + } + + @Test + void test_getContactField() { + ContactFieldResponse contactField = api.getContactField(accountId, getFieldId); + + assertNotNull(contactField); + assertEquals("First name", contactField.getName()); + assertEquals(ContactFieldDataType.TEXT, contactField.getDataType()); + assertEquals(777, contactField.getId()); + } + + @Test + void test_updateContactField() { + + ContactFieldResponse contactFieldResponse = api.updateContactField(accountId, updateFieldId, new UpdateContactFieldRequest("My Updated Contact Field", "my_updated_contact_field")); + + assertNotNull(contactFieldResponse); + assertEquals(ContactFieldDataType.TEXT, contactFieldResponse.getDataType()); + assertEquals(updateFieldId, contactFieldResponse.getId()); + } + + @Test + void test_deleteContactField() { + assertDoesNotThrow(() -> api.deleteContactField(accountId, deleteFieldId)); + } + +} diff --git a/src/test/java/io/mailtrap/testutils/BaseTest.java b/src/test/java/io/mailtrap/testutils/BaseTest.java index 9939ac7..821740f 100644 --- a/src/test/java/io/mailtrap/testutils/BaseTest.java +++ b/src/test/java/io/mailtrap/testutils/BaseTest.java @@ -18,4 +18,8 @@ public class BaseTest { protected final String contactUUID = "018dd5e3-f6d2-7c00-8f9b-e5c3f2d8a132"; protected final String contactUUIDEncoded = URLEncoder.encode(contactUUID, Charset.defaultCharset()); protected final long importId = 1L; + protected final long fieldId = 1L; + protected final long getFieldId = 777L; + protected final long updateFieldId = 999L; + protected final long deleteFieldId = 1111L; } diff --git a/src/test/resources/api/contactfields/createContactFieldRequest.json b/src/test/resources/api/contactfields/createContactFieldRequest.json new file mode 100644 index 0000000..0f1230f --- /dev/null +++ b/src/test/resources/api/contactfields/createContactFieldRequest.json @@ -0,0 +1,5 @@ +{ + "name": "My Contact Field", + "data_type": "boolean", + "merge_tag": "my_contact_field" +} diff --git a/src/test/resources/api/contactfields/createContactFieldResponse.json b/src/test/resources/api/contactfields/createContactFieldResponse.json new file mode 100644 index 0000000..201e98e --- /dev/null +++ b/src/test/resources/api/contactfields/createContactFieldResponse.json @@ -0,0 +1,6 @@ +{ + "id": 1337, + "name": "My Contact Field", + "data_type": "boolean", + "merge_tag": "my_contact_field" +} diff --git a/src/test/resources/api/contactfields/getAllContactFieldsResponse.json b/src/test/resources/api/contactfields/getAllContactFieldsResponse.json new file mode 100644 index 0000000..07d06a4 --- /dev/null +++ b/src/test/resources/api/contactfields/getAllContactFieldsResponse.json @@ -0,0 +1,20 @@ +[ + { + "id": 111, + "name": "First name", + "data_type": "text", + "merge_tag": "first_name" + }, + { + "id": 222, + "name": "Birthdate", + "data_type": "date", + "merge_tag": "birthdate" + }, + { + "id": 333, + "name": "Cats count", + "data_type": "integer", + "merge_tag": "cats_count" + } +] diff --git a/src/test/resources/api/contactfields/getContactFieldResponse.json b/src/test/resources/api/contactfields/getContactFieldResponse.json new file mode 100644 index 0000000..00c9133 --- /dev/null +++ b/src/test/resources/api/contactfields/getContactFieldResponse.json @@ -0,0 +1,6 @@ +{ + "id": 777, + "name": "First name", + "data_type": "text", + "merge_tag": "first_name" +} diff --git a/src/test/resources/api/contactfields/updateContactFieldRequest.json b/src/test/resources/api/contactfields/updateContactFieldRequest.json new file mode 100644 index 0000000..4d8de52 --- /dev/null +++ b/src/test/resources/api/contactfields/updateContactFieldRequest.json @@ -0,0 +1,4 @@ +{ + "name": "My Updated Contact Field", + "merge_tag": "my_updated_contact_field" +} diff --git a/src/test/resources/api/contactfields/updateContactFieldResponse.json b/src/test/resources/api/contactfields/updateContactFieldResponse.json new file mode 100644 index 0000000..9165e88 --- /dev/null +++ b/src/test/resources/api/contactfields/updateContactFieldResponse.json @@ -0,0 +1,6 @@ +{ + "id": 999, + "name": "My Updated Contact Field", + "data_type": "text", + "merge_tag": "my_updated_contact_field" +} From dd205750f957f9de0d96264758887d31daecbf05 Mon Sep 17 00:00:00 2001 From: vtasun Date: Sun, 31 Aug 2025 00:14:43 +0300 Subject: [PATCH 2/3] Minor comment change --- src/main/java/io/mailtrap/api/contactfields/ContactFields.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/mailtrap/api/contactfields/ContactFields.java b/src/main/java/io/mailtrap/api/contactfields/ContactFields.java index 66171a7..c12f327 100644 --- a/src/main/java/io/mailtrap/api/contactfields/ContactFields.java +++ b/src/main/java/io/mailtrap/api/contactfields/ContactFields.java @@ -19,7 +19,7 @@ public interface ContactFields { /** * Create new Contact Fields (up to 40) * - * @param accountId unique account IDC + * @param accountId unique account ID * @param request contact field data * @return attributes of the created contact field */ From ee34b680a22df53d27c4b08457201cb54daf2507 Mon Sep 17 00:00:00 2001 From: vtasun Date: Tue, 2 Sep 2025 00:44:06 +0300 Subject: [PATCH 3/3] Indentation fix --- src/main/java/io/mailtrap/model/ContactFieldDataType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/mailtrap/model/ContactFieldDataType.java b/src/main/java/io/mailtrap/model/ContactFieldDataType.java index a5a4009..0d3b87a 100644 --- a/src/main/java/io/mailtrap/model/ContactFieldDataType.java +++ b/src/main/java/io/mailtrap/model/ContactFieldDataType.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - public enum ContactFieldDataType { +public enum ContactFieldDataType { TEXT("text"), INTEGER("integer"), FLOAT("float"),