Skip to content

Commit c7b43e2

Browse files
authored
Merge pull request #161 from ssk910/feature/unit_test-for-integrations-api
Added unit tests for integrations api
2 parents 434c092 + ae56db9 commit c7b43e2

15 files changed

+549
-136
lines changed

api/src/main/java/com/messagebird/MessageBirdClient.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
import com.messagebird.objects.conversations.ConversationWebhookCreateRequest;
4747
import com.messagebird.objects.conversations.ConversationWebhookList;
4848
import com.messagebird.objects.conversations.ConversationWebhookUpdateRequest;
49-
import com.messagebird.objects.integrations.WhatsAppTemplate;
50-
import com.messagebird.objects.integrations.WhatsAppTemplateList;
51-
import com.messagebird.objects.integrations.WhatsAppTemplateResponse;
49+
import com.messagebird.objects.integrations.Template;
50+
import com.messagebird.objects.integrations.TemplateList;
51+
import com.messagebird.objects.integrations.TemplateResponse;
5252
import com.messagebird.objects.voicecalls.RecordingResponse;
5353
import com.messagebird.objects.voicecalls.TranscriptionResponse;
5454
import com.messagebird.objects.voicecalls.VoiceCall;
@@ -122,7 +122,7 @@ public class MessageBirdClient {
122122
private static final String CONVERSATION_SEND_PATH = "/send";
123123
private static final String CONVERSATION_MESSAGE_PATH = "/messages";
124124
private static final String CONVERSATION_WEBHOOK_PATH = "/webhooks";
125-
private static final String INTEGRATIONS_WHATSAPP_PATH = "/platforms/whatsapp";
125+
static final String INTEGRATIONS_WHATSAPP_PATH = "/platforms/whatsapp";
126126
static final String VOICECALLSPATH = "/calls";
127127
static final String LEGSPATH = "/legs";
128128
static final String RECORDINGPATH = "/recordings";
@@ -1862,13 +1862,14 @@ public String downloadFile(String id, String filename, String basePath) throws G
18621862
/**
18631863
* Create a WhatsApp message template through messagebird.
18641864
*
1865-
* @param template {@link WhatsAppTemplate} object to be created
1866-
* @return {@link WhatsAppTemplateResponse} response object
1867-
* @throws UnauthorizedException if client is unauthorized
1868-
* @throws GeneralException general exception or invalid template format
1865+
* @param template {@link Template} object to be created
1866+
* @return {@link TemplateResponse} response object
1867+
* @throws UnauthorizedException if client is unauthorized
1868+
* @throws GeneralException general exception
1869+
* @throws IllegalArgumentException invalid template format
18691870
*/
1870-
public WhatsAppTemplateResponse createWhatsAppTemplate(final WhatsAppTemplate template)
1871-
throws UnauthorizedException, GeneralException {
1871+
public TemplateResponse createWhatsAppTemplate(final Template template)
1872+
throws UnauthorizedException, GeneralException, IllegalArgumentException {
18721873
template.validate();
18731874

18741875
String url = String.format(
@@ -1877,7 +1878,7 @@ public WhatsAppTemplateResponse createWhatsAppTemplate(final WhatsAppTemplate te
18771878
INTEGRATIONS_WHATSAPP_PATH,
18781879
TEMPLATES_PATH
18791880
);
1880-
return messageBirdService.sendPayLoad(url, template, WhatsAppTemplateResponse.class);
1881+
return messageBirdService.sendPayLoad(url, template, TemplateResponse.class);
18811882
}
18821883

18831884
/**
@@ -1889,15 +1890,15 @@ public WhatsAppTemplateResponse createWhatsAppTemplate(final WhatsAppTemplate te
18891890
* @throws UnauthorizedException if client is unauthorized
18901891
* @throws GeneralException general exception
18911892
*/
1892-
public WhatsAppTemplateList listWhatsAppTemplates(final int offset, final int limit)
1893+
public TemplateList listWhatsAppTemplates(final int offset, final int limit)
18931894
throws UnauthorizedException, GeneralException {
18941895
String url = String.format(
18951896
"%s%s%s",
18961897
INTEGRATIONS_BASE_URL_V3,
18971898
INTEGRATIONS_WHATSAPP_PATH,
18981899
TEMPLATES_PATH
18991900
);
1900-
return messageBirdService.requestList(url, offset, limit, WhatsAppTemplateList.class);
1901+
return messageBirdService.requestList(url, offset, limit, TemplateList.class);
19011902
}
19021903

19031904
/**
@@ -1907,7 +1908,7 @@ public WhatsAppTemplateList listWhatsAppTemplates(final int offset, final int li
19071908
* @throws UnauthorizedException if client is unauthorized
19081909
* @throws GeneralException general exception
19091910
*/
1910-
public WhatsAppTemplateList listWhatsAppTemplates() throws UnauthorizedException, GeneralException {
1911+
public TemplateList listWhatsAppTemplates() throws UnauthorizedException, GeneralException {
19111912
final int offset = 0;
19121913
final int limit = 10;
19131914

@@ -1923,7 +1924,7 @@ public WhatsAppTemplateList listWhatsAppTemplates() throws UnauthorizedException
19231924
* @throws GeneralException general exception
19241925
* @throws NotFoundException if template name is not found
19251926
*/
1926-
public List<WhatsAppTemplateResponse> getWhatsAppTemplatesBy(final String templateName)
1927+
public List<TemplateResponse> getWhatsAppTemplatesBy(final String templateName)
19271928
throws GeneralException, UnauthorizedException, NotFoundException {
19281929
if (templateName == null) {
19291930
throw new IllegalArgumentException("Template name must be specified.");
@@ -1936,8 +1937,7 @@ public List<WhatsAppTemplateResponse> getWhatsAppTemplatesBy(final String templa
19361937
TEMPLATES_PATH
19371938
);
19381939

1939-
final WhatsAppTemplateResponse[] templateResponses = messageBirdService.requestByID(url, templateName, WhatsAppTemplateResponse[].class);
1940-
return Arrays.asList(templateResponses);
1940+
return messageBirdService.requestByIdAsList(url, templateName, TemplateResponse.class);
19411941
}
19421942

19431943
/**
@@ -1951,7 +1951,7 @@ public List<WhatsAppTemplateResponse> getWhatsAppTemplatesBy(final String templa
19511951
* @throws GeneralException general exception
19521952
* @throws NotFoundException if template name and language are not found
19531953
*/
1954-
public WhatsAppTemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language)
1954+
public TemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language)
19551955
throws GeneralException, UnauthorizedException, NotFoundException {
19561956
if (templateName == null || language == null) {
19571957
throw new IllegalArgumentException("Template name and language must be specified.");
@@ -1965,7 +1965,7 @@ public WhatsAppTemplateResponse fetchWhatsAppTemplateBy(final String templateNam
19651965
templateName,
19661966
language
19671967
);
1968-
return messageBirdService.request(url, WhatsAppTemplateResponse.class);
1968+
return messageBirdService.request(url, TemplateResponse.class);
19691969
}
19701970

19711971

api/src/main/java/com/messagebird/MessageBirdService.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.messagebird.exceptions.NotFoundException;
55
import com.messagebird.exceptions.UnauthorizedException;
66
import com.messagebird.objects.PagedPaging;
7+
import java.util.List;
78
import java.util.Map;
89

910
/**
@@ -38,6 +39,20 @@ public interface MessageBirdService {
3839

3940
<R> R requestByID(String request, String id, Map<String, Object> params, Class<R> clazz) throws UnauthorizedException, GeneralException, NotFoundException;
4041

42+
/**
43+
* Execute a object by ID request. It will add the id to the request parameter and retrieve a list of an object E back.
44+
*
45+
* @author ssk910
46+
* @param request path to the request, for example "/messages"
47+
* @param id id of the object to request. id can be null in case request's that don't need a id, for example /balance
48+
* @param elementClass Class type of List to return
49+
* @return new list of elementClass
50+
* @throws UnauthorizedException if client is unauthorized
51+
* @throws GeneralException general exception
52+
* @throws NotFoundException
53+
*/
54+
<E> List<E> requestByIdAsList(String request, String id, Class<E> elementClass) throws UnauthorizedException, GeneralException, NotFoundException;
55+
4156
/**
4257
* Delete a object by ID.
4358
*

api/src/main/java/com/messagebird/MessageBirdServiceImpl.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.messagebird;
22

33
import com.fasterxml.jackson.annotation.JsonInclude.Include;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
45
import com.fasterxml.jackson.databind.DeserializationFeature;
56
import com.fasterxml.jackson.databind.JsonNode;
67
import com.fasterxml.jackson.databind.MapperFeature;
@@ -27,6 +28,7 @@
2728
import java.util.ArrayList;
2829
import java.util.Arrays;
2930
import java.util.Collection;
31+
import java.util.Collections;
3032
import java.util.HashMap;
3133
import java.util.Iterator;
3234
import java.util.LinkedHashMap;
@@ -138,6 +140,17 @@ public <R> R requestByID(String request, String id, Map<String, Object> params,
138140
return getJsonData(request + path + queryParams, null, "GET", clazz);
139141
}
140142

143+
@Override
144+
public <E> List<E> requestByIdAsList(String request, String id, Class<E> elementClass)
145+
throws UnauthorizedException, GeneralException, NotFoundException {
146+
String path = "";
147+
if (id != null) {
148+
path = "/" + id;
149+
}
150+
151+
return getJsonDataAsList(request + path, null, "GET", elementClass);
152+
}
153+
141154
@Override
142155
public void deleteByID(String request, String id) throws UnauthorizedException, GeneralException, NotFoundException {
143156
getJsonData(request + "/" + id, null, "DELETE", null);
@@ -234,6 +247,10 @@ public <T, P> T getJsonData(final String request, final P payload, final String
234247
return getJsonData(request, payload, requestType, new HashMap<>(), clazz);
235248
}
236249

250+
public <P, E> List<E> getJsonDataAsList(final String request, final P payload, final String requestType, final Class<E> elementClass) throws UnauthorizedException, GeneralException, NotFoundException {
251+
return getJsonDataAsList(request, payload, requestType, new HashMap<>(), elementClass);
252+
}
253+
237254
public <T, P> T getJsonData(final String request, final P payload, final String requestType, final Map<String, String> headers, final Class<T> clazz) throws UnauthorizedException, GeneralException, NotFoundException {
238255
if (request == null) {
239256
throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED);
@@ -260,7 +277,7 @@ public <T, P> T getJsonData(final String request, final P payload, final String
260277
// Prevents mismatched exception when clazz is null
261278
return clazz == null
262279
? null
263-
: mapper.readValue(body, clazz);
280+
: this.readValue(mapper, body, clazz);
264281
} catch (IOException ioe) {
265282
throw new GeneralException(ioe);
266283
}
@@ -271,6 +288,54 @@ public <T, P> T getJsonData(final String request, final P payload, final String
271288
return null;
272289
}
273290

291+
// todo: need to refactor for duplicated code.
292+
public <P, E> List<E> getJsonDataAsList(final String request,
293+
final P payload, final String requestType, final Map<String, String> headers, final Class<E> elementClass)
294+
throws UnauthorizedException, GeneralException, NotFoundException {
295+
if (request == null) {
296+
throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED);
297+
}
298+
299+
String url = request;
300+
if (!isURLAbsolute(url)) {
301+
url = serviceUrl + url;
302+
}
303+
final APIResponse apiResponse = doRequest(requestType, url, headers, payload);
304+
305+
final String body = apiResponse.getBody();
306+
final int status = apiResponse.getStatus();
307+
308+
if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED || status == HttpURLConnection.HTTP_ACCEPTED) {
309+
try {
310+
final ObjectMapper mapper = new ObjectMapper();
311+
// If we as new properties, we don't want the system to fail, we rather want to ignore them
312+
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
313+
// Enable case insensitivity to avoid parsing errors if parameters' case in api response doesn't match sdk's
314+
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
315+
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
316+
317+
// Prevents mismatched exception when clazz is null
318+
return this.readValueAsList(mapper, body, elementClass);
319+
} catch (IOException ioe) {
320+
throw new GeneralException(ioe);
321+
}
322+
} else if (status == HttpURLConnection.HTTP_NO_CONTENT) {
323+
return Collections.emptyList(); // no content doesn't mean an error
324+
}
325+
handleHttpFailStatuses(status, body);
326+
return Collections.emptyList();
327+
}
328+
329+
private <T> T readValue(ObjectMapper mapper, String content, Class<T> clazz)
330+
throws JsonProcessingException {
331+
return mapper.readValue(content, clazz);
332+
}
333+
334+
private <E> List<E> readValueAsList(ObjectMapper mapper, String content, final Class<E> elementClass)
335+
throws JsonProcessingException {
336+
return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, elementClass));
337+
}
338+
274339
private void handleHttpFailStatuses(final int status, String body) throws UnauthorizedException, NotFoundException, GeneralException {
275340
if (status == HttpURLConnection.HTTP_UNAUTHORIZED) {
276341
final List<ErrorReport> errorReport = getErrorReportOrNull(body);

api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.messagebird.objects.integrations;
22

3-
import com.messagebird.exceptions.GeneralException;
43
import java.util.List;
54

65
/**
@@ -71,19 +70,19 @@ public String toString() {
7170
/**
7271
* Check if this component is valid.
7372
*
74-
* @throws GeneralException Occurs when validation is not passed.
73+
* @throws IllegalArgumentException Occurs when validation is not passed.
7574
*/
76-
public void validateComponent() throws GeneralException {
75+
public void validateComponent() throws IllegalArgumentException {
7776
this.validateButtons();
7877
this.validateComponentExample();
7978
}
8079

8180
/**
8281
* Check if button list is valid.
8382
*
84-
* @throws GeneralException Occurs when validation is not passed.
83+
* @throws IllegalArgumentException Occurs when validation is not passed.
8584
*/
86-
private void validateButtons() throws GeneralException {
85+
private void validateButtons() throws IllegalArgumentException {
8786
if (this.buttons == null) {
8887
return;
8988
}
@@ -96,9 +95,9 @@ private void validateButtons() throws GeneralException {
9695
/**
9796
* Check for header_text and header_url.
9897
*
99-
* @throws GeneralException Occurs when {@code header_text} or {@code header_url} is not able to use.
98+
* @throws IllegalArgumentException Occurs when {@code header_text} or {@code header_url} is not able to use.
10099
*/
101-
private void validateComponentExample() throws GeneralException {
100+
private void validateComponentExample() throws IllegalArgumentException {
102101
final boolean isExampleNotNull = this.example != null;
103102
final boolean isHeaderTextNotEmpty =
104103
isExampleNotNull && !(this.example.getHeader_text() == null || this.example.getHeader_text()
@@ -119,26 +118,26 @@ private void validateComponentExample() throws GeneralException {
119118
/**
120119
* Check if header_text is able to use.
121120
*
122-
* @throws GeneralException Occurs when type is not {@code HEADER} and format is not {@code TEXT}.
121+
* @throws IllegalArgumentException Occurs when type is not {@code HEADER} and format is not {@code TEXT}.
123122
*/
124-
private void checkHeaderText() throws GeneralException {
123+
private void checkHeaderText() throws IllegalArgumentException {
125124
if (!(type.equals(HSMComponentType.HEADER)
126125
&& format.equals(HSMComponentFormat.TEXT))
127126
) {
128-
throw new GeneralException("\"header_text\" is available for only HEADER type and TEXT format.");
127+
throw new IllegalArgumentException("\"header_text\" is available for only HEADER type and TEXT format.");
129128
}
130129
}
131130

132131
/**
133132
* Check if header_url is able to use.
134133
*
135-
* @throws GeneralException Occurs when type is not {@code HEADER} and format is not {@code IMAGE}.
134+
* @throws IllegalArgumentException Occurs when type is not {@code HEADER} and format is not {@code IMAGE}.
136135
*/
137-
private void checkHeaderUrl() throws GeneralException {
136+
private void checkHeaderUrl() throws IllegalArgumentException {
138137
if (!(type.equals(HSMComponentType.HEADER)
139138
&& format.equals(HSMComponentFormat.IMAGE))
140139
) {
141-
throw new GeneralException("\"header_url\" is available for only HEADER type and IMAGE format.");
140+
throw new IllegalArgumentException("\"header_url\" is available for only HEADER type and IMAGE format.");
142141
}
143142
}
144143
}

api/src/main/java/com/messagebird/objects/integrations/HSMComponentButton.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.messagebird.objects.integrations;
22

3-
import com.messagebird.exceptions.GeneralException;
43
import java.util.List;
54

65
/**
@@ -71,9 +70,9 @@ public String toString() {
7170
/**
7271
* Check if example field is able to use.
7372
*
74-
* @throws GeneralException Occurs if button type is not {@code URL} or {@code QUICK_REPLY}.
73+
* @throws IllegalArgumentException Occurs if button type is not {@code URL} or {@code QUICK_REPLY}.
7574
*/
76-
public void validateButtonExample() throws GeneralException {
75+
public void validateButtonExample() throws IllegalArgumentException {
7776
final boolean isExampleEmpty = this.example == null || this.example.isEmpty();
7877
final boolean isNotProperType = !(this.type.equals(HSMComponentButtonType.URL)
7978
|| this.type.equals(HSMComponentButtonType.QUICK_REPLY));
@@ -83,7 +82,7 @@ public void validateButtonExample() throws GeneralException {
8382
}
8483

8584
if (isNotProperType) {
86-
throw new GeneralException("An example field in HSMComponentButton is available for only URL or QUICK_REPLY button types.");
85+
throw new IllegalArgumentException("An example field in HSMComponentButton is available for only URL or QUICK_REPLY button types.");
8786
}
8887
}
8988
}

0 commit comments

Comments
 (0)