Skip to content

Commit 96f60a8

Browse files
feat: add global contacts methods (#75)
1 parent 31a9c8e commit 96f60a8

File tree

2 files changed

+240
-6
lines changed

2 files changed

+240
-6
lines changed

src/main/java/com/resend/services/contacts/Contacts.java

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ public Contacts(final String apiKey) {
3333
public CreateContactResponseSuccess create(CreateContactOptions createContactOptions) throws ResendException {
3434
String segmentId = createContactOptions.getSegmentId() != null ? createContactOptions.getSegmentId() : createContactOptions.getAudienceId();
3535
String payload = super.resendMapper.writeValue(createContactOptions);
36-
AbstractHttpResponse<String> response = httpClient.perform("/segments/" + segmentId + "/contacts" , super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json"));
36+
37+
// Use /contacts for global contacts (when no segment ID is provided)
38+
String endpoint = (segmentId == null || segmentId.isEmpty())
39+
? "/contacts"
40+
: "/segments/" + segmentId + "/contacts";
41+
42+
AbstractHttpResponse<String> response = httpClient.perform(endpoint, super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json"));
3743

3844
if (!response.isSuccessful()) {
3945
throw new ResendException(response.getCode(), response.getBody());
@@ -83,13 +89,51 @@ public ListContactsResponseSuccess list(String segmentId, ListParams params) thr
8389
return resendMapper.readValue(responseBody, ListContactsResponseSuccess.class);
8490
}
8591

92+
/**
93+
* Retrieves a list of global contacts (not associated with any segment).
94+
*
95+
* @return A ListContactsResponseSuccess containing the list of global contacts.
96+
* @throws ResendException If an error occurs during the contacts list retrieval process.
97+
*/
98+
public ListContactsResponseSuccess list() throws ResendException {
99+
AbstractHttpResponse<String> response = this.httpClient.perform("/contacts", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json"));
100+
101+
if (!response.isSuccessful()) {
102+
throw new ResendException(response.getCode(), response.getBody());
103+
}
104+
105+
String responseBody = response.getBody();
106+
107+
return resendMapper.readValue(responseBody, ListContactsResponseSuccess.class);
108+
}
109+
110+
/**
111+
* Retrieves a paginated list of global contacts (not associated with any segment).
112+
*
113+
* @param params The params used to customize the list.
114+
* @return A ListContactsResponseSuccess containing the paginated list of global contacts.
115+
* @throws ResendException If an error occurs during the contacts list retrieval process.
116+
*/
117+
public ListContactsResponseSuccess list(ListParams params) throws ResendException {
118+
String pathWithQuery = "/contacts" + URLHelper.parse(params);
119+
AbstractHttpResponse<String> response = this.httpClient.perform(pathWithQuery, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json"));
120+
121+
if (!response.isSuccessful()) {
122+
throw new ResendException(response.getCode(), response.getBody());
123+
}
124+
125+
String responseBody = response.getBody();
126+
127+
return resendMapper.readValue(responseBody, ListContactsResponseSuccess.class);
128+
}
129+
86130
/**
87131
* Retrieves a contact by its unique identifier.
88132
*
89133
* @param params The object containing:
90134
* – {@code audienceId}: the audience to which the contact belongs
91-
* – Either {@code id}: the contacts id
92-
* or {@code email}: the contacts email address
135+
* – Either {@code id}: the contact's id
136+
* or {@code email}: the contact's email address
93137
* @return The retrieved contact details.
94138
* @throws ResendException If an error occurs while retrieving the contact.
95139
*/
@@ -104,7 +148,35 @@ public GetContactResponseSuccess get(GetContactOptions params) throws ResendExce
104148
String contactIdentifier = (id != null && !id.isEmpty()) ? id : email;
105149
String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId();
106150

107-
AbstractHttpResponse<String> response = this.httpClient.perform("/segments/" + segmentId + "/contacts/" + contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json"));
151+
// Use /contacts for global contacts (when no segment ID is provided)
152+
String endpoint = (segmentId == null || segmentId.isEmpty())
153+
? "/contacts/" + contactIdentifier
154+
: "/segments/" + segmentId + "/contacts/" + contactIdentifier;
155+
156+
AbstractHttpResponse<String> response = this.httpClient.perform(endpoint, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json"));
157+
158+
if (!response.isSuccessful()) {
159+
throw new ResendException(response.getCode(), response.getBody());
160+
}
161+
162+
String responseBody = response.getBody();
163+
164+
return resendMapper.readValue(responseBody, GetContactResponseSuccess.class);
165+
}
166+
167+
/**
168+
* Retrieves a global contact by its unique identifier (not associated with any segment).
169+
*
170+
* @param contactIdOrEmail The contact's id or email address.
171+
* @return The retrieved contact details.
172+
* @throws ResendException If an error occurs while retrieving the contact.
173+
*/
174+
public GetContactResponseSuccess get(String contactIdOrEmail) throws ResendException {
175+
if (contactIdOrEmail == null || contactIdOrEmail.isEmpty()) {
176+
throw new IllegalArgumentException("Contact id or email must be provided");
177+
}
178+
179+
AbstractHttpResponse<String> response = this.httpClient.perform("/contacts/" + contactIdOrEmail, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json"));
108180

109181
if (!response.isSuccessful()) {
110182
throw new ResendException(response.getCode(), response.getBody());
@@ -131,7 +203,36 @@ public RemoveContactResponseSuccess remove(RemoveContactOptions params) throws R
131203
String pathParameter = params.getId() != null ? params.getId() : params.getEmail();
132204
String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId();
133205

134-
AbstractHttpResponse<String> response = httpClient.perform("/segments/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null);
206+
// Use /contacts for global contacts (when no segment ID is provided)
207+
String endpoint = (segmentId == null || segmentId.isEmpty())
208+
? "/contacts/" + pathParameter
209+
: "/segments/" + segmentId + "/contacts/" + pathParameter;
210+
211+
AbstractHttpResponse<String> response = httpClient.perform(endpoint, super.apiKey, HttpMethod.DELETE, "", null);
212+
213+
if (!response.isSuccessful()) {
214+
throw new ResendException(response.getCode(), response.getBody());
215+
}
216+
217+
String responseBody = response.getBody();
218+
219+
return resendMapper.readValue(responseBody, RemoveContactResponseSuccess.class);
220+
}
221+
222+
/**
223+
* Deletes a global contact based on the provided contact ID.
224+
* Note: Global contacts can only be removed by ID, not by email.
225+
*
226+
* @param contactId The contact's id.
227+
* @return The RemoveContactsResponseSuccess with the details of the removed contact.
228+
* @throws ResendException If an error occurs during the contact deletion process.
229+
*/
230+
public RemoveContactResponseSuccess remove(String contactId) throws ResendException {
231+
if (contactId == null || contactId.isEmpty()) {
232+
throw new IllegalArgumentException("Contact id must be provided");
233+
}
234+
235+
AbstractHttpResponse<String> response = httpClient.perform("/contacts/" + contactId, super.apiKey, HttpMethod.DELETE, "", null);
135236

136237
if (!response.isSuccessful()) {
137238
throw new ResendException(response.getCode(), response.getBody());
@@ -158,8 +259,13 @@ public UpdateContactResponseSuccess update(UpdateContactOptions params) throws R
158259
String pathParameter = params.getId() != null ? params.getId() : params.getEmail();
159260
String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId();
160261

262+
// Use /contacts for global contacts (when no segment ID is provided)
263+
String endpoint = (segmentId == null || segmentId.isEmpty())
264+
? "/contacts/" + pathParameter
265+
: "/segments/" + segmentId + "/contacts/" + pathParameter;
266+
161267
String payload = super.resendMapper.writeValue(params);
162-
AbstractHttpResponse<String> response = httpClient.perform("/segments/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json"));
268+
AbstractHttpResponse<String> response = httpClient.perform(endpoint, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json"));
163269

164270
if (!response.isSuccessful()) {
165271
throw new ResendException(response.getCode(), response.getBody());

src/test/java/com/resend/services/contacts/ContactsTest.java

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,132 @@ public void testUpdateTopics_Success() throws ResendException {
203203
assertEquals(expectedResponse.getId(), res.getId());
204204
verify(contacts, times(1)).updateTopics(options);
205205
}
206+
207+
// Global contacts tests (without segment ID)
208+
209+
@Test
210+
public void testCreateGlobalContact_Success() throws ResendException {
211+
CreateContactResponseSuccess expectedContact = ContactsUtil.createContactResponseSuccess();
212+
CreateContactOptions param = CreateContactOptions.builder()
213+
214+
.firstName("John")
215+
.lastName("Doe")
216+
.build();
217+
218+
when(contacts.create(param))
219+
.thenReturn(expectedContact);
220+
221+
CreateContactResponseSuccess createdContact = contacts.create(param);
222+
223+
assertEquals(createdContact, expectedContact);
224+
verify(contacts, times(1)).create(param);
225+
}
226+
227+
@Test
228+
public void testListGlobalContacts_Success() throws ResendException {
229+
ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse();
230+
231+
when(contacts.list())
232+
.thenReturn(expectedResponse);
233+
234+
ListContactsResponseSuccess res = contacts.list();
235+
236+
assertNotNull(res);
237+
assertEquals(expectedResponse.getData().size(), res.getData().size());
238+
assertEquals(expectedResponse.getObject(), res.getObject());
239+
}
240+
241+
@Test
242+
public void testListGlobalContactsWithPagination_Success() throws ResendException {
243+
ListParams params = ListParams.builder()
244+
.limit(3).build();
245+
246+
ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse();
247+
248+
when(contacts.list(params))
249+
.thenReturn(expectedResponse);
250+
251+
ListContactsResponseSuccess res = contacts.list(params);
252+
253+
assertNotNull(res);
254+
assertEquals(params.getLimit(), res.getData().size());
255+
assertEquals(expectedResponse.getObject(), res.getObject());
256+
}
257+
258+
@Test
259+
public void testGetGlobalContactById_Success() throws ResendException {
260+
String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3";
261+
GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess();
262+
263+
when(contacts.get(contactId)).thenReturn(expected);
264+
265+
GetContactResponseSuccess res = contacts.get(contactId);
266+
267+
assertNotNull(res);
268+
assertEquals(expected, res);
269+
verify(contacts, times(1)).get(contactId);
270+
}
271+
272+
@Test
273+
public void testGetGlobalContactByEmail_Success() throws ResendException {
274+
String contactEmail = "[email protected]";
275+
GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess();
276+
277+
when(contacts.get(contactEmail)).thenReturn(expected);
278+
279+
GetContactResponseSuccess res = contacts.get(contactEmail);
280+
281+
assertNotNull(res);
282+
assertEquals(expected, res);
283+
verify(contacts, times(1)).get(contactEmail);
284+
}
285+
286+
@Test
287+
public void testRemoveGlobalContactById_Success() throws ResendException {
288+
String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3";
289+
RemoveContactResponseSuccess removed = ContactsUtil.removeContactResponseSuccess();
290+
291+
when(contacts.remove(contactId))
292+
.thenReturn(removed);
293+
294+
RemoveContactResponseSuccess res = contacts.remove(contactId);
295+
296+
assertEquals(removed, res);
297+
verify(contacts, times(1)).remove(contactId);
298+
}
299+
300+
@Test
301+
public void testUpdateGlobalContact_Success() throws ResendException {
302+
UpdateContactOptions params = UpdateContactOptions.builder()
303+
.id("e169aa45-1ecf-4183-9955-b1499d5701d3")
304+
.firstName("Jane")
305+
.lastName("Smith")
306+
.build();
307+
UpdateContactResponseSuccess expectedResponse = ContactsUtil.updateContactResponseSuccess();
308+
309+
when(contacts.update(params))
310+
.thenReturn(expectedResponse);
311+
312+
UpdateContactResponseSuccess res = contacts.update(params);
313+
314+
assertNotNull(res);
315+
assertEquals(expectedResponse.getId(), res.getId());
316+
assertEquals(expectedResponse.getObject(), res.getObject());
317+
}
318+
319+
@Test
320+
public void testGetContactWithoutSegmentId_Success() throws ResendException {
321+
GetContactOptions params = GetContactOptions.builder()
322+
.id("e169aa45-1ecf-4183-9955-b1499d5701d3")
323+
.build();
324+
GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess();
325+
326+
when(contacts.get(params)).thenReturn(expected);
327+
328+
GetContactResponseSuccess res = contacts.get(params);
329+
330+
assertNotNull(res);
331+
assertEquals(expected, res);
332+
verify(contacts, times(1)).get(params);
333+
}
206334
}

0 commit comments

Comments
 (0)