clazz) throws UnauthorizedException, GeneralException;
+
+ /**
+ * Gets the data from the request URL and stores it to basePath/fileName
+ *
+ * @param request path to the request, for example "/messages"
+ * @param basePath base path for storing directory
+ * @param fileName the fileName that is going to be stored.
+ * @return the path that file is stored
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if the file is not found
+ */
+ String getBinaryData(String request, String basePath, String fileName) throws UnauthorizedException, GeneralException, NotFoundException;
}
diff --git a/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java b/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java
index b001ad55..ef179b45 100644
--- a/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java
+++ b/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java
@@ -1,47 +1,79 @@
package com.messagebird;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.messagebird.exceptions.GeneralException;
+import com.messagebird.exceptions.NotFoundException;
+import com.messagebird.exceptions.UnauthorizedException;
+import com.messagebird.objects.ErrorReport;
+import com.messagebird.objects.PagedPaging;
+import org.apache.maven.artifact.versioning.ComparableVersion;
+
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.map.DeserializationConfig;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
-
-import com.messagebird.exceptions.GeneralException;
-import com.messagebird.exceptions.NotFoundException;
-import com.messagebird.exceptions.UnauthorizedException;
-import com.messagebird.objects.ErrorReport;
+import java.util.Scanner;
/**
* Implementation of MessageBirdService
* Sends and receives JSON objects from the Messagebird platform
- *
+ *
* Created by rvt on 1/5/15.
*/
public class MessageBirdServiceImpl implements MessageBirdService {
+
private static final String NOT_AUTHORISED_MSG = "You are not authorised for the MessageBird service, please check your access key.";
private static final String FAILED_DATA_RESPONSE_CODE = "Failed to retrieve data from MessageBird service with response code ";
private static final String ACCESS_KEY_MUST_BE_SPECIFIED = "Access key must be specified";
private static final String SERVICE_URL_MUST_BE_SPECIFIED = "Service URL must be specified";
private static final String REQUEST_VALUE_MUST_BE_SPECIFIED = "Request value must be specified";
- private static final String REQUEST_TYPE_MUST_BE_SET_TO_GET_OR_POST = "Request type must be set to GET, POST or DELETE";
- private static final List REQUESTMETHODS = Arrays.asList(new String[]{"GET", "POST", "DELETE"});
+ private static final String REQUEST_METHOD_NOT_ALLOWED = "Request method %s is not allowed.";
+ private static final String CAN_NOT_ALLOW_PATCH = "Can not set HttpURLConnection.methods field to allow PATCH.";
+
+ private static final String METHOD_DELETE = "DELETE";
+ private static final String METHOD_GET = "GET";
+ private static final String METHOD_PATCH = "PATCH";
+ private static final String METHOD_POST = "POST";
+ private static final String METHOD_PUT = "PUT";
+
+ private static final List REQUEST_METHODS = Arrays.asList(METHOD_DELETE, METHOD_GET, METHOD_PATCH, METHOD_POST, METHOD_PUT);
+ private static final List REQUEST_METHODS_WITH_PAYLOAD = Arrays.asList(METHOD_PATCH, METHOD_POST, METHOD_PUT);
+ private static final String[] PROTOCOL_LISTS = new String[]{"http://", "https://"};
+ private static final List PROTOCOLS = Arrays.asList(PROTOCOL_LISTS);
+
+ private static final ComparableVersion JAVA_VERSION = getJavaVersion();
+
+ // Indicates whether we've overridden HttpURLConnection's behaviour to
+ // allow PATCH requests yet. Also see docs on allowPatchRequestsIfNeeded().
+ private static boolean isPatchRequestAllowed = false;
+
+ private static final int BUFFER_SIZE = 4096;
+
private final String accessKey;
private final String serviceUrl;
- private final String clientVersion = "1.3.1";
- private final String userAgentString = "MessageBird/Java ApiClient/" + clientVersion;
+ private final String clientVersion = "6.3.1";
+ private final String userAgentString;
private Proxy proxy = null;
public MessageBirdServiceImpl(final String accessKey, final String serviceUrl) {
@@ -53,16 +85,38 @@ public MessageBirdServiceImpl(final String accessKey, final String serviceUrl) {
}
this.accessKey = accessKey;
this.serviceUrl = serviceUrl;
+ this.userAgentString = determineUserAgentString();
+
+ }
+
+ private static ComparableVersion getJavaVersion() {
+ try {
+ String version = System.getProperty("java.version");
+ return new ComparableVersion(version);
+ } catch (IllegalArgumentException e) {
+ return new ComparableVersion("0.0");
+ }
+ }
+
+ private String determineUserAgentString() {
+ return String.format("MessageBird Java/%s ApiClient/%s", JAVA_VERSION, clientVersion);
}
/**
* Initiate service with default serviceUrl.
- * @param accessKey
+ *
+ * @param accessKey developer access key
*/
public MessageBirdServiceImpl(final String accessKey) {
this(accessKey, "https://rest.messagebird.com");
}
+ @Override
+ public R request(String request, Class clazz)
+ throws UnauthorizedException, GeneralException, NotFoundException {
+ return getJsonData(request, null, "GET", clazz);
+ }
+
@Override
public R requestByID(String request, String id, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException {
String path = "";
@@ -86,89 +140,439 @@ public R requestByID(String request, String id, Map params,
return getJsonData(request + path + queryParams, null, "GET", clazz);
}
+ @Override
+ public List requestByIdAsList(String request, String id, Class elementClass)
+ throws UnauthorizedException, GeneralException, NotFoundException {
+ String path = "";
+ if (id != null) {
+ path = "/" + id;
+ }
+
+ return getJsonDataAsList(request + path, null, "GET", elementClass);
+ }
+
@Override
public void deleteByID(String request, String id) throws UnauthorizedException, GeneralException, NotFoundException {
getJsonData(request + "/" + id, null, "DELETE", null);
}
+ @Override
+ public R delete(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException {
+ return getJsonData(request, null, "DELETE", clazz);
+ }
+
@Override
public R requestList(String request, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException {
- Map map = new LinkedHashMap();
- if (offset!=null) map.put("offset", String.valueOf(offset));
- if (limit!=null) map.put("limit", String.valueOf(limit));
+ Map map = new LinkedHashMap<>();
+ if (offset != null) map.put("offset", String.valueOf(offset));
+ if (limit != null) map.put("limit", String.valueOf(limit));
try {
- return getJsonData(request+"?"+getPathVariables(map), null, "GET", clazz);
+ return getJsonData(request + "?" + getPathVariables(map), null, "GET", clazz);
} catch (NotFoundException e) {
throw new GeneralException(e);
}
}
@Override
- public R sendPayLoad(String request, P payLoad, Class clazz) throws UnauthorizedException, GeneralException {
+ public R requestList(String request, Map params, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException {
+ if (offset != null) params.put("offset", String.valueOf(offset));
+ if (limit != null) params.put("limit", String.valueOf(limit));
try {
- return getJsonData(request, payLoad, "POST", clazz);
+ return getJsonData(request + "?" + getPathVariables(params), null, "GET", clazz);
} catch (NotFoundException e) {
throw new GeneralException(e);
}
}
+ @Override
+ public R requestList(String request, PagedPaging pagedPaging, Class clazz) throws UnauthorizedException, GeneralException {
+ Map map = new LinkedHashMap<>();
+ if (pagedPaging.getPage() != null) map.put("page", String.valueOf(pagedPaging.getPage()));
+ if (pagedPaging.getPageSize() != null) map.put("perPage", String.valueOf(pagedPaging.getPageSize()));
+
+ try {
+ return getJsonData(request + "?" + getPathVariables(map), null, "GET", clazz);
+ } catch (NotFoundException e) {
+ throw new GeneralException(e);
+ }
+ }
+
+ @Override
+ public R sendPayLoad(String request, P payload, Class clazz) throws UnauthorizedException, GeneralException {
+ return this.sendPayLoad("POST", request, payload, clazz);
+ }
+
+ @Override
+ public R sendPayLoad(String method, String request, P payload, Class clazz) throws UnauthorizedException, GeneralException {
+ return sendPayLoad(method, request, new HashMap<>(), payload, clazz);
+ }
+
+ @Override
+ public R sendPayLoad(String method, String request, Map headers, P payload, Class clazz) throws UnauthorizedException, GeneralException {
+ if (!REQUEST_METHODS_WITH_PAYLOAD.contains(method)) {
+ throw new IllegalArgumentException(String.format(REQUEST_METHOD_NOT_ALLOWED, method));
+ }
+
+ try {
+ return getJsonData(request, payload, method, headers, clazz);
+ } catch (NotFoundException e) {
+ throw new GeneralException(e);
+ }
+ }
+
+ @Override
+ public String getBinaryData(String request, String basePath, String fileName) throws GeneralException, UnauthorizedException, NotFoundException {
+ if (basePath == null) {
+ String homePath = System.getProperty("user.home");
+ //Home path is not existing
+ if (homePath == null) {
+ throw new IllegalArgumentException("BasePath must be specified.");
+ }
+ basePath = String.format("%s/%s",homePath,"Downloads");
+ }
+ File file = new File(basePath);
+ if(!file.exists()) {
+ throw new IllegalArgumentException("basePath must be existed as directory.");
+ }
+
+ if(!file.isDirectory()) {
+ throw new IllegalArgumentException("basePath must be a directory.");
+ }
+ String filePath = String.format("%s/%s", basePath, fileName);
+ return doGetRequestForFileAndStore(request, filePath);
+ }
+
public T getJsonData(final String request, final P payload, final String requestType, final Class clazz) throws UnauthorizedException, GeneralException, NotFoundException {
+ return getJsonData(request, payload, requestType, new HashMap<>(), clazz);
+ }
+
+ public List getJsonDataAsList(final String request, final P payload, final String requestType, final Class elementClass) throws UnauthorizedException, GeneralException, NotFoundException {
+ return getJsonDataAsList(request, payload, requestType, new HashMap<>(), elementClass);
+ }
+
+ public T getJsonData(final String request, final P payload, final String requestType, final Map headers, final Class clazz) throws UnauthorizedException, GeneralException, NotFoundException {
if (request == null) {
throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED);
}
- InputStream inputStream = null;
+
+ String url = request;
+ if (!isURLAbsolute(url)) {
+ url = serviceUrl + url;
+ }
+ final APIResponse apiResponse = doRequest(requestType, url, headers, payload);
+
+ final String body = apiResponse.getBody();
+ final int status = apiResponse.getStatus();
+
+ if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED || status == HttpURLConnection.HTTP_ACCEPTED) {
+ try {
+ final ObjectMapper mapper = new ObjectMapper();
+ // If we as new properties, we don't want the system to fail, we rather want to ignore them
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ // Enable case insensitivity to avoid parsing errors if parameters' case in api response doesn't match sdk's
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
+
+ // Prevents mismatched exception when clazz is null
+ return clazz == null
+ ? null
+ : this.readValue(mapper, body, clazz);
+ } catch (IOException ioe) {
+ throw new GeneralException(ioe);
+ }
+ } else if (status == HttpURLConnection.HTTP_NO_CONTENT) {
+ return null; // no content doesn't mean an error
+ }
+ handleHttpFailStatuses(status, body);
+ return null;
+ }
+
+ // todo: need to refactor for duplicated code.
+ public List getJsonDataAsList(final String request,
+ final P payload, final String requestType, final Map headers, final Class elementClass)
+ throws UnauthorizedException, GeneralException, NotFoundException {
+ if (request == null) {
+ throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED);
+ }
+
+ String url = request;
+ if (!isURLAbsolute(url)) {
+ url = serviceUrl + url;
+ }
+ final APIResponse apiResponse = doRequest(requestType, url, headers, payload);
+
+ final String body = apiResponse.getBody();
+ final int status = apiResponse.getStatus();
+
+ if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED || status == HttpURLConnection.HTTP_ACCEPTED) {
+ try {
+ final ObjectMapper mapper = new ObjectMapper();
+ // If we as new properties, we don't want the system to fail, we rather want to ignore them
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ // Enable case insensitivity to avoid parsing errors if parameters' case in api response doesn't match sdk's
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
+
+ // Prevents mismatched exception when clazz is null
+ return this.readValueAsList(mapper, body, elementClass);
+ } catch (IOException ioe) {
+ throw new GeneralException(ioe);
+ }
+ } else if (status == HttpURLConnection.HTTP_NO_CONTENT) {
+ return Collections.emptyList(); // no content doesn't mean an error
+ }
+ handleHttpFailStatuses(status, body);
+ return Collections.emptyList();
+ }
+
+ private T readValue(ObjectMapper mapper, String content, Class clazz)
+ throws JsonProcessingException {
+ return mapper.readValue(content, clazz);
+ }
+
+ private List readValueAsList(ObjectMapper mapper, String content, final Class elementClass)
+ throws JsonProcessingException {
+ return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, elementClass));
+ }
+
+ private void handleHttpFailStatuses(final int status, String body) throws UnauthorizedException, NotFoundException, GeneralException {
+ if (status == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ final List errorReport = getErrorReportOrNull(body);
+ throw new UnauthorizedException(NOT_AUTHORISED_MSG, errorReport);
+ } else if (status >= 400 && status < 500) { // Any code in the 400 range will have a list of error codes attached
+ final List errorReport = getErrorReportOrNull(body);
+ if (status == HttpURLConnection.HTTP_NOT_FOUND) {
+ throw new NotFoundException(errorReport);
+ }
+ throw new GeneralException(FAILED_DATA_RESPONSE_CODE + status, status, errorReport);
+ } else {
+ throw new GeneralException(FAILED_DATA_RESPONSE_CODE + status, status);
+ }
+ }
+ /**
+ * Actually sends a HTTP request and returns its body and HTTP status code.
+ *
+ * @param method HTTP method.
+ * @param url Absolute URL.
+ * @param headers additional headers to set on the request.
+ * @param payload Payload to JSON encode for the request body. May be null.
+ * @param Type of the payload.
+ * @return APIResponse containing the response's body and status.
+ */
+
APIResponse doRequest(final String method, final String url, final Map headers, final P payload) throws GeneralException {
HttpURLConnection connection = null;
- int responseCode = -1;
+ InputStream inputStream = null;
+
+ if (METHOD_PATCH.equalsIgnoreCase(method)) {
+ // It'd perhaps be cleaner to call this in the constructor, but
+ // we'd then need to throw GeneralExceptions from there. This means
+ // it wouldn't be possible to declare AND initialize _instance_
+ // fields of MessageBirdServiceImpl at the same time. This method
+ // already throws this exception, so now we don't have to pollute
+ // our public API further.
+ allowPatchRequestsIfNeeded();
+ }
+
try {
- connection = getConnection(serviceUrl + request, payload, requestType);
- responseCode = connection.getResponseCode();
- if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
+ connection = getConnection(url, payload, method, headers);
+ int status = connection.getResponseCode();
+
+ if (APIResponse.isSuccessStatus(status)) {
inputStream = connection.getInputStream();
- final ObjectMapper mapper = new ObjectMapper();
- // If we as new properties, we don't want the system to fail, we rather want to ignore them
- mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- return mapper.readValue(inputStream, clazz);
- } else if (responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
- return null; // no content doesn't mean an error
- } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
- final List errorReport = getErrorReport(connection.getErrorStream());
- throw new UnauthorizedException(NOT_AUTHORISED_MSG, errorReport);
- } else if (responseCode >= 400 && responseCode < 500) { // Any code in the 400 range will have a list of error codes attached
- final List errorReport = getErrorReport(connection.getErrorStream());
- if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
- throw new NotFoundException(errorReport);
+ } else {
+ inputStream = connection.getErrorStream();
+ if (inputStream == null) {
+ throw new IOException("Server returned HTTP error code " + status + " with no body.");
}
- throw new GeneralException(FAILED_DATA_RESPONSE_CODE + responseCode, responseCode, errorReport);
+ }
+
+ return new APIResponse(readToEnd(inputStream), status);
+ } catch (IOException ioe) {
+ throw new GeneralException(ioe);
+ } finally {
+ saveClose(inputStream);
+
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ /**
+ *
+ * Do get request for file from input url and stores the file in filepath.
+ * @param url Absolute URL.
+ * @param filePath the path where the downloaded file is going to be stored.
+ * @return if it succeed, it returns filepath otherwise null or exception.
+ */
+ private String doGetRequestForFileAndStore(final String url, final String filePath) throws GeneralException, UnauthorizedException, NotFoundException {
+ HttpURLConnection connection = null;
+ InputStream inputStream = null;
+
+ try {
+ connection = getConnection(url, null, METHOD_GET);
+ int status = connection.getResponseCode();
+
+ if (APIResponse.isSuccessStatus(status)) {
+ inputStream = connection.getInputStream();
} else {
- throw new GeneralException(FAILED_DATA_RESPONSE_CODE + responseCode, responseCode);
+ inputStream = connection.getErrorStream();
+ if (inputStream == null) {
+ throw new GeneralException("Error stream was empty");
+ }
}
- } catch (IOException e) {
- throw new GeneralException(e);
+ if (status == HttpURLConnection.HTTP_OK) {
+ return writeInputStreamToFile(inputStream, filePath);
+ }
+ String body = readToEnd(inputStream);
+ handleHttpFailStatuses(status, body);
+ } catch (IOException ioe) {
+ throw new GeneralException(ioe);
} finally {
saveClose(inputStream);
if (connection != null) {
connection.disconnect();
}
}
+ return null;
+ }
+
+ /**
+ * Writes input stream from IO to filepath.
+ * @param inputStream stream that has been collected file input
+ * @param filepath the storage path for the file
+ * @return if it succeed, it returns filepath otherwise null or exception.
+ * @throws IOException
+ */
+ private String writeInputStreamToFile(InputStream inputStream, String filepath) throws IOException {
+ // opens an output stream to save into file
+ FileOutputStream outputStream = new FileOutputStream(filepath);
+
+ int bytesRead = -1;
+ byte[] buffer = new byte[BUFFER_SIZE];
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+
+ outputStream.close();
+ return filepath;
+ }
+
+ /**
+ * By default, HttpURLConnection does not support PATCH requests. We can
+ * however work around this with reflection. Many thanks to okutane on
+ * StackOverflow: https://stackoverflow.com/a/46323891/3521243.
+ */
+ private synchronized static void allowPatchRequestsIfNeeded() throws GeneralException {
+ if (isPatchRequestAllowed) {
+ // Don't do anything if we've run this method before. We're in a
+ // synchronized block, so return ASAP.
+ return;
+ }
+
+ try {
+ // Ensure we can access the fields we need to set.
+ Field methodsField = HttpURLConnection.class.getDeclaredField("methods");
+ methodsField.setAccessible(true);
+
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);
+
+ Object noInstanceBecauseStaticField = null;
+
+ // Determine what methods should be allowed.
+ String[] existingMethods = (String[]) methodsField.get(noInstanceBecauseStaticField);
+ String[] allowedMethods = getAllowedMethods(existingMethods);
+
+ // Override the actual field to allow PATCH.
+ methodsField.set(noInstanceBecauseStaticField, allowedMethods);
+
+ // Set flag so we only have to run this once.
+ isPatchRequestAllowed = true;
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new GeneralException(CAN_NOT_ALLOW_PATCH);
+ }
+ }
+
+ /**
+ * Appends PATCH to the provided array.
+ *
+ * @param existingMethods Methods that are, and must be, allowed.
+ * @return New array also containing PATCH.
+ */
+ private static String[] getAllowedMethods(String[] existingMethods) {
+ int listCapacity = existingMethods.length + 1;
+
+ List allowedMethods = new ArrayList<>(listCapacity);
+
+ allowedMethods.addAll(Arrays.asList(existingMethods));
+ allowedMethods.add(METHOD_PATCH);
+ allowedMethods.add(METHOD_PUT);
+
+ return allowedMethods.toArray(new String[0]);
+ }
+
+ /**
+ * Reads the stream until it has no more bytes and returns a UTF-8 encoded
+ * string representation.
+ *
+ * @param inputStream Stream to read from.
+ * @return UTF-8 encoded string representation of stream's contents.
+ */
+ private String readToEnd(InputStream inputStream) {
+ Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+
+ return scanner.hasNext() ? scanner.next() : "";
+ }
+
+ /**
+ * Attempts determining whether the provided URL is an absolute one, based on the scheme.
+ *
+ * @param url provided url
+ * @return boolean
+ */
+ private boolean isURLAbsolute(String url) {
+ for (String protocol : PROTOCOLS) {
+ if (url.startsWith(protocol)) {
+ return true;
+ }
+ }
+
+ return false;
}
/**
* Create a HttpURLConnection connection object
*
* @param serviceUrl URL that needs to be requested
- * @param postData PostDATA, must be not null for requestType is POST
+ * @param body body could not be empty for POST or PUT requests
* @param requestType Request type POST requests without a payload will generate a exception
- * @return
- * @throws IOException
+ * @return base class
+ * @throws IOException io exception
*/
- public HttpURLConnection getConnection(final String serviceUrl, final P postData, final String requestType) throws IOException {
- if (requestType == null || !REQUESTMETHODS.contains(requestType)) {
- throw new IllegalArgumentException(REQUEST_TYPE_MUST_BE_SET_TO_GET_OR_POST);
+ public
HttpURLConnection getConnection(final String serviceUrl, final P body, final String requestType) throws IOException {
+ return getConnection(serviceUrl, body, requestType, new HashMap<>());
+ }
+
+ /**
+ * Create a HttpURLConnection connection object
+ *
+ * @param serviceUrl URL that needs to be requested
+ * @param body body could not be empty for POST or PUT requests
+ * @param requestType Request type POST requests without a payload will generate a exception
+ * @param headers additional headers to set on the request
+ * @return base class
+ * @throws IOException io exception
+ */
+ public
HttpURLConnection getConnection(final String serviceUrl, final P body, final String requestType, final Map headers) throws IOException {
+ if (requestType == null || !REQUEST_METHODS.contains(requestType)) {
+ throw new IllegalArgumentException(String.format(REQUEST_METHOD_NOT_ALLOWED, requestType));
}
- if (postData == null && "POST".equals(requestType)) {
- throw new IllegalArgumentException("POST detected without a payload, please supply a payload with a POST request");
+ if (body == null && ("POST".equals(requestType) || "PUT".equals(requestType))) {
+ throw new IllegalArgumentException("Empty body is not allowed for POST or PUT requests");
}
final URL restService = new URL(serviceUrl);
@@ -186,61 +590,92 @@ public HttpURLConnection getConnection(final String serviceUrl, final P post
connection.setRequestProperty("Authorization", "AccessKey " + accessKey);
connection.setRequestProperty("User-agent", userAgentString);
- if ("POST".equals(requestType)) {
- connection.setRequestMethod("POST");
+ if ("POST".equals(requestType) || "PUT".equals(requestType) || "PATCH".equals(requestType)) {
+ connection.setRequestMethod(requestType);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
ObjectMapper mapper = new ObjectMapper();
- mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
-
+ mapper.setSerializationInclusion(Include.NON_NULL);
// Specifically set the date format for POST requests so scheduled
// messages and other things relying on specific date formats don't
// fail when sending.
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZ");
+ DateFormat df = getDateFormat();
mapper.setDateFormat(df);
- final String json = mapper.writeValueAsString(postData);
- connection.getOutputStream().write(json.getBytes("UTF-8"));
+ setAdditionalHeaders(connection, headers);
+
+ byte[] bodyBytes;
+ if (body instanceof byte[]) {
+ bodyBytes = (byte[]) body;
+ } else {
+ final String json = mapper.writeValueAsString(body);
+ bodyBytes = json.getBytes(StandardCharsets.UTF_8);
+ }
+ connection.getOutputStream().write(bodyBytes);
} else if ("DELETE".equals(requestType)) {
// could have just used rquestType as it is
connection.setDoOutput(false);
connection.setRequestMethod("DELETE");
connection.setRequestProperty("Content-Type", "text/plain");
+
+ setAdditionalHeaders(connection, headers);
} else {
connection.setDoOutput(false);
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "text/plain");
+
+ setAdditionalHeaders(connection, headers);
}
return connection;
}
+ private void setAdditionalHeaders(HttpURLConnection connection, Map headers) {
+ for (Map.Entry header : headers.entrySet()) {
+ connection.setRequestProperty(header.getKey(), header.getValue());
+ }
+ }
+
+ private DateFormat getDateFormat() {
+ ComparableVersion java6 = new ComparableVersion("1.6");
+ if (JAVA_VERSION.compareTo(java6) > 0) {
+ return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
+ }
+ return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZ");
+ }
+
/**
- * Get the MessageBird error report data
+ * Get the MessageBird error report data.
*
- * @param inputStream, this would usually be the errorStream from the connection
- * @return will be null when no data was found or there was a error receicing the dataset
+ * @param body Raw response body.
+ * @return Error report, or null if the body can not be deserialized.
*/
- private List getErrorReport(final InputStream inputStream) {
- ObjectMapper mapper = new ObjectMapper();
- JsonNode node = null;
- List errorReport = null;
+ private List getErrorReportOrNull(final String body) {
+ ObjectMapper objectMapper = new ObjectMapper();
try {
- node = mapper.readValue(inputStream, JsonNode.class);
- errorReport = Arrays.asList(mapper.readValue(node.get("errors"), ErrorReport[].class));
+ JsonNode jsonNode = objectMapper.readValue(body, JsonNode.class);
+ if(!jsonNode.has("errors")) {
+ return null;
+ }
+
+ ErrorReport[] errors = objectMapper.readValue(jsonNode.get("errors").toString(), ErrorReport[].class);
+
+ List result = Arrays.asList(errors);
+
+ if (result.isEmpty()) {
+ return null;
+ }
+
+ return result;
} catch (IOException e) {
- // we will ignore this and simply leave the error report null
+ return null;
}
- // In some conditions you might get a 404 but with a valid response
- if (errorReport != null && errorReport.size() == 0) {
- errorReport = null;
- }
- return errorReport;
}
/**
* Get the used access key
- * @return
+ *
+ * @return String
*/
public String getAccessKey() {
return accessKey;
@@ -248,7 +683,8 @@ public String getAccessKey() {
/**
* get the used service URL
- * @return
+ *
+ * @return String
*/
public String getServiceUrl() {
return serviceUrl;
@@ -256,7 +692,8 @@ public String getServiceUrl() {
/**
* Get the client version
- * @return
+ *
+ * @return String
*/
public String getClientVersion() {
return clientVersion;
@@ -264,7 +701,8 @@ public String getClientVersion() {
/**
* Get the user agent string
- * @return
+ *
+ * @return String
*/
public String getUserAgentString() {
return userAgentString;
@@ -275,7 +713,8 @@ public String getUserAgentString() {
* example:
* Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.0.0.1", 8080));
* messageBirdService.setProxy(proxy);
- * @param proxy
+ *
+ * @param proxy proxy
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
@@ -283,7 +722,8 @@ public void setProxy(Proxy proxy) {
/**
* Get the proxy object if set
- * @return
+ *
+ * @return Proxy
*/
public Proxy getProxy() {
return proxy;
@@ -291,21 +731,35 @@ public Proxy getProxy() {
/**
* Safe-close a input stream
- * @param is
+ *
+ * @param is input stream
*/
private void saveClose(final InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
+ // Do nothing
}
}
}
+ /**
+ * Encodes a key/value pair with percent encoding.
+ *
+ * @param key the key name to be used
+ * @param value the value to be assigned to that key
+ * @return String
+ */
+ private String encodeKeyValuePair(String key, Object value) throws UnsupportedEncodingException {
+ return URLEncoder.encode(key, String.valueOf(StandardCharsets.UTF_8)) + "=" + URLEncoder.encode(String.valueOf(value), String.valueOf(StandardCharsets.UTF_8));
+ }
+
/**
* Build a path variable for GET requests
- * @param map
- * @return
+ *
+ * @param map map for getting path variables
+ * @return String
*/
private String getPathVariables(final Map map) {
final StringBuilder bpath = new StringBuilder();
@@ -314,12 +768,34 @@ private String getPathVariables(final Map map) {
bpath.append("&");
}
try {
- bpath.append(URLEncoder.encode(param.getKey(), "UTF-8") + "=" + URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
- } catch (UnsupportedEncodingException e) {
+ // Check to see if the value is a Collection
+ if (param.getValue() instanceof Collection) {
+ // If it is, cast the value as a Collection explicitly
+ // so it can be iterated over. Its values should be
+ // appended to the querystring parameters using the
+ // original key provided (e.g., ?features=sms&features=mms)
+ Collection> col = (Collection>) param.getValue();
+ Iterator> iterator = col.iterator();
+ int count = 0;
+ // While there are still remaining iterables
+ while (iterator.hasNext()) {
+ // Append & if not the first iterable
+ if (count > 0) {
+ bpath.append("&");
+ }
+ // Append the encoded querystring key/value pair.
+ // the value is returned from the next() call
+ bpath.append(encodeKeyValuePair(param.getKey(), iterator.next()));
+ count++;
+ }
+ } else {
+ // If the value is not a collection, create the querystring value directly.
+ bpath.append(encodeKeyValuePair(param.getKey(), param.getValue()));
+ }
+ } catch (UnsupportedEncodingException exception) {
+ // Do nothing
}
}
return bpath.toString();
}
-
-
}
diff --git a/api/src/main/java/com/messagebird/Request.java b/api/src/main/java/com/messagebird/Request.java
new file mode 100644
index 00000000..46048b0e
--- /dev/null
+++ b/api/src/main/java/com/messagebird/Request.java
@@ -0,0 +1,60 @@
+package com.messagebird;
+
+import java.util.Arrays;
+
+/**
+ * Holds request data needed to calculate a signature hash for incoming
+ * webhooks.
+ *
+ * @deprecated This class is being deprecated together with {@link RequestSigner}
+ */
+@Deprecated
+public class Request {
+
+ private final String timestamp;
+ private final String queryParameters;
+ private final byte[] data;
+
+ private final static String QUERY_PARAMETERS_DELIMITER = "&";
+
+ /**
+ * Constructs a new request instance.
+ *
+ * @param timestamp Timestamp provided in the MessageBird-Request-Timestamp
+ * header.
+ * @param queryParameters Query parameters in abc=foo&def=ghi format.
+ * @param data Raw body of this request.
+ * @deprecated
+ */
+ @Deprecated
+ public Request(String timestamp, String queryParameters, byte[] data) {
+ if (timestamp == null || timestamp.isEmpty()) {
+ throw new IllegalArgumentException("Timestamp can not be null or empty");
+ }
+
+ this.timestamp = timestamp;
+ this.queryParameters = queryParameters;
+ this.data = data;
+ }
+
+ String getTimestamp() {
+ return timestamp;
+ }
+
+ String getSortedQueryParameters() {
+ String[] params = queryParameters.split(QUERY_PARAMETERS_DELIMITER);
+ Arrays.sort(params);
+ StringBuilder sortedParamsAccumulator = new StringBuilder();
+ for (int i = 0, paramsLength = params.length; i < paramsLength; i++) {
+ sortedParamsAccumulator.append(params[i]);
+ if (i < paramsLength - 1) {
+ sortedParamsAccumulator.append(QUERY_PARAMETERS_DELIMITER);
+ }
+ }
+ return sortedParamsAccumulator.toString();
+ }
+
+ byte[] getData() {
+ return data;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/RequestSigner.java b/api/src/main/java/com/messagebird/RequestSigner.java
new file mode 100644
index 00000000..d79e464c
--- /dev/null
+++ b/api/src/main/java/com/messagebird/RequestSigner.java
@@ -0,0 +1,128 @@
+package com.messagebird;
+
+import com.messagebird.exceptions.RequestSigningException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Base64;
+
+/**
+ * RequestSigner is used to verify HTTP requests and is an implementation of:
+ * https://developers.messagebird.com/docs/verify-http-requests. Retrieve your
+ * signing key at https://dashboard.messagebird.com/developers/settings.
+ *
+ * @deprecated This class is being deprecated.
+ * Use {@link RequestValidator} instead.
+ */
+@Deprecated
+public class RequestSigner {
+
+ private static final String ALGORITHM_SHA256 = "SHA-256";
+ private static final String ALGORITHM_HMAC_SHA256 = "HmacSHA256";
+ private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
+
+ private final SecretKeySpec secret;
+
+ /**
+ * Constructs a new RequestSigner instance.
+ *
+ * @param key Signing key. Can be retrieved through
+ * https://dashboard.messagebird.com/developers/settings. This
+ * is NOT your API key.
+ * @deprecated Use {@link RequestValidator#RequestValidator(String)} )} instead.
+ */
+ @Deprecated
+ public RequestSigner(byte[] key) {
+ this.secret = new SecretKeySpec(key, ALGORITHM_HMAC_SHA256);
+ }
+
+ /**
+ * Computes the signature for the provided request and determines whether
+ * it matches the expected signature (from the raw MessageBird-Signature header).
+ *
+ * @param expectedSignature Signature from the MessageBird-Signature
+ * header in its original base64 encoded state.
+ * @param request Request containing the values from the incoming webhook.
+ * @return True if the computed signature matches the expected signature.
+ * @deprecated Use {@link RequestValidator#validateSignature(String, String, byte[])} instead.
+ */
+ @Deprecated
+ public boolean isMatch(String expectedSignature, Request request) {
+ try {
+ return isMatch(Base64.getDecoder().decode(expectedSignature), request);
+ } catch (IllegalArgumentException e) {
+ throw new RequestSigningException(e);
+ }
+ }
+
+ /**
+ * Computes the signature for the provided request and determines whether
+ * it matches the expected signature
+ *
+ * @param expectedSignature Decoded (with base64) signature
+ * from the MessageBird-Signature header
+ * @param request Request containing the values from the incoming webhook.
+ * @return True if the computed signature matches the expected signature.
+ * @deprecated Use {@link RequestValidator#validateSignature(String, String, byte[])} instead.
+ */
+ @Deprecated
+ public boolean isMatch(byte[] expectedSignature, Request request) {
+ return Arrays.equals(computeSignature(request), expectedSignature);
+ }
+
+ /**
+ * Computes the signature for a request instance.
+ *
+ * @param request Request to compute signature for.
+ * @return HMAC-SHA2556 signature for the provided request.
+ */
+ private byte[] computeSignature(Request request) {
+ String timestampAndQuery = request.getTimestamp() + '\n' +
+ request.getSortedQueryParameters() + '\n';
+
+ byte[] timestampAndQueryBytes = timestampAndQuery.getBytes(CHARSET_UTF8);
+ byte[] bodyHashBytes = getSha256Hash(request.getData());
+
+ return getHmacSha256Signature(appendArrays(timestampAndQueryBytes, bodyHashBytes));
+ }
+
+ private byte[] getSha256Hash(byte[] bytes) {
+ try {
+ return MessageDigest.getInstance(ALGORITHM_SHA256).digest(bytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RequestSigningException(e);
+ }
+ }
+
+ /**
+ * Stitches the two arrays together and returns a new one.
+ *
+ * @param first Start of the new array.
+ * @param second End of the new array.
+ * @return New array based on first and second.
+ */
+ private byte[] appendArrays(byte[] first, byte[] second) {
+ byte[] result = new byte[first.length + second.length];
+ System.arraycopy(first, 0, result, 0, first.length);
+ System.arraycopy(second, 0, result, first.length, second.length);
+
+ return result;
+ }
+
+ private byte[] getHmacSha256Signature(byte[] bytes) {
+ try {
+ Mac mac = Mac.getInstance(ALGORITHM_HMAC_SHA256);
+ mac.init(secret);
+
+ return mac.doFinal(bytes);
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new RequestSigningException(e);
+ }
+ }
+}
diff --git a/api/src/main/java/com/messagebird/RequestValidator.java b/api/src/main/java/com/messagebird/RequestValidator.java
new file mode 100644
index 00000000..24ae6343
--- /dev/null
+++ b/api/src/main/java/com/messagebird/RequestValidator.java
@@ -0,0 +1,204 @@
+package com.messagebird;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier.BaseVerification;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.auth0.jwt.interfaces.JWTVerifier;
+import com.messagebird.exceptions.RequestValidationException;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.Clock;
+
+/**
+ * RequestValidator validates request signature signed by MessageBird services.
+ *
+ * @see Verify HTTP Requests
+ */
+public class RequestValidator {
+
+ /**
+ * Signature of signed request is set with header name 'MessageBird-Signature-JWT'
+ */
+ public static final String SIGNATURE_HEADER = "MessageBird-Signature-JWT";
+ private static final String ALGORITHM_SHA256 = "SHA-256";
+ private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f'};
+
+ private final Algorithm HMAC256, HMAC384, HMAC512;
+
+ /**
+ * This field instructs Validator to not validate url_hash claim.
+ * It is recommended to not skip URL validation to ensure high security.
+ * but the ability to skip URL validation is necessary in some cases, e.g.
+ * your service is behind proxy or when you want to validate it yourself.
+ * Note that when true, no query parameters should be trusted.
+ * Defaults to false.
+ */
+ private final boolean skipURLValidation;
+
+ /**
+ * RequestValidator validates request signature with a customer signature key.
+ *
+ * @param signatureKey customer signature key. Can be retrieved through
+ * Developer Settings.
+ * This is NOT your API key.
+ * @see Verify HTTP Requests
+ */
+ public RequestValidator(String signatureKey) {
+ this(signatureKey, false);
+ }
+
+ /**
+ * RequestValidator validates webhook signature with a customer signature key.
+ *
+ * @param signatureKey customer signature key. Can be retrieved through
+ * Developer Settings.
+ * This is NOT your API key.
+ * @param skipURLValidation whether url_hash claim validation should be skipped.
+ * Note that when true, no query parameters should be trusted.
+ * @see Verify HTTP Requests
+ */
+ public RequestValidator(String signatureKey, boolean skipURLValidation) {
+ this.HMAC256 = Algorithm.HMAC256(signatureKey);
+ this.HMAC384 = Algorithm.HMAC384(signatureKey);
+ this.HMAC512 = Algorithm.HMAC512(signatureKey);
+ this.skipURLValidation = skipURLValidation;
+ }
+
+ /**
+ * Returns raw signature payload after validating a signature successfully,
+ * otherwise throws {@code RequestValidationException}.
+ *
+ * This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and
+ * a specific account.
+ * The JWT contains the following claims:
+ *
+ *
+ * - "url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.
+ * - "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.
+ * - "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).
+ * - "nbf" - the not before timestamp.
+ * - "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.
+ * - "iss" - the issuer name, always MessageBird.
+ *
+ *
+ * @param clock custom {@link Clock} instance to validate timestamp claims.
+ * @param signature the actual signature.
+ * @param url the raw url including the protocol, hostname and query string,
+ * {@code https://example.com/?example=42}.
+ * @param requestBody the raw request body.
+ * @return raw signature payload as {@link DecodedJWT} object.
+ * @throws RequestValidationException when the signature is invalid.
+ * @see Verify HTTP Requests
+ */
+ public DecodedJWT validateSignature(Clock clock, String signature, String url, byte[] requestBody)
+ throws RequestValidationException {
+ if (signature == null || signature.length() == 0)
+ throw new RequestValidationException("The signature can not be empty.");
+
+ if (!skipURLValidation && (url == null || url.length() == 0))
+ throw new RequestValidationException("The url can not be empty.");
+
+ DecodedJWT jwt = JWT.decode(signature);
+
+ Algorithm algorithm;
+ switch (jwt.getAlgorithm()) {
+ case "HS256":
+ algorithm = HMAC256;
+ break;
+ case "HS384":
+ algorithm = HMAC384;
+ break;
+ case "HS512":
+ algorithm = HMAC512;
+ break;
+ default:
+ throw new RequestValidationException(String.format("The signing method '%s' is invalid.", jwt.getAlgorithm()));
+ }
+
+ BaseVerification builder = (BaseVerification) JWT.require(algorithm)
+ .withIssuer("MessageBird")
+ .ignoreIssuedAt()
+ .acceptLeeway(1);
+
+ if (!skipURLValidation)
+ builder.withClaim("url_hash", calculateSha256(url.getBytes()));
+
+ Claim payloadHashClaim = jwt.getClaim("payload_hash");
+ boolean payloadHashClaimExist = !(payloadHashClaim.isNull() || payloadHashClaim.isMissing());
+ if (requestBody != null && requestBody.length > 0) {
+ if (!payloadHashClaimExist) {
+ throw new RequestValidationException("The Claim 'payload_hash' is not set but payload is present.");
+ }
+ builder.withClaim("payload_hash", calculateSha256(requestBody));
+ } else if (payloadHashClaimExist) {
+ throw new RequestValidationException("The Claim 'payload_hash' is set but actual payload is missing.");
+ }
+
+ JWTVerifier verifier = clock == null ? builder.build() : builder.build(clock);
+
+ try {
+ return verifier.verify(jwt);
+ } catch (SignatureVerificationException e) {
+ throw new RequestValidationException("Signature is invalid.", e);
+ } catch (JWTVerificationException e) {
+ throw new RequestValidationException(e.getMessage(), e.getCause());
+ }
+ }
+
+ /**
+ * Returns raw signature payload after validating a signature successfully,
+ * otherwise throws {@code RequestValidationException}.
+ *
+ * @param signature the actual signature.
+ * @param url the raw url including the protocol, hostname and query string,
+ * {@code https://example.com/?example=42}.
+ * @param requestBody the raw request body.
+ * @return raw signature payload as {@link DecodedJWT} object.
+ * @throws RequestValidationException when the signature is invalid.
+ * @see RequestValidator#validateSignature(Clock, String, String, byte[])
+ */
+ public DecodedJWT validateSignature(String signature, String url, byte[] requestBody)
+ throws RequestValidationException {
+ return validateSignature(null, signature, url, requestBody);
+ }
+
+ /**
+ * Validates request signature with URL validation disabled.
+ * Note that no query parameters should be trusted and this only works if {@code RequestValidator} is constructed
+ * with {@code skipURLValidation} set to true.
+ *
+ * @param signature the actual signature.
+ * @param requestBody the raw request body.
+ * @return raw signature payload as {@link DecodedJWT} object.
+ * @throws RequestValidationException when the signature is invalid.
+ * @see RequestValidator#validateSignature(String, String, byte[])
+ */
+ public DecodedJWT validateSignature(String signature, byte[] requestBody)
+ throws RequestValidationException {
+ return validateSignature(null, signature, null, requestBody);
+ }
+
+ private static String calculateSha256(byte[] bytes) {
+ try {
+ return encodeHex(MessageDigest.getInstance(ALGORITHM_SHA256).digest(bytes));
+ } catch (NoSuchAlgorithmException e) {
+ throw new RequestValidationException(e);
+ }
+ }
+
+ private static String encodeHex(final byte[] data) {
+ final int l = data.length;
+ final char[] out = new char[l << 1];
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
+ out[j++] = HEX_DIGITS[0x0F & data[i]];
+ }
+ return new String(out);
+ }
+}
diff --git a/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java b/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java
new file mode 100644
index 00000000..d69a1922
--- /dev/null
+++ b/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java
@@ -0,0 +1,21 @@
+package com.messagebird.exceptions;
+
+/**
+ * Thrown if an error occurs during request signing.
+ *
+ * @deprecated This class is being deprecated together with {@link com.messagebird.RequestSigner}
+ */
+@Deprecated
+public class RequestSigningException extends RuntimeException {
+
+ public RequestSigningException() {
+ }
+
+ public RequestSigningException(String message) {
+ super(message);
+ }
+
+ public RequestSigningException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java b/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java
new file mode 100644
index 00000000..f759b211
--- /dev/null
+++ b/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java
@@ -0,0 +1,22 @@
+package com.messagebird.exceptions;
+
+/**
+ * Thrown if an error occurs during request signing.
+ */
+public class RequestValidationException extends RuntimeException {
+
+ public RequestValidationException() {
+ }
+
+ public RequestValidationException(String message) {
+ super(message);
+ }
+
+ public RequestValidationException(Throwable cause) {
+ super(cause);
+ }
+
+ public RequestValidationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/AccessKey.java b/api/src/main/java/com/messagebird/objects/AccessKey.java
new file mode 100644
index 00000000..4d529711
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/AccessKey.java
@@ -0,0 +1,98 @@
+package com.messagebird.objects;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class AccessKey {
+ private String id;
+ @JsonProperty("access_key")
+ private String accessKey;
+ private String mode;
+ private String description;
+ @JsonProperty("core_user_id")
+ private int coreUserId;
+ @JsonProperty("user_id")
+ private int userId;
+ @JsonProperty("external_id")
+ private int externalId;
+ private List roles;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAccessKey() {
+ return accessKey;
+ }
+
+ public void setAccessKey(String accessKey) {
+ this.accessKey = accessKey;
+ }
+
+ public String getMode() {
+ return mode;
+ }
+
+ public void setMode(String mode) {
+ this.mode = mode;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public int getCoreUserId() {
+ return coreUserId;
+ }
+
+ public void setCoreUserId(int coreUserId) {
+ this.coreUserId = coreUserId;
+ }
+
+ public int getUserId() {
+ return userId;
+ }
+
+ public void setUserId(int userId) {
+ this.userId = userId;
+ }
+
+ public int getExternalId() {
+ return externalId;
+ }
+
+ public void setExternalId(int externalId) {
+ this.externalId = externalId;
+ }
+
+ public List getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+ @Override
+ public String toString() {
+ return "AccessKey{" +
+ "id='" + id + '\'' +
+ ", access_key='" + accessKey + '\'' +
+ ", mod='" + mode + '\'' +
+ ", description='" + description + '\'' +
+ ", core_user_id=" + coreUserId +
+ ", user_id=" + userId +
+ ", external_id=" + externalId +
+ ", roles=" + roles +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/Balance.java b/api/src/main/java/com/messagebird/objects/Balance.java
index 993bcb6c..ddf653cb 100644
--- a/api/src/main/java/com/messagebird/objects/Balance.java
+++ b/api/src/main/java/com/messagebird/objects/Balance.java
@@ -14,7 +14,7 @@ public class Balance implements Serializable{
private String payment;
private String type;
- private Integer amount;
+ private float amount;
public Balance() {
}
@@ -48,7 +48,7 @@ public String getType() {
* The amount of balance of the payment type. When postpaid is your payment method, the amount will be 0.
* @return
*/
- public Integer getAmount() {
+ public float getAmount() {
return amount;
}
}
diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java
new file mode 100644
index 00000000..40ec38a4
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java
@@ -0,0 +1,53 @@
+package com.messagebird.objects;
+
+import java.util.List;
+
+public class ChildAccountCreateResponse extends ChildAccountResponse{
+ private List accessKeys;
+ private String signingKey;
+ private String invoiceAggregation;
+ private String paymentMoment;
+
+ public List getAccessKeys() {
+ return accessKeys;
+ }
+
+ public void setAccessKeys(List accessKeys) {
+ this.accessKeys = accessKeys;
+ }
+
+ public String getSigningKey() {
+ return signingKey;
+ }
+
+ public void setSigningKey(String signingKey) {
+ this.signingKey = signingKey;
+ }
+
+ public String getInvoiceAggregation() {
+ return invoiceAggregation;
+ }
+
+ public void setInvoiceAggregation(String invoiceAggregation) {
+ this.invoiceAggregation = invoiceAggregation;
+ }
+
+ public String getPaymentMoment() {
+ return paymentMoment;
+ }
+
+ public void setPaymentMoment(String paymentMoment) {
+ this.paymentMoment = paymentMoment;
+ }
+
+ @Override
+ public String toString() {
+ return "ChildAccountCreateResponse{" +
+ "id='" + getId() + '\'' +
+ ", name='" + getName() + '\'' +
+ ", accessKeys=" + accessKeys + '\'' +
+ ", invoiceAggregation='" + invoiceAggregation + '\'' +
+ ", paymentMoment='" + paymentMoment + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java
new file mode 100644
index 00000000..e39d5969
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java
@@ -0,0 +1,22 @@
+package com.messagebird.objects;
+
+public class ChildAccountDetailedResponse extends ChildAccountResponse{
+ private String invoiceAggregation;
+
+ public String getInvoiceAggregation() {
+ return invoiceAggregation;
+ }
+
+ public void setInvoiceAggregation(String invoiceAggregation) {
+ this.invoiceAggregation = invoiceAggregation;
+ }
+
+ @Override
+ public String toString() {
+ return "ChildAccountDetailedResponse{" +
+ "id='" + getId() + '\'' +
+ ", name='" + getName() + '\'' +
+ ", invoiceAggregation='" + invoiceAggregation + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java b/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java
new file mode 100644
index 00000000..aa0e9e5f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java
@@ -0,0 +1,13 @@
+package com.messagebird.objects;
+
+public class ChildAccountRequest {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java
new file mode 100644
index 00000000..de232f05
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java
@@ -0,0 +1,34 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+public class ChildAccountResponse implements Serializable {
+ private static final long serialVersionUID = -8605510461438669942L;
+
+ private String id;
+ private String name;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "ChildAccountResponse{" +
+ "id='" + id + '\'' +
+ ", name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/Contact.java b/api/src/main/java/com/messagebird/objects/Contact.java
new file mode 100644
index 00000000..92c1a1e9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Contact.java
@@ -0,0 +1,116 @@
+package com.messagebird.objects;
+
+import java.util.Date;
+
+/**
+ * Complete Contact object. To create and update contacts, use ContactRequest.
+ */
+public class Contact {
+
+ private String id;
+ private String href;
+ private String msisdn;
+ private String firstName;
+ private String lastName;
+ private GroupReference groups;
+ private MessageReference messages;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+ private CustomDetails customDetails;
+
+ public String getId() {
+ return id;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public String getMsisdn() {
+ return msisdn;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public CustomDetails getCustomDetails() {
+ return customDetails;
+ }
+
+ public GroupReference getGroups() {
+ return groups;
+ }
+
+ public MessageReference getMessages() {
+ return messages;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public void setMsisdn(String msisdn) {
+ this.msisdn = msisdn;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public void setCustomDetails(CustomDetails customDetails) {
+ this.customDetails = customDetails;
+ }
+
+ public void setGroups(GroupReference groups) {
+ this.groups = groups;
+ }
+
+ public void setMessages(MessageReference messages) {
+ this.messages = messages;
+ }
+
+ @Override
+ public String toString() {
+ return "Contact{" +
+ "id='" + id + '\'' +
+ ", href='" + href + '\'' +
+ ", msisdn='" + msisdn + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", customDetails=" + customDetails +
+ ", groups=" + groups +
+ ", messages=" + messages +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/ContactList.java b/api/src/main/java/com/messagebird/objects/ContactList.java
new file mode 100644
index 00000000..1f90251c
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ContactList.java
@@ -0,0 +1,9 @@
+package com.messagebird.objects;
+
+/**
+ * Represents a listing of Contact objects, along with pagination details.
+ */
+public class ContactList extends ListBase {
+
+ //
+}
diff --git a/api/src/main/java/com/messagebird/objects/ContactReference.java b/api/src/main/java/com/messagebird/objects/ContactReference.java
new file mode 100644
index 00000000..983b5723
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ContactReference.java
@@ -0,0 +1,23 @@
+package com.messagebird.objects;
+
+public class ContactReference {
+
+ private String href;
+ private int totalCount;
+
+ public String getHref() {
+ return href;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ @Override
+ public String toString() {
+ return "ContactReference{" +
+ "href='" + href + '\'' +
+ ", totalCount=" + totalCount +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/ContactRequest.java b/api/src/main/java/com/messagebird/objects/ContactRequest.java
new file mode 100644
index 00000000..a61688ca
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/ContactRequest.java
@@ -0,0 +1,87 @@
+package com.messagebird.objects;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+/**
+ * Contains writable values for Contact objects.
+ */
+public class ContactRequest {
+
+ private String msisdn;
+ private String firstName;
+ private String lastName;
+
+ // Flatten the customDetails object to the Contact when serializing,
+ // see: https://developers.messagebird.com/docs/contacts#create-a-contact
+ @JsonUnwrapped
+ private CustomDetails customDetails;
+
+ public ContactRequest(String msisdn, String firstName, String lastName, CustomDetails customDetails) {
+ this.msisdn = msisdn;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.customDetails = customDetails;
+ }
+
+ public ContactRequest(String msisdn, String firstName, String lastName) {
+ this.msisdn = msisdn;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public ContactRequest(String msisdn) {
+ this.msisdn = msisdn;
+ }
+
+ public ContactRequest() {
+ //
+ }
+
+ /**
+ * The phone number of contact.
+ */
+ public String getMsisdn() {
+ return msisdn;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public CustomDetails getCustomDetails() {
+ return customDetails;
+ }
+
+ /**
+ * The phone number of contact.
+ */
+ public void setMsisdn(String msisdn) {
+ this.msisdn = msisdn;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public void setCustomDetails(CustomDetails customDetails) {
+ this.customDetails = customDetails;
+ }
+
+ @Override
+ public String toString() {
+ return "ContactRequest{" +
+ "msisdn='" + msisdn + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", customDetails=" + customDetails +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/CustomDetails.java b/api/src/main/java/com/messagebird/objects/CustomDetails.java
new file mode 100644
index 00000000..3577f81e
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/CustomDetails.java
@@ -0,0 +1,54 @@
+package com.messagebird.objects;
+
+/**
+ * Custom fields of this contact.
+ */
+public class CustomDetails {
+
+ private String custom1;
+ private String custom2;
+ private String custom3;
+ private String custom4;
+
+ public String getCustom1() {
+ return custom1;
+ }
+
+ public String getCustom2() {
+ return custom2;
+ }
+
+ public String getCustom3() {
+ return custom3;
+ }
+
+ public String getCustom4() {
+ return custom4;
+ }
+
+ public void setCustom1(String custom1) {
+ this.custom1 = custom1;
+ }
+
+ public void setCustom2(String custom2) {
+ this.custom2 = custom2;
+ }
+
+ public void setCustom3(String custom3) {
+ this.custom3 = custom3;
+ }
+
+ public void setCustom4(String custom4) {
+ this.custom4 = custom4;
+ }
+
+ @Override
+ public String toString() {
+ return "CustomDetails{" +
+ "custom1='" + custom1 + '\'' +
+ ", custom2='" + custom2 + '\'' +
+ ", custom3='" + custom3 + '\'' +
+ ", custom4='" + custom4 + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/DataCodingType.java b/api/src/main/java/com/messagebird/objects/DataCodingType.java
index 986cea4a..3f78d21d 100644
--- a/api/src/main/java/com/messagebird/objects/DataCodingType.java
+++ b/api/src/main/java/com/messagebird/objects/DataCodingType.java
@@ -7,7 +7,8 @@
*/
public enum DataCodingType {
plain("plain"),
- unicode("unicode");
+ unicode("unicode"),
+ auto("auto");
final String value;
diff --git a/api/src/main/java/com/messagebird/objects/ErrorReport.java b/api/src/main/java/com/messagebird/objects/ErrorReport.java
index 00c74aa9..afd7dcf9 100644
--- a/api/src/main/java/com/messagebird/objects/ErrorReport.java
+++ b/api/src/main/java/com/messagebird/objects/ErrorReport.java
@@ -1,32 +1,46 @@
package com.messagebird.objects;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.io.Serializable;
+
/**
* When MessageBird returns a 4xx, you will find a list of any error codes in your return dataset.
* you will receive a list of errors from the API in such case.
*
* Created by rvt on 1/5/15.
*/
-public class ErrorReport {
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class ErrorReport implements Serializable {
+
+ private static final long serialVersionUID = -8611665867089703268L;
+
private Integer code;
private String description;
private String parameter;
+ private String message;
public ErrorReport() {
}
- public ErrorReport(Integer code, String description, String parameter) {
+ public ErrorReport(Integer code, String description, String parameter, String message) {
this.code = code;
this.description = description;
this.parameter = parameter;
+ this.message = message;
}
@Override
public String toString() {
- return "ErrorReport{" +
- "code=" + code +
- ", description='" + description + '\'' +
- ", parameter='" + parameter + '\'' +
- '}';
+ String str = "ErrorReport{code=" + code;
+ if (message != null && !message.isEmpty()) {
+ str = str.concat(", message='" + message + "'");
+ } else {
+ str = str.concat(", description='" + description + "'");
+ str = str.concat(", parameter='" + parameter + "'");
+ }
+ str = str.concat("}");
+ return str;
}
/**
@@ -53,4 +67,11 @@ public String getParameter() {
return parameter;
}
+ /**
+ * message not null for only voice API response
+ * @return
+ */
+ public String getMessage() {
+ return message;
+ }
}
diff --git a/api/src/main/java/com/messagebird/objects/FileUploadResponse.java b/api/src/main/java/com/messagebird/objects/FileUploadResponse.java
new file mode 100644
index 00000000..29155a63
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/FileUploadResponse.java
@@ -0,0 +1,21 @@
+package com.messagebird.objects;
+
+public class FileUploadResponse {
+
+ private String id;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "FileUploadResponse{" +
+ "id='" + id + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/Group.java b/api/src/main/java/com/messagebird/objects/Group.java
new file mode 100644
index 00000000..388496a6
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Group.java
@@ -0,0 +1,52 @@
+package com.messagebird.objects;
+
+import java.util.Date;
+
+/**
+ * Complete Group object. To create and update groups, use GroupRequest.
+ */
+public class Group {
+
+ private String id;
+ private String href;
+ private String name;
+ private ContactReference contacts;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+
+ public String getId() {
+ return id;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ContactReference getContacts() {
+ return contacts;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ @Override
+ public String toString() {
+ return "Group{" +
+ "id='" + id + '\'' +
+ ", href='" + href + '\'' +
+ ", name='" + name + '\'' +
+ ", contacts=" + contacts +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/GroupList.java b/api/src/main/java/com/messagebird/objects/GroupList.java
new file mode 100644
index 00000000..352f3869
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/GroupList.java
@@ -0,0 +1,9 @@
+package com.messagebird.objects;
+
+/**
+ * Represents a listing of Group objects, along with pagination details.
+ */
+public class GroupList extends ListBase {
+
+ //
+}
diff --git a/api/src/main/java/com/messagebird/objects/GroupReference.java b/api/src/main/java/com/messagebird/objects/GroupReference.java
new file mode 100644
index 00000000..110c35aa
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/GroupReference.java
@@ -0,0 +1,31 @@
+package com.messagebird.objects;
+
+public class GroupReference {
+
+ private String href;
+ private int totalCount;
+
+ public String getHREF() {
+ return href;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ public void setHREF(String href) {
+ this.href = href;
+ }
+
+ public void setTotalCount(int totalCount) {
+ this.totalCount = totalCount;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupReference{" +
+ "href='" + href + '\'' +
+ ", totalCount=" + totalCount +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/GroupRequest.java b/api/src/main/java/com/messagebird/objects/GroupRequest.java
new file mode 100644
index 00000000..c2f03b08
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/GroupRequest.java
@@ -0,0 +1,32 @@
+package com.messagebird.objects;
+
+/**
+ * Contains writable values for the Groups API.
+ */
+public class GroupRequest {
+
+ private String name;
+
+ public GroupRequest(String name) {
+ this.name = name;
+ }
+
+ public GroupRequest() {
+ //
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupRequest{" +
+ "name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/IfMachineType.java b/api/src/main/java/com/messagebird/objects/IfMachineType.java
index c967d3dd..012f37f0 100644
--- a/api/src/main/java/com/messagebird/objects/IfMachineType.java
+++ b/api/src/main/java/com/messagebird/objects/IfMachineType.java
@@ -1,7 +1,7 @@
package com.messagebird.objects;
-import org.codehaus.jackson.annotate.JsonCreator;
-import org.codehaus.jackson.annotate.JsonValue;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
/**
* IfMachine class for Voice Messages
diff --git a/api/src/main/java/com/messagebird/objects/Language.java b/api/src/main/java/com/messagebird/objects/Language.java
index 6ce76027..ccb63351 100644
--- a/api/src/main/java/com/messagebird/objects/Language.java
+++ b/api/src/main/java/com/messagebird/objects/Language.java
@@ -1,5 +1,7 @@
package com.messagebird.objects;
+import com.fasterxml.jackson.annotation.JsonValue;
+
/**
* Created by faizan on 09/12/15.
*/
@@ -11,7 +13,7 @@ public enum Language {
EN_US("en-us"),
ES_ES("es-es"),
FR_FR("fr-fr"),
- RU_RU("ru_ru"),
+ RU_RU("ru-ru"),
ZH_CN("zh-cn"),
EN_AU("en-au"),
ES_MX("es-mx"),
@@ -25,13 +27,21 @@ public enum Language {
PT_BR("pt-br"),
RO_RO("ro-ro");
- private String code;
+ final String code;
Language(String code) {
this.code = code;
}
+ @JsonValue
+ public String getCode() {
+ return code;
+ }
+
+ @Override
public String toString() {
- return this.code;
+ return "Language{" +
+ "code='" + code + '\'' +
+ '}';
}
}
diff --git a/api/src/main/java/com/messagebird/objects/MessageListBase.java b/api/src/main/java/com/messagebird/objects/ListBase.java
similarity index 73%
rename from api/src/main/java/com/messagebird/objects/MessageListBase.java
rename to api/src/main/java/com/messagebird/objects/ListBase.java
index 8cb0b823..da54e709 100644
--- a/api/src/main/java/com/messagebird/objects/MessageListBase.java
+++ b/api/src/main/java/com/messagebird/objects/ListBase.java
@@ -3,22 +3,24 @@
import java.util.List;
/**
- * Base class for retrieval of message lists
+ * Provides an object to deserialize to for endpoints returning listings.
*
- * Created by rvt on 1/8/15.
+ * @param Type of the items, e.g. Contact or Message.
*/
-public class MessageListBase {
+public class ListBase {
+
private Integer offset;
private Integer limit;
private Integer totalCount;
private Links links;
+ private List items;
- public MessageListBase() {
+ public ListBase() {
}
@Override
public String toString() {
- return "MessageListBase{" +
+ return "ListBase{" +
"offset=" + offset +
", limit=" + limit +
", totalCount=" + totalCount +
@@ -27,7 +29,6 @@ public String toString() {
'}';
}
- private List items;
public Integer getOffset() {
return offset;
@@ -48,4 +49,8 @@ public Links getLinks() {
public List getItems() {
return items;
}
+
+ public void setItems(List items) {
+ this.items = items;
+ }
}
diff --git a/api/src/main/java/com/messagebird/objects/MClassType.java b/api/src/main/java/com/messagebird/objects/MClassType.java
index 3eba1c16..4474b15e 100644
--- a/api/src/main/java/com/messagebird/objects/MClassType.java
+++ b/api/src/main/java/com/messagebird/objects/MClassType.java
@@ -1,7 +1,7 @@
package com.messagebird.objects;
-import org.codehaus.jackson.annotate.JsonCreator;
-import org.codehaus.jackson.annotate.JsonValue;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
/**
* MClass type
@@ -30,7 +30,6 @@ public Integer toJson() {
return getValue();
}
- @JsonCreator
public static MClassType forValue(String value) {
if ("0".equals(value)) {
return flash;
diff --git a/api/src/main/java/com/messagebird/objects/MessageList.java b/api/src/main/java/com/messagebird/objects/MessageList.java
index ad3e4395..96507b9c 100644
--- a/api/src/main/java/com/messagebird/objects/MessageList.java
+++ b/api/src/main/java/com/messagebird/objects/MessageList.java
@@ -4,7 +4,7 @@
* Class to retreive a list of Messages
* Created by rvt on 1/8/15.
*/
-public class MessageList extends MessageListBase {
-
+public class MessageList extends ListBase {
+ //
}
diff --git a/api/src/main/java/com/messagebird/objects/MessageReference.java b/api/src/main/java/com/messagebird/objects/MessageReference.java
new file mode 100644
index 00000000..26778292
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/MessageReference.java
@@ -0,0 +1,37 @@
+package com.messagebird.objects;
+
+public class MessageReference {
+
+ private String href;
+ private int totalCount;
+ private String lastMessageId;
+
+ public String getHREF() {
+ return href;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ public void setHREF(String href) {
+ this.href = href;
+ }
+
+ public void setTotalCount(int totalCount) {
+ this.totalCount = totalCount;
+ }
+
+ public String getLastMessageId() {
+ return lastMessageId;
+ }
+
+ @Override
+ public String toString() {
+ return "MessageReference{" +
+ "href='" + href + '\'' +
+ ", totalCount=" + totalCount +
+ ", lastMessageId='" + lastMessageId + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/MessageResponse.java b/api/src/main/java/com/messagebird/objects/MessageResponse.java
index d662f725..aa104e52 100644
--- a/api/src/main/java/com/messagebird/objects/MessageResponse.java
+++ b/api/src/main/java/com/messagebird/objects/MessageResponse.java
@@ -1,6 +1,9 @@
package com.messagebird.objects;
+import org.jetbrains.annotations.Nullable;
+
import java.io.Serializable;
+import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
@@ -57,7 +60,8 @@ public String toString() {
/**
* An unique random ID which is created on the MessageBird platform and is returned upon creation of the object.
- * @return
+ *
+ * @return String
*/
@Override
public String getId() {
@@ -74,7 +78,7 @@ public String getHref() {
* mt: mobile terminated (sent to mobile)
* mo: mobile originated (received from mobile)
*
- * @return
+ * @return String
*/
public String getDirection() {
return direction;
@@ -83,7 +87,7 @@ public String getDirection() {
/**
* The type of message. Values can be: sms, binary, premium, or flash
*
- * @return
+ * @return MsgType
*/
public MsgType getType() {
return type;
@@ -92,7 +96,7 @@ public MsgType getType() {
/**
* The sender of the message. This can be a telephone number (including country code) or an alphanumeric string. In case of an alphanumeric string, the maximum length is 11 characters.
*
- * @return
+ * @return String
*/
public String getOriginator() {
return originator;
@@ -121,7 +125,7 @@ public void setReference(String reference) {
/**
* The amount of seconds that the message is valid. If a message is not delivered within this time, the message will be discarded.
*
- * @return
+ * @return Integer
*/
public Integer getValidity() {
return validity;
@@ -130,7 +134,7 @@ public Integer getValidity() {
/**
* The SMS route that is used to send the message.
*
- * @return
+ * @return Integer
*/
public Integer getGateway() {
return gateway;
@@ -139,7 +143,7 @@ public Integer getGateway() {
/**
* The datacoding used, can be plain or unicode
*
- * @return
+ * @return DataCodingType
*/
public DataCodingType getDatacoding() {
return datacoding;
@@ -148,7 +152,7 @@ public DataCodingType getDatacoding() {
/**
* Indicated the message type. 1 is a normal message, 0 is a flash message.
*
- * @return
+ * @return MClassType
*/
public MClassType getMclass() {
return mclass;
@@ -157,7 +161,7 @@ public MClassType getMclass() {
/**
* The scheduled date and time of the message
*
- * @return
+ * @return Date
*/
public Date getScheduledDatetime() {
return scheduledDatetime;
@@ -166,7 +170,7 @@ public Date getScheduledDatetime() {
/**
* The date and time of the creation of the message
*
- * @return
+ * @return Date
*/
public Date getCreatedDatetime() {
return createdDatetime;
@@ -179,7 +183,8 @@ public Recipients getRecipients() {
/**
* Return type details object
- * @return
+ *
+ * @return Map
*/
public Map getTypeDetails() {
return typeDetails;
@@ -192,7 +197,7 @@ public Map getTypeDetails() {
static public class Recipients implements Serializable {
private static final long serialVersionUID = 547164972757802213L;
-
+ private Integer totalCount;
private Integer totalSentCount;
private Integer totalDeliveredCount;
private Integer totalDeliveryFailedCount;
@@ -204,17 +209,22 @@ public Recipients() {
@Override
public String toString() {
return "Recipients{" +
- "totalSentCount=" + totalSentCount +
+ "totalCount=" + totalCount +
+ ", totalSentCount=" + totalSentCount +
", totalDeliveredCount=" + totalDeliveredCount +
", totalDeliveryFailedCount=" + totalDeliveryFailedCount +
", items=" + items +
'}';
}
+ public Integer getTotalCount() {
+ return totalCount;
+ }
+
/**
* The count of recipients that have the message pending (status sent, and buffered).
*
- * @return
+ * @return Integer
*/
public Integer getTotalSentCount() {
return totalSentCount;
@@ -223,7 +233,7 @@ public Integer getTotalSentCount() {
/**
* The count of recipients where the message is delivered (status delivered).
*
- * @return
+ * @return Integer
*/
public Integer getTotalDeliveredCount() {
return totalDeliveredCount;
@@ -232,7 +242,7 @@ public Integer getTotalDeliveredCount() {
/**
* The count of recipients where the delivery has failed (status delivery_failed).
*
- * @return
+ * @return Integer
*/
public Integer getTotalDeliveryFailedCount() {
return totalDeliveryFailedCount;
@@ -241,7 +251,7 @@ public Integer getTotalDeliveryFailedCount() {
/**
* An array of recipient hashes
*
- * @return
+ * @return List
*/
public List getItems() {
return items;
@@ -256,8 +266,20 @@ static public class Items implements Serializable {
private static final long serialVersionUID = -4104837036540050532L;
private BigInteger recipient;
+ private BigInteger originator;
private String status;
private Date statusDatetime;
+ private String recipientCountry;
+ private Integer recipientCountryPrefix;
+ private String recipientOperator;
+ private Integer messageLength;
+ private String statusReason;
+ @Nullable
+ private Price price;
+ private String mccmnc;
+ private String mcc;
+ private String mnc;
+ private int messagePartCount;
public Items() {
}
@@ -266,15 +288,26 @@ public Items() {
public String toString() {
return "Items{" +
"recipient=" + recipient +
+ ", originator=" + originator +
", status='" + status + '\'' +
", statusDatetime=" + statusDatetime +
- "}";
+ ", recipientCountry='" + recipientCountry + '\'' +
+ ", recipientCountryPrefix=" + recipientCountryPrefix +
+ ", recipientOperator='" + recipientOperator + '\'' +
+ ", messageLength=" + messageLength +
+ ", statusReason='" + statusReason + '\'' +
+ ", price=" + price +
+ ", mccmnc='" + mccmnc + '\'' +
+ ", mcc='" + mcc + '\'' +
+ ", mnc='" + mnc + '\'' +
+ ", messagePartCount=" + messagePartCount +
+ '}';
}
/**
* The msisdn of the recipient
*
- * @return
+ * @return BigInteger
*/
public BigInteger getRecipient() {
return recipient;
@@ -283,7 +316,7 @@ public BigInteger getRecipient() {
/**
* The status of the message sent to the recipient. Possible values: scheduled, sent, buffered, delivered, and delivery_failed
*
- * @return
+ * @return String
*/
public String getStatus() {
return status;
@@ -292,12 +325,90 @@ public String getStatus() {
/**
* The datum time of the last status
*
- * @return
+ * @return Date
*/
public Date getStatusDatetime() {
return statusDatetime;
}
+ public Price getPrice() {
+ return price;
+ }
+
+ public BigInteger getOriginator() {
+ return originator;
+ }
+
+ public String getRecipientCountry() {
+ return recipientCountry;
+ }
+
+ public Integer getRecipientCountryPrefix() {
+ return recipientCountryPrefix;
+ }
+
+ public String getRecipientOperator() {
+ return recipientOperator;
+ }
+
+ public Integer getMessageLength() {
+ return messageLength;
+ }
+
+ public String getStatusReason() {
+ return statusReason;
+ }
+
+ public String getMccmnc() {
+ return mccmnc;
+ }
+
+ public String getMcc() {
+ return mcc;
+ }
+
+ public String getMnc() {
+ return mnc;
+ }
+
+ public int getMessagePartCount() {
+ return messagePartCount;
+ }
+ }
+
+ /**
+ * Response price of items
+ */
+ static public class Price implements Serializable {
+
+ private static final long serialVersionUID = -4104837036540050532L;
+
+ private BigDecimal amount;
+ private String currency;
+
+ public Price() {
+ }
+
+ @Override
+ public String toString() {
+ return "Price{" +
+ "amount=" + amount +
+ ", currency=" + currency +
+ "}";
+ }
+
+ public float getAmount() {
+ return amount.floatValue();
+ }
+
+ public BigDecimal getAmountDecimal() {
+ return amount;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
}
}
diff --git a/api/src/main/java/com/messagebird/objects/MsgType.java b/api/src/main/java/com/messagebird/objects/MsgType.java
index dc9af724..bfc421c8 100644
--- a/api/src/main/java/com/messagebird/objects/MsgType.java
+++ b/api/src/main/java/com/messagebird/objects/MsgType.java
@@ -7,6 +7,7 @@
*/
public enum MsgType {
sms("sms"),
+ mms("mms"),
binary("binary"),
premium("premium"),
flash("flash");
diff --git a/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java b/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java
new file mode 100644
index 00000000..3733cf69
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java
@@ -0,0 +1,60 @@
+package com.messagebird.objects;
+
+import java.math.BigDecimal;
+
+public class OutboundSmsPrice {
+ private BigDecimal price;
+ private String currencyCode;
+ private String mccmnc;
+ private String mcc;
+ private String mnc;
+ private String countryName;
+ private String countryIsoCode;
+ private String operatorName;
+
+ public BigDecimal getPrice() {
+ return price;
+ }
+
+ public String getCurrencyCode() {
+ return currencyCode;
+ }
+
+ public String getMccmnc() {
+ return mccmnc;
+ }
+
+ public String getMcc() {
+ return mcc;
+ }
+
+ public String getMnc() {
+ return mnc;
+ }
+
+ public String getCountryName() {
+ return countryName;
+ }
+
+ public String getCountryIsoCode() {
+ return countryIsoCode;
+ }
+
+ public String getOperatorName() {
+ return operatorName;
+ }
+
+ @Override
+ public String toString() {
+ return "OutboundSmsPrice{" +
+ "price=" + price +
+ ", currencyCode='" + currencyCode + '\'' +
+ ", mccmnc='" + mccmnc + '\'' +
+ ", mcc='" + mcc + '\'' +
+ ", mnc='" + mnc + '\'' +
+ ", countryName='" + countryName + '\'' +
+ ", countryIsoCode='" + countryIsoCode + '\'' +
+ ", operatorName='" + operatorName + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java b/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java
new file mode 100644
index 00000000..6d036882
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java
@@ -0,0 +1,36 @@
+package com.messagebird.objects;
+
+import java.util.List;
+
+public class OutboundSmsPriceResponse {
+ private int gateway;
+ private String currencyCode;
+ private int totalCount;
+ private List prices;
+
+ public int getGateway() {
+ return gateway;
+ }
+
+ public String getCurrencyCode() {
+ return currencyCode;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ public List getPrices() {
+ return prices;
+ }
+
+ @Override
+ public String toString() {
+ return "OutboundSmsPriceResponse{" +
+ "gateway=" + gateway +
+ ", currencyCode='" + currencyCode + '\'' +
+ ", totalCount=" + totalCount +
+ ", prices=" + prices +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PagedPaging.java b/api/src/main/java/com/messagebird/objects/PagedPaging.java
new file mode 100644
index 00000000..1f26ed4a
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PagedPaging.java
@@ -0,0 +1,23 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+public class PagedPaging implements Serializable {
+
+ private static final long serialVersionUID = 5764858762655505158L;
+ private Integer page;
+ private Integer pageSize;
+
+ public PagedPaging(Integer page, Integer pageSize) {
+ this.page = page;
+ this.pageSize = pageSize;
+ }
+
+ public Integer getPage() {
+ return page;
+ }
+
+ public Integer getPageSize() {
+ return pageSize;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumber.java b/api/src/main/java/com/messagebird/objects/PhoneNumber.java
new file mode 100644
index 00000000..5fdda19f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumber.java
@@ -0,0 +1,50 @@
+package com.messagebird.objects;
+
+import com.messagebird.objects.PhoneNumberFeature;
+
+import java.util.EnumSet;
+
+public class PhoneNumber {
+ private String number;
+ private String country;
+ private String region;
+ private String locality;
+ private EnumSet features;
+ private String type;
+
+ public String getNumber() {
+ return this.number;
+ }
+
+ public String getCountry() {
+ return this.country;
+ }
+
+ public String getRegion() {
+ return this.region;
+ }
+
+ public String getLocality() {
+ return this.locality;
+ }
+
+ public EnumSet getFeatures() {
+ return this.features;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumber{" +
+ "number='" + number + "\'" +
+ ", country='" + country + "\'" +
+ ", region='" + region + "\'" +
+ ", locality='" + locality + "\'" +
+ ", features=" + features +
+ ", type='" + type + "\'" +
+ "}";
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java b/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java
new file mode 100644
index 00000000..790177c9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java
@@ -0,0 +1,19 @@
+package com.messagebird.objects;
+
+public enum PhoneNumberFeature {
+
+ SMS("sms"),
+ MMS("mms"),
+ VOICE("voice");
+
+ private String type;
+
+ PhoneNumberFeature(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return this.type;
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java b/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java
new file mode 100644
index 00000000..cf0b3099
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java
@@ -0,0 +1,18 @@
+package com.messagebird.objects;
+
+public enum PhoneNumberSearchPattern {
+ START("start"),
+ ANYWHERE("anywhere"),
+ END("end");
+
+ private String type;
+
+ PhoneNumberSearchPattern(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return this.type;
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberType.java b/api/src/main/java/com/messagebird/objects/PhoneNumberType.java
new file mode 100644
index 00000000..82908203
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumberType.java
@@ -0,0 +1,18 @@
+package com.messagebird.objects;
+
+public enum PhoneNumberType {
+ LANDLINE("landline"),
+ MOBILE("mobile"),
+ PREMIUM_RATE("premium_rate");
+
+ private String type;
+
+ PhoneNumberType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return this.type;
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java b/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java
new file mode 100644
index 00000000..8b8396fa
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java
@@ -0,0 +1,93 @@
+package com.messagebird.objects;
+
+import com.messagebird.exceptions.GeneralException;
+import com.messagebird.objects.PhoneNumberFeature;
+import com.messagebird.objects.PhoneNumberType;
+import com.messagebird.objects.PhoneNumberSearchPattern;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.lang.reflect.Field;
+
+public class PhoneNumbersLookup {
+
+ private Number number;
+ private Number limit;
+ private EnumSet features;
+ private PhoneNumberType type;
+ private PhoneNumberSearchPattern searchPattern;
+
+ public Number getNumber() {
+ return this.number;
+ }
+
+ public EnumSet getFeatures() {
+ return this.features;
+ }
+
+ public PhoneNumberType getType() {
+ return this.type;
+ }
+
+ public Number getLimit() {
+ return this.limit;
+ }
+
+ public PhoneNumberSearchPattern getSearchPattern() {
+ return this.searchPattern;
+ }
+
+ public void setNumber(Number number) {
+ this.number = number;
+ }
+
+ public void setFeatures(EnumSet features) {
+ this.features = features;
+ }
+
+ public void setFeatures(PhoneNumberFeature... features) {
+ EnumSet featuresEnum = EnumSet.noneOf(PhoneNumberFeature.class);
+ featuresEnum.addAll(Arrays.asList(features));
+ this.features = featuresEnum;
+ }
+
+ public void setType(PhoneNumberType type) {
+ this.type = type;
+ }
+
+ public void setLimit(Number limit) {
+ this.limit = limit;
+ }
+
+ public void setSearchPattern(PhoneNumberSearchPattern searchPattern) {
+ this.searchPattern = searchPattern;
+ }
+
+ public HashMap toHashMap() throws GeneralException {
+ final HashMap map = new HashMap();
+ for (Field f: getClass().getDeclaredFields()) {
+ try {
+ Object value = f.get(this);
+ String key = f.getName();
+ if (value != null) {
+ map.put(key, value);
+ }
+ } catch (IllegalAccessException exception) {
+ throw new GeneralException("Error Converting PhoneNumbersLookup Class to HashMap.");
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumbersLookup{" +
+ " number='" + number + "'" +
+ ", features='" + features + "'" +
+ ", type='" + type + "'" +
+ ", limit='" + limit + "'" +
+ ", searchPattern='" + searchPattern + "'" +
+ "}";
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java b/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java
new file mode 100644
index 00000000..68e9ef4d
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java
@@ -0,0 +1,38 @@
+package com.messagebird.objects;
+
+import com.messagebird.objects.PhoneNumber;
+
+import java.util.List;
+
+import java.io.Serializable;
+
+public class PhoneNumbersResponse implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6177098534499444839L;
+ private Number limit;
+ private Number offset;
+ private List items;
+
+ public Number getLimit() {
+ return this.limit;
+ }
+
+ public Number getOffset() {
+ return this.offset;
+ }
+
+ public List getItems() {
+ return this.items;
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumbersResponse{" +
+ "limit=" + limit +
+ ", offset=" + offset +
+ ", items=" + items +
+ "}";
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumber.java b/api/src/main/java/com/messagebird/objects/PurchasedNumber.java
new file mode 100644
index 00000000..fb07a686
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PurchasedNumber.java
@@ -0,0 +1,30 @@
+package com.messagebird.objects;
+
+import java.util.List;
+
+public class PurchasedNumber extends PhoneNumber {
+ private List tags;
+ private String status;
+
+ public List getTags() {
+ return tags;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumber{" +
+ "number='" + this.getNumber() + "\'" +
+ ", country='" + this.getCountry() + "\'" +
+ ", region='" + this.getRegion() + "\'" +
+ ", locality='" + this.getLocality() + "\'" +
+ ", features=" + this.getFeatures() +
+ ", type='" + this.getType() + "\'" +
+ ", tags='" + tags + "\'" +
+ ", status='" + status + "\'" +
+ "}";
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java b/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java
new file mode 100644
index 00000000..8922db2e
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java
@@ -0,0 +1,32 @@
+package com.messagebird.objects;
+
+import java.util.Date;
+
+public class PurchasedNumberCreatedResponse extends PurchasedNumber {
+ private Date createdAt;
+ private Date renewalAt;
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public Date getRenewalAt() {
+ return renewalAt;
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumber{" +
+ "number='" + this.getNumber() + "\'" +
+ ", country='" + this.getCountry() + "\'" +
+ ", region='" + this.getRegion() + "\'" +
+ ", locality='" + this.getLocality() + "\'" +
+ ", features=" + this.getFeatures() +
+ ", type='" + this.getType() + "\'" +
+ ", tags='" + this.getTags() + "\'" +
+ ", status='" + this.getStatus() + "\'" +
+ ", createdAt='" + createdAt + "\'" +
+ ", renewalAt='" + renewalAt + "\'" +
+ "}";
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java b/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java
new file mode 100644
index 00000000..40c6dcd9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java
@@ -0,0 +1,135 @@
+package com.messagebird.objects;
+
+import com.messagebird.exceptions.GeneralException;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+
+public class PurchasedNumbersFilter implements Serializable {
+ private int limit = 10;
+ private int offset = 0;
+ private EnumSet features = EnumSet.noneOf(PhoneNumberFeature.class);
+ private ArrayList tags = new ArrayList<>();
+ private String number;
+ private String region;
+ private String locality;
+ private PhoneNumberType type;
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public EnumSet getFeatures() {
+ return features;
+ }
+
+ public void addFeature(PhoneNumberFeature... features) {
+ Collections.addAll(this.features, features);
+ }
+
+ public void removeFeature(PhoneNumberFeature... features) {
+ for (PhoneNumberFeature feature: features) {
+ this.features.remove(feature);
+ }
+ }
+
+ public ArrayList getTags() {
+ return tags;
+ }
+
+ public void addTag(String... tags) {
+ for (String tag: tags) {
+ if (!this.tags.contains(tag)) {
+ this.tags.add(tag);
+ }
+ }
+ }
+
+ public void removeTag(String... tags) {
+ for (String tag: tags) {
+ this.tags.remove(tag);
+ }
+ }
+
+ public void clearTags() {
+ this.tags.clear();
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public String getLocality() {
+ return locality;
+ }
+
+ public void setLocality(String locality) {
+ this.locality = locality;
+ }
+
+ public PhoneNumberType getType() {
+ return type;
+ }
+
+ public void setType(PhoneNumberType type) {
+ this.type = type;
+ }
+
+ public HashMap toHashMap() throws GeneralException {
+ final HashMap map = new HashMap();
+ for (Field f: getClass().getDeclaredFields()) {
+ try {
+ Object value = f.get(this);
+ String key = f.getName();
+ if (value != null) {
+ map.put(key, value);
+ }
+ } catch (IllegalAccessException exception) {
+ throw new GeneralException("Error converting to HashMap.");
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "PurchasedNumbersFilter{" +
+ "limit=" + limit +
+ ", offset=" + offset +
+ ", features=" + features +
+ ", tags=" + tags +
+ ", number='" + number + '\'' +
+ ", region='" + region + '\'' +
+ ", locality='" + locality + '\'' +
+ ", type=" + type +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java b/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java
new file mode 100644
index 00000000..b61928ca
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java
@@ -0,0 +1,42 @@
+package com.messagebird.objects;
+
+import java.util.List;
+
+public class PurchasedNumbersResponse {
+ private int offset;
+ private int limit;
+ private int count;
+ private int totalCount;
+ private List items;
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ public List getItems() {
+ return items;
+ }
+
+ @Override
+ public String toString() {
+ return "PurchasedNumbersResponse{" +
+ "offset=" + offset +
+ ", limit=" + limit +
+ ", count=" + count +
+ ", totalCount=" + totalCount +
+ ", items=" + items +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/VerifyMessage.java b/api/src/main/java/com/messagebird/objects/VerifyMessage.java
new file mode 100644
index 00000000..b2929f11
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/VerifyMessage.java
@@ -0,0 +1,35 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+/**
+ * Created by leandro.pinto on 22/06/15.
+ */
+public class VerifyMessage implements Serializable {
+
+ private String id;
+ private String status;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String toString() {
+ return "VerifyMessage {" + " " +
+ "id=" + this.id + " " +
+ "status=" + this.status + " " +
+ "}";
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/VerifyRequest.java b/api/src/main/java/com/messagebird/objects/VerifyRequest.java
index c0272034..bee684e9 100644
--- a/api/src/main/java/com/messagebird/objects/VerifyRequest.java
+++ b/api/src/main/java/com/messagebird/objects/VerifyRequest.java
@@ -10,13 +10,15 @@ public class VerifyRequest implements Serializable {
private String recipient;
private String originator;
private String reference;
- private MsgType type;
+ private VerifyType type;
private DataCodingType datacoding = DataCodingType.plain;
private String template;
private Integer timeout;
private Integer tokenLength;
+ private Integer maxAttempts;
private Gender voice;
private Language language;
+ private String subject;
public VerifyRequest(String recipient) {
this.recipient = recipient;
@@ -46,14 +48,18 @@ public void setReference(String reference) {
this.reference = reference;
}
- public MsgType getType() {
+ public VerifyType getType() {
return type;
}
- public void setType(MsgType type) {
+ public void setType(VerifyType type) {
this.type = type;
}
+ public void setType(String type) {
+ this.type = VerifyType.valueOf(type.toUpperCase());
+ }
+
/**
* The datacoding used by the template.
*
@@ -112,4 +118,19 @@ public void setLanguage(Language language) {
this.language = language;
}
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public Integer getMaxAttempts() {
+ return maxAttempts;
+ }
+
+ public void setMaxAttempts(Integer maxAttempts) {
+ this.maxAttempts = maxAttempts;
+ }
}
diff --git a/api/src/main/java/com/messagebird/objects/VerifyType.java b/api/src/main/java/com/messagebird/objects/VerifyType.java
new file mode 100644
index 00000000..630e8e53
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/VerifyType.java
@@ -0,0 +1,32 @@
+package com.messagebird.objects;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Determines which type the message will be.
+ */
+public enum VerifyType {
+
+ FLASH("flash"),
+ SMS("sms"),
+ TTS("tts"),
+ EMAIL("email");
+
+ final String value;
+
+ VerifyType(String type) {
+ this.value = type;
+ }
+
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "VerifyType{" +
+ "value='" + value + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/VoiceMessage.java b/api/src/main/java/com/messagebird/objects/VoiceMessage.java
index 8d941051..0674851e 100644
--- a/api/src/main/java/com/messagebird/objects/VoiceMessage.java
+++ b/api/src/main/java/com/messagebird/objects/VoiceMessage.java
@@ -22,6 +22,7 @@ public class VoiceMessage implements MessageBase, Serializable {
private VoiceType voice;
private Integer repeat;
private IfMachineType ifMachine;
+ private int machineTimeout;
private Date scheduledDatetime;
public VoiceMessage(String body, List recipients) {
@@ -171,6 +172,22 @@ public void setIfMachine(IfMachineType ifMachine) {
this.ifMachine = ifMachine;
}
+ /**
+ * The time (in milliseconds) to analyze if a machine has picked up the phone.
+ * Used in combination with the delay and hangup values of the ifMachine attribute.
+ * Minimum: 400, maximum: 10000. Default: 7000
+ * @return value of machine timeout
+ */
+ public int getMachineTimeout() { return machineTimeout; }
+
+ /**
+ * The time (in milliseconds) to analyze if a machine has picked up the phone.
+ * Used in combination with the delay and hangup values of the ifMachine attribute.
+ * Minimum: 400, maximum: 10000. Default: 7000
+ * @param machineTimeout value of machine timeout
+ */
+ public void setMachineTimeout(int machineTimeout) { this.machineTimeout = machineTimeout; }
+
@Override
public Date getScheduledDatetime() {
return scheduledDatetime;
diff --git a/api/src/main/java/com/messagebird/objects/VoiceMessageList.java b/api/src/main/java/com/messagebird/objects/VoiceMessageList.java
index 726a10b2..3344606a 100644
--- a/api/src/main/java/com/messagebird/objects/VoiceMessageList.java
+++ b/api/src/main/java/com/messagebird/objects/VoiceMessageList.java
@@ -4,5 +4,7 @@
* Class to hold a list of stored voice messages
* Created by rvt on 1/8/15.
*/
-public class VoiceMessageList extends MessageListBase {
+public class VoiceMessageList extends ListBase {
+
+ //
}
diff --git a/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java b/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java
index fe8d56f1..6563919e 100644
--- a/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java
+++ b/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java
@@ -21,6 +21,7 @@ public class VoiceMessageResponse implements MessageResponseBase, Serializable {
private VoiceType voice;
private Integer repeat;
private IfMachineType ifMachine;
+ private int machineTimeout;
private Date scheduledDatetime;
private Date createdDatetime;
private MessageResponse.Recipients recipients;
@@ -40,6 +41,7 @@ public String toString() {
", voice=" + voice +
", repeat=" + repeat +
", ifMachine=" + ifMachine +
+ ", machineTimeout=" + machineTimeout +
", scheduledDatetime=" + scheduledDatetime +
", createdDatetime=" + createdDatetime +
", recipients=" + recipients +
@@ -114,6 +116,14 @@ public IfMachineType getIfMachine() {
return ifMachine;
}
+ /**
+ * The time (in milliseconds) to analyze if a machine has picked up the phone.
+ * Used in combination with the delay and hangup values of the ifMachine attribute.
+ * Minimum: 400, maximum: 10000. Default: 7000
+ * @return value of machine timeout
+ */
+ public int getMachineTimeout() { return machineTimeout; }
+
/**
* The scheduled date and time of the message
*
diff --git a/api/src/main/java/com/messagebird/objects/VoiceStep.java b/api/src/main/java/com/messagebird/objects/VoiceStep.java
new file mode 100644
index 00000000..8958f8b8
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/VoiceStep.java
@@ -0,0 +1,84 @@
+package com.messagebird.objects;
+
+import com.messagebird.objects.voicecalls.VoiceCallCondition;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class VoiceStep implements Serializable {
+
+ private static final long serialVersionUID = 7548590380013370980L;
+
+ private String id;
+ private String action;
+ private VoiceStepOption options;
+
+ private VoiceCallCondition[] conditions;
+
+ private String onKeypressGoto;
+ private String onKeypressVar;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public VoiceStepOption getOptions() {
+ return options;
+ }
+
+ public void setOptions(VoiceStepOption options) {
+ this.options = options;
+ }
+
+ public VoiceCallCondition[] getConditions() {
+ return conditions;
+ }
+
+ public void setConditions(VoiceCallCondition[] conditions) {
+ this.conditions = conditions;
+ }
+
+ public static long getSerialVersionUID() {
+ return serialVersionUID;
+ }
+
+ public String getOnKeypressGoto() {
+ return onKeypressGoto;
+ }
+
+ public void setOnKeypressGoto(String onKeypressGoto) {
+ this.onKeypressGoto = onKeypressGoto;
+ }
+
+ public String getOnKeypressVar() {
+ return onKeypressVar;
+ }
+
+ public void setOnKeypressVar(String onKeypressVar) {
+ this.onKeypressVar = onKeypressVar;
+ }
+
+ @Override
+ public String toString() {
+ return "VoiceStep{" +
+ "id='" + id + '\'' +
+ ", action='" + action + '\'' +
+ ", options=" + options +
+ ", conditions=" + Arrays.toString(conditions) +
+ ", onKeypressGoto='" + onKeypressGoto + '\'' +
+ ", onKeypressVar='" + onKeypressVar + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/VoiceStepOption.java b/api/src/main/java/com/messagebird/objects/VoiceStepOption.java
new file mode 100644
index 00000000..70c0670b
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/VoiceStepOption.java
@@ -0,0 +1,213 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+public class VoiceStepOption implements Serializable {
+
+ private static final long serialVersionUID = -3049137903264066344L;
+
+ private String destination;
+ private String payload;
+ private String language;
+ private String voice;
+ private int repeat;
+ private String media;
+ private int length;
+ private int maxLength;
+ private int timeout;
+ private String finishOnKey;
+ private boolean transcribe;
+ private String transcribeLanguage;
+ private String record;
+ private String url;
+ private String ifMachine;
+ private int machineTimeout;
+ private String onFinish;
+ private boolean mask;
+ private String keys;
+ private int duration;
+ private int interval;
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getPayload() {
+ return payload;
+ }
+
+ public void setPayload(String payload) {
+ this.payload = payload;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getVoice() {
+ return voice;
+ }
+
+ public void setVoice(String voice) {
+ this.voice = voice;
+ }
+
+ public int getRepeat() {
+ return repeat;
+ }
+
+ public void setRepeat(int repeat) {
+ this.repeat = repeat;
+ }
+
+ public String getMedia() {
+ return media;
+ }
+
+ public void setMedia(String media) {
+ this.media = media;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public int getMaxLength() {
+ return maxLength;
+ }
+
+ public void setMaxLength(int maxLength) {
+ this.maxLength = maxLength;
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ public String getFinishOnKey() {
+ return finishOnKey;
+ }
+
+ public void setFinishOnKey(String finishOnKey) {
+ this.finishOnKey = finishOnKey;
+ }
+
+ public boolean isTranscribe() {
+ return transcribe;
+ }
+
+ public void setTranscribe(boolean transcribe) {
+ this.transcribe = transcribe;
+ }
+
+ public String getTranscribeLanguage() {
+ return transcribeLanguage;
+ }
+
+ public void setTranscribeLanguage(String transcribeLanguage) {
+ this.transcribeLanguage = transcribeLanguage;
+ }
+
+ public String getRecord() {
+ return record;
+ }
+
+ public void setRecord(String record) {
+ this.record = record;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getIfMachine() {
+ return ifMachine;
+ }
+
+ public void setIfMachine(String ifMachine) {
+ this.ifMachine = ifMachine;
+ }
+
+ public int getMachineTimeout() {
+ return machineTimeout;
+ }
+
+ public void setMachineTimeout(int machineTimeout) {
+ this.machineTimeout = machineTimeout;
+ }
+
+ public String getOnFinish() {
+ return onFinish;
+ }
+
+ public void setOnFinish(String onFinish) {
+ this.onFinish = onFinish;
+ }
+
+ public boolean isMask() {
+ return mask;
+ }
+
+ public void setMask(boolean mask) {
+ this.mask = mask;
+ }
+
+ public String getKeys() { return keys; }
+
+ public void setKeys(String keys) { this.keys = keys; }
+
+ public int getDuration() { return duration; }
+
+ public void setDuration(int duration) { this.duration = duration; }
+
+ public int getInterval() { return interval; }
+
+ public void setInterval(int interval) { this.interval = interval; }
+
+ @Override
+ public String toString() {
+ return "VoiceStepOption{" +
+ "destination='" + destination + '\'' +
+ ", payload='" + payload + '\'' +
+ ", language='" + language + '\'' +
+ ", voice='" + voice + '\'' +
+ ", repeat='" + repeat + '\'' +
+ ", media='" + media + '\'' +
+ ", length=" + length +
+ ", maxLength=" + maxLength +
+ ", timeout=" + timeout +
+ ", finishOnKey='" + finishOnKey + '\'' +
+ ", transcribe=" + transcribe +
+ ", transcribeLanguage='" + transcribeLanguage + '\'' +
+ ", record='" + record + '\'' +
+ ", url='" + url + '\'' +
+ ", ifMachine='" + ifMachine + '\'' +
+ ", machineTimeout=" + machineTimeout +
+ ", onFinish='" + onFinish + '\'' +
+ ", mask=" + mask + '\'' +
+ ", keys='" + keys + '\'' +
+ ", interval='" + interval + '\'' +
+ ", duration='" + duration +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/Conversation.java b/api/src/main/java/com/messagebird/objects/conversations/Conversation.java
new file mode 100644
index 00000000..42aac60d
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/Conversation.java
@@ -0,0 +1,119 @@
+package com.messagebird.objects.conversations;
+
+import com.messagebird.objects.MessageReference;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Response object for the Conversation type.
+ */
+public class Conversation {
+
+ private String id;
+ private String contactId;
+ private ConversationContact contact;
+ private List channels;
+ private MessageReference messages;
+ private ConversationStatus status;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+ private Date lastReceivedDatetime;
+ private String lastUsedChannelId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String contactId) {
+ this.contactId = contactId;
+ }
+
+ public ConversationContact getContact() {
+ return contact;
+ }
+
+ public void setContact(ConversationContact contact) {
+ this.contact = contact;
+ }
+
+ public List getChannels() {
+ return channels;
+ }
+
+ public void setChannels(List channels) {
+ this.channels = channels;
+ }
+
+ public MessageReference getMessages() {
+ return messages;
+ }
+
+ public void setMessages(MessageReference messages) {
+ this.messages = messages;
+ }
+
+ public ConversationStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ConversationStatus status) {
+ this.status = status;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ public Date getLastReceivedDatetime() {
+ return lastReceivedDatetime;
+ }
+
+ public void setLastReceivedDatetime(Date lastReceivedDatetime) {
+ this.lastReceivedDatetime = lastReceivedDatetime;
+ }
+
+ public String getLastUsedChannelId() {
+ return lastUsedChannelId;
+ }
+
+ public void setLastUsedChannelId(String lastUsedChannelId) {
+ this.lastUsedChannelId = lastUsedChannelId;
+ }
+
+ @Override
+ public String toString() {
+ return "Conversation{" +
+ "id='" + id + '\'' +
+ ", contactId='" + contactId + '\'' +
+ ", contact=" + contact +
+ ", channels=" + channels +
+ ", messages=" + messages +
+ ", status=" + status +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ ", lastReceivedDatetime=" + lastReceivedDatetime +
+ ", lastUsedChannelId='" + lastUsedChannelId + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java
new file mode 100644
index 00000000..2c2bd06e
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java
@@ -0,0 +1,79 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Date;
+
+/**
+ * Response object for the Channel type. It represents a platform that messages
+ * can be sent and received on. A conversation can take place over multiple
+ * channels simultaneously.
+ */
+public class ConversationChannel {
+
+ private String id;
+ private String name;
+ // See: ConversationPlatformConstants
+ private String platformId;
+ private ConversationChannelStatus status;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public ConversationChannelStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ConversationChannelStatus status) {
+ this.status = status;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(final Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(final Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ public String getPlatformId() {
+ return platformId;
+ }
+
+ public void setPlatformId(String platformId) {
+ this.platformId = platformId;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationChannel{" +
+ "id='" + id + '\'' +
+ ", name='" + name + '\'' +
+ ", platformId=" + platformId +
+ ", status=" + status +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationChannelStatus.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannelStatus.java
new file mode 100644
index 00000000..425b785d
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannelStatus.java
@@ -0,0 +1,50 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Status for a Conversation channel. Only ACTIVE channels can be used for
+ * messaging.
+ */
+public enum ConversationChannelStatus {
+
+ ACTIVATING("activating"),
+ ACTIVATION_CODE_REQUIRED("activation_code_required"),
+ ACTIVATION_REQUIRED("activation_required"),
+ ACTIVE("active"),
+ DELETED("deleted"),
+ INACTIVE("inactive"),
+ PENDING("pending");
+
+ private final String status;
+
+ ConversationChannelStatus(final String status) {
+ this.status = status;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ @JsonCreator
+ public static ConversationChannelStatus forValue(final String value) {
+ for (ConversationChannelStatus status : ConversationChannelStatus.values()) {
+ if (status.getStatus().equals(value)) {
+ return status;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getStatus();
+ }
+
+ @Override
+ public String toString() {
+ return getStatus();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContact.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContact.java
new file mode 100644
index 00000000..62a8d5fd
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContact.java
@@ -0,0 +1,98 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Response object for the ConversationContact type. Represents an end-user you
+ * are communicating with.
+ */
+public class ConversationContact {
+
+ private String id;
+ private String href;
+ private String msisdn;
+ private String firstName;
+ private String lastName;
+ private Map customDetails;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(final String href) {
+ this.href = href;
+ }
+
+ public String getMsisdn() {
+ return msisdn;
+ }
+
+ public void setMsisdn(final String msisdn) {
+ this.msisdn = msisdn;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(final String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(final String lastName) {
+ this.lastName = lastName;
+ }
+
+ public Map getCustomDetails() {
+ return customDetails;
+ }
+
+ public void setCustomDetails(final Map customDetails) {
+ this.customDetails = customDetails;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(final Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(final Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContact{" +
+ "id='" + id + '\'' +
+ ", href='" + href + '\'' +
+ ", msisdn='" + msisdn + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", customDetails=" + customDetails +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java
new file mode 100644
index 00000000..8b4fe164
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java
@@ -0,0 +1,102 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * ConversationContent wraps actual content. The field that should be set here
+ * is indicated by ConversationContentType.
+ *
+ * Unknown keys are ignored on deserialization so that consumers parsing
+ * webhook payloads in their own handlers are not broken by content fields added
+ * after their SDK version. Serialization is unaffected.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ConversationContent {
+
+ private ConversationContentMedia audio;
+ private ConversationContentMedia file;
+ private ConversationContentHsm hsm;
+ private ConversationContentMedia image;
+ private ConversationContentLocation location;
+ private ConversationContentEmail email;
+ private String text;
+ private ConversationContentMedia video;
+
+ public ConversationContentMedia getAudio() {
+ return audio;
+ }
+
+ public void setAudio(ConversationContentMedia audio) {
+ this.audio = audio;
+ }
+
+ public ConversationContentMedia getFile() {
+ return file;
+ }
+
+ public void setFile(ConversationContentMedia file) {
+ this.file = file;
+ }
+
+ public ConversationContentHsm getHsm() {
+ return hsm;
+ }
+
+ public void setHsm(ConversationContentHsm hsm) {
+ this.hsm = hsm;
+ }
+
+ public ConversationContentMedia getImage() {
+ return image;
+ }
+
+ public void setImage(ConversationContentMedia image) {
+ this.image = image;
+ }
+
+ public ConversationContentLocation getLocation() {
+ return location;
+ }
+
+ public void setLocation(ConversationContentLocation location) {
+ this.location = location;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public ConversationContentMedia getVideo() {
+ return video;
+ }
+
+ public void setVideo(ConversationContentMedia video) {
+ this.video = video;
+ }
+
+ public ConversationContentEmail getEmail() {
+ return email;
+ }
+
+ public void setEmail(ConversationContentEmail email) {
+ this.email = email;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContent{" +
+ "audio=" + audio +
+ ", file=" + file +
+ ", hsm=" + hsm +
+ ", image=" + image +
+ ", location=" + location +
+ ", email=" + email +
+ ", text='" + text + '\'' +
+ ", video=" + video +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java
new file mode 100644
index 00000000..3ba8b2d8
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java
@@ -0,0 +1,133 @@
+package com.messagebird.objects.conversations;
+
+import java.util.List;
+import java.util.Map;
+
+public class ConversationContentEmail {
+ private String id;
+ private ConversationEmailRecipient from;
+ private List to;
+ private String subject;
+ private ConversationEmailContent content;
+ private String replyTo;
+ private String returnPath;
+ private Map headers;
+ private ConversationEmailTracking tracking;
+ private boolean performSubstitutions;
+ private List attachments;
+ private List inlineImages;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public ConversationEmailRecipient getFrom() {
+ return from;
+ }
+
+ public void setFrom(ConversationEmailRecipient from) {
+ this.from = from;
+ }
+
+ public List getTo() {
+ return to;
+ }
+
+ public void setTo(List to) {
+ this.to = to;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public ConversationEmailContent getContent() {
+ return content;
+ }
+
+ public void setContent(ConversationEmailContent content) {
+ this.content = content;
+ }
+
+ public String getReplyTo() {
+ return replyTo;
+ }
+
+ public void setReplyTo(String replyTo) {
+ this.replyTo = replyTo;
+ }
+
+ public String getReturnPath() {
+ return returnPath;
+ }
+
+ public void setReturnPath(String returnPath) {
+ this.returnPath = returnPath;
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(Map headers) {
+ this.headers = headers;
+ }
+
+ public ConversationEmailTracking getTracking() {
+ return tracking;
+ }
+
+ public void setTracking(ConversationEmailTracking tracking) {
+ this.tracking = tracking;
+ }
+
+ public boolean isPerformSubstitutions() {
+ return performSubstitutions;
+ }
+
+ public void setPerformSubstitutions(boolean performSubstitutions) {
+ this.performSubstitutions = performSubstitutions;
+ }
+
+ public List getAttachments() {
+ return attachments;
+ }
+
+ public void setAttachments(List attachments) {
+ this.attachments = attachments;
+ }
+
+ public List getInlineImages() {
+ return inlineImages;
+ }
+
+ public void setInlineImages(List inlineImages) {
+ this.inlineImages = inlineImages;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContentEmail{" +
+ "id='" + id + '\'' +
+ ", from=" + from +
+ ", to=" + to +
+ ", subject='" + subject + '\'' +
+ ", content=" + content +
+ ", replyTo='" + replyTo + '\'' +
+ ", returnPath='" + returnPath + '\'' +
+ ", headers=" + headers +
+ ", tracking=" + tracking +
+ ", performSubstitutions=" + performSubstitutions +
+ ", attachments=" + attachments +
+ ", inlineImages=" + inlineImages +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java
new file mode 100644
index 00000000..824f7fc8
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java
@@ -0,0 +1,109 @@
+package com.messagebird.objects.conversations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Highly structured message, only for WhatsApp channels. This is a
+ * pre-approved, reusable message template required when messaging over
+ * WhatsApp. It allows you to just send the required parameter values instead
+ * of the full message. It also allows for localization of the message and
+ * decreases the possibility of being blocked on the first contact as the
+ * message is pre-approved by WhatsApp.
+ */
+public class ConversationContentHsm {
+
+ private String namespace;
+ private String templateName;
+ private ConversationHsmLanguage language;
+ private List params;
+ private List components;
+
+ public ConversationContentHsm(
+ final String namespace,
+ final String templateName,
+ final ConversationHsmLanguage language,
+ final List params,
+ final List components
+ ) {
+ this.namespace = namespace;
+ this.templateName = templateName;
+ this.language = language;
+ this.params = params;
+ this.components = components;
+ }
+
+ public ConversationContentHsm(
+ final String namespace,
+ final String templateName,
+ final ConversationHsmLanguage language
+ ) {
+ this.namespace = namespace;
+ this.templateName = templateName;
+ this.language = language;
+ }
+
+ public ConversationContentHsm() {
+ //
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(final String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getTemplateName() {
+ return templateName;
+ }
+
+ public void setTemplateName(final String templateName) {
+ this.templateName = templateName;
+ }
+
+ public ConversationHsmLanguage getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(final ConversationHsmLanguage language) {
+ this.language = language;
+ }
+
+ public List getParams() {
+ return params;
+ }
+
+ public void addParams(final ConversationHsmLocalizableParameter... params) {
+ if (this.params == null) {
+ this.params = new ArrayList<>(params.length);
+ }
+
+ Collections.addAll(this.params, params);
+ }
+
+ public void setParams(final List params) {
+ this.params = params;
+ }
+
+ public List getComponents() {
+ return components;
+ }
+
+ public void setComponents(List components) {
+ this.components = components;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContentHsm{" +
+ "namespace='" + namespace + '\'' +
+ ", templateName='" + templateName + '\'' +
+ ", language=" + language +
+ ", params=" + params +
+ ", components=" + components +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentLocation.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentLocation.java
new file mode 100644
index 00000000..9511fd13
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentLocation.java
@@ -0,0 +1,42 @@
+package com.messagebird.objects.conversations;
+
+/**
+ * Coordinates that point to a location.
+ */
+public class ConversationContentLocation {
+
+ private double latitude, longitude;
+
+ public ConversationContentLocation(double latitude, double longitude) {
+ this.latitude = latitude;
+ this.longitude = longitude;
+ }
+
+ public ConversationContentLocation() {
+ //
+ }
+
+ public double getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(double latitude) {
+ this.latitude = latitude;
+ }
+
+ public double getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(double longitude) {
+ this.longitude = longitude;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContentLocation{" +
+ "latitude=" + latitude +
+ ", longitude=" + longitude +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java
new file mode 100644
index 00000000..7ac29e8f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java
@@ -0,0 +1,48 @@
+package com.messagebird.objects.conversations;
+
+/**
+ * Contains media referenced by a URL. Supported types are audio, file, image
+ * and video.
+ */
+public class ConversationContentMedia {
+
+ private String url;
+ private String caption;
+
+ public ConversationContentMedia(final String url, final String caption) {
+ this.url = url;
+ this.caption = caption;
+ }
+
+ public ConversationContentMedia(final String url) {
+ this.url = url;
+ }
+
+ public ConversationContentMedia() {
+ //
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(final String url) {
+ this.url = url;
+ }
+
+ public String getCaption() {
+ return caption;
+ }
+
+ public void setCaption(String caption) {
+ this.caption = caption;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationContentMedia{" +
+ "url='" + url + '\'' +
+ ", caption='" + caption + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java
new file mode 100644
index 00000000..3dec398a
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java
@@ -0,0 +1,51 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * The type of content that the message holds. Indicates what field is set on a
+ * ConversationContent object.
+ */
+public enum ConversationContentType {
+
+ AUDIO("audio"),
+ FILE("file"),
+ HSM("hsm"),
+ IMAGE("image"),
+ LOCATION("location"),
+ TEXT("text"),
+ VIDEO("video"),
+ EMAIL("email");
+
+ private final String type;
+
+ ConversationContentType(final String type) {
+ this.type = type;
+ }
+
+ @JsonCreator
+ public static ConversationContentType forValue(String value) {
+ for (ConversationContentType conversationContentType : ConversationContentType.values()) {
+ if (conversationContentType.getType().equals(value)) {
+ return conversationContentType;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getType();
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return getType();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java
new file mode 100644
index 00000000..82e1a48a
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java
@@ -0,0 +1,60 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationEmailAttachment {
+ private String id;
+ private String name;
+ private String type;
+ private String URL;
+ private String length;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getURL() {
+ return URL;
+ }
+
+ public void setURL(String URL) {
+ this.URL = URL;
+ }
+
+ public String getLength() {
+ return length;
+ }
+
+ public void setLength(String length) {
+ this.length = length;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationEmailAttachment{" +
+ "id='" + id + '\'' +
+ ", name='" + name + '\'' +
+ ", type='" + type + '\'' +
+ ", URL='" + URL + '\'' +
+ ", length='" + length + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java
new file mode 100644
index 00000000..7127b104
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java
@@ -0,0 +1,30 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationEmailContent {
+ private String html;
+ private String text;
+
+ public String getHtml() {
+ return html;
+ }
+
+ public void setHtml(String html) {
+ this.html = html;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationEmailContent{" +
+ "html='" + html + '\'' +
+ ", text='" + text + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java
new file mode 100644
index 00000000..a7318216
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java
@@ -0,0 +1,70 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationEmailInlineImage {
+ private String id;
+ private String name;
+ private String type;
+ private String URL;
+ private int length;
+ private String contentId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getURL() {
+ return URL;
+ }
+
+ public void setURL(String URL) {
+ this.URL = URL;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public String getContentId() {
+ return contentId;
+ }
+
+ public void setContentId(String contentId) {
+ this.contentId = contentId;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationEmailInlineImage{" +
+ "id='" + id + '\'' +
+ ", name='" + name + '\'' +
+ ", type='" + type + '\'' +
+ ", URL='" + URL + '\'' +
+ ", length=" + length +
+ ", contentId='" + contentId + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java
new file mode 100644
index 00000000..f5bbdfb9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java
@@ -0,0 +1,42 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Map;
+
+public class ConversationEmailRecipient {
+ private String address;
+ private String name;
+ private Map variables;
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getVariables() {
+ return variables;
+ }
+
+ public void setVariables(Map variables) {
+ this.variables = variables;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationEmailRecipient{" +
+ "address='" + address + '\'' +
+ ", name='" + name + '\'' +
+ ", variables=" + variables +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java
new file mode 100644
index 00000000..b8861d14
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java
@@ -0,0 +1,30 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationEmailTracking {
+ private boolean open;
+ private boolean click;
+
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void setOpen(boolean open) {
+ this.open = open;
+ }
+
+ public boolean isClick() {
+ return click;
+ }
+
+ public void setClick(boolean click) {
+ this.click = click;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationEmailTracking{" +
+ "open=" + open +
+ ", click=" + click +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java
new file mode 100644
index 00000000..0048de12
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java
@@ -0,0 +1,38 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationFallbackOption {
+ private String from;
+ private String after;
+
+ public ConversationFallbackOption() {
+ }
+
+ public ConversationFallbackOption(String from, String after) {
+ this.from = from;
+ this.after = after;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getAfter() {
+ return after;
+ }
+
+ public void setAfter(String after) {
+ this.after = after;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationFallbackOption{" +
+ "from='" + from + '\'' +
+ ", after='" + after + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguage.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguage.java
new file mode 100644
index 00000000..8a5ae957
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguage.java
@@ -0,0 +1,48 @@
+package com.messagebird.objects.conversations;
+
+/**
+ * Language options used for localization. Code can be in language or
+ * language_locale formats (e.g. "en" and "en_US"). The policy indicates
+ * whether the message is delivered exactly in the locale requested
+ * (deterministic). If the fallback policy is used, WhatsApp will attempt to
+ * deliver the HSM in the user's device language. If that is not found, the
+ * fallback is used.
+ */
+public class ConversationHsmLanguage {
+
+ private String code;
+ private ConversationHsmLanguagePolicy policy;
+
+ public ConversationHsmLanguage(final String code, final ConversationHsmLanguagePolicy policy) {
+ this.code = code;
+ this.policy = policy;
+ }
+
+ public ConversationHsmLanguage() {
+ //
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(final String code) {
+ this.code = code;
+ }
+
+ public ConversationHsmLanguagePolicy getPolicy() {
+ return policy;
+ }
+
+ public void setPolicy(final ConversationHsmLanguagePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationHsmLanguage{" +
+ "code='" + code + '\'' +
+ ", policy=" + policy +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguagePolicy.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguagePolicy.java
new file mode 100644
index 00000000..1cd2c304
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLanguagePolicy.java
@@ -0,0 +1,52 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Determines the lanuage policy for a HSM.
+ */
+public enum ConversationHsmLanguagePolicy {
+
+ /**
+ * Messages are delivered in exactly the language/locale requested.
+ */
+ DETERMINISTIC("deterministic"),
+
+ /**
+ * Messages are delivered in the user's device language. If that is not
+ * found, the requested locale is used.
+ */
+ FALLBACK("fallback");
+
+ private final String policy;
+
+ ConversationHsmLanguagePolicy(final String policy) {
+ this.policy = policy;
+ }
+
+ @JsonCreator
+ public static ConversationHsmLanguagePolicy forValue(String value) {
+ if ("deterministic".equals(value)) {
+ return ConversationHsmLanguagePolicy.DETERMINISTIC;
+ } else if ("fallback".equals(value)) {
+ return ConversationHsmLanguagePolicy.FALLBACK;
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return policy;
+ }
+
+ public String getPolicy() {
+ return policy;
+ }
+
+ @Override
+ public String toString() {
+ return getPolicy();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameter.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameter.java
new file mode 100644
index 00000000..cbf2e324
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameter.java
@@ -0,0 +1,83 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Date;
+
+/**
+ * Some of the template parameters related to date/time and currency are
+ * localizable and can be displayed based on customer's device language and
+ * local preferences. Default values are used when localization fails.
+ */
+public class ConversationHsmLocalizableParameter {
+
+ @JsonProperty("default")
+ private String defaultValue;
+
+ private ConversationHsmLocalizableParameterCurrency currency;
+ private Date dateTime;
+
+ private ConversationHsmLocalizableParameter() {
+ // Jackson requires an empty constructor for instantiation.
+ }
+
+ /**
+ * Gets a parameter that does a simple replacement without localization.
+ *
+ * @param defaultValue String to replace parameter with.
+ */
+ public static ConversationHsmLocalizableParameter defaultValue(final String defaultValue) {
+ ConversationHsmLocalizableParameter parameter = new ConversationHsmLocalizableParameter();
+ parameter.defaultValue = defaultValue;
+
+ return parameter;
+ }
+
+ /**
+ * Gets a parameter that localizes a currency.
+ *
+ * @param defaultValue Default for when localization fails.
+ * @param code ISO 4217 compliant currency code.
+ * @param amount Amount multiplied by 1000. E.g. 12.34 becomes 12340.
+ */
+ public static ConversationHsmLocalizableParameter currency(final String defaultValue, final String code, final int amount) {
+ ConversationHsmLocalizableParameter parameter = new ConversationHsmLocalizableParameter();
+ parameter.defaultValue = defaultValue;
+ parameter.currency = new ConversationHsmLocalizableParameterCurrency(code, amount);
+
+ return parameter;
+ }
+
+ /**
+ * Gets a parameter that localizes a date/time.
+ *
+ * @param defaultValue Default for when localization fails.
+ * @param dateTime Localizable date/time.
+ */
+ public static ConversationHsmLocalizableParameter dateTime(final String defaultValue, final Date dateTime) {
+ ConversationHsmLocalizableParameter parameter = new ConversationHsmLocalizableParameter();
+ parameter.defaultValue = defaultValue;
+ parameter.dateTime = dateTime;
+
+ return parameter;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public ConversationHsmLocalizableParameterCurrency getCurrency() {
+ return currency;
+ }
+
+ public Date getDateTime() {
+ return dateTime;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationHsmLocalizableParameter{" +
+ "defaultValue='" + defaultValue + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java
new file mode 100644
index 00000000..708938ee
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java
@@ -0,0 +1,49 @@
+package com.messagebird.objects.conversations;
+
+/**
+ * Parameter for HSMs that localizes currencies.
+ */
+public class ConversationHsmLocalizableParameterCurrency {
+
+ private String currencyCode;
+ private int amount;
+
+ /**
+ * Instantiates a localizable parameter for currencies.
+ *
+ * @param currencyCode ISO 4217 compliant currency code.
+ * @param amount Amount multiplied by 1000. E.g. 12.34 becomes 12340.
+ */
+ public ConversationHsmLocalizableParameterCurrency(final String currencyCode, final int amount) {
+ this.currencyCode = currencyCode;
+ this.amount = amount;
+ }
+
+ public ConversationHsmLocalizableParameterCurrency() {
+ //
+ }
+
+ public String getCurrencyCode() {
+ return currencyCode;
+ }
+
+ public void setCurrencyCode(final String currencyCode) {
+ this.currencyCode = currencyCode;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ public void setAmount(final int amount) {
+ this.amount = amount;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationHsmLocalizableParameterCurrency{" +
+ "amount=" + amount +
+ ", currencyCode='" + currencyCode + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationList.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationList.java
new file mode 100644
index 00000000..95b57eff
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationList.java
@@ -0,0 +1,11 @@
+package com.messagebird.objects.conversations;
+
+import com.messagebird.objects.ListBase;
+
+/**
+ * Response object representing the Conversation list type.
+ */
+public class ConversationList extends ListBase {
+
+ //
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java
new file mode 100644
index 00000000..1dea8280
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java
@@ -0,0 +1,162 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Response object that represents a conversation's message. Messages can be
+ * sent to and received from a customer and are automatically threaded in a
+ * conversation.
+ */
+public class ConversationMessage {
+
+ private String id;
+ private String conversationId;
+ private String channelId;
+ private String trackId;
+ private ConversationMessageDirection direction;
+ private ConversationMessageStatus status;
+ private ConversationContentType type;
+ private ConversationContent content;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+ private Map source;
+ private ConversationMessageTag tag;
+ private ConversationMessageMetadata metadata;
+ /**
+ * See: {@link ConversationPlatformConstants}
+ */
+ private String platform;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getConversationId() {
+ return conversationId;
+ }
+
+ public void setConversationId(String conversationId) {
+ this.conversationId = conversationId;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public ConversationMessageDirection getDirection() {
+ return direction;
+ }
+
+ public void setDirection(ConversationMessageDirection direction) {
+ this.direction = direction;
+ }
+
+ public ConversationMessageStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ConversationMessageStatus status) {
+ this.status = status;
+ }
+
+ public ConversationContentType getType() {
+ return type;
+ }
+
+ public void setType(ConversationContentType type) {
+ this.type = type;
+ }
+
+ public ConversationContent getContent() {
+ return content;
+ }
+
+ public void setContent(ConversationContent content) {
+ this.content = content;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ public Map getSource() {
+ return source;
+ }
+
+ public void setSource(Map source) {
+ this.source = source;
+ }
+
+ public ConversationMessageTag getTag() {
+ return tag;
+ }
+
+ public void setTag(ConversationMessageTag tag) {
+ this.tag = tag;
+ }
+
+ public ConversationMessageMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(ConversationMessageMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ public String getPlatform() {
+ return platform;
+ }
+
+ public void setPlatform(String platform) {
+ this.platform = platform;
+ }
+
+ public String getTrackId() {
+ return trackId;
+ }
+
+ public void setTrackId(String trackId) {
+ this.trackId = trackId;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationMessage{" +
+ "id='" + id + '\'' +
+ ", conversationId='" + conversationId + '\'' +
+ ", channelId='" + channelId + '\'' +
+ ", direction=" + direction +
+ ", status=" + status +
+ ", type=" + type +
+ ", trackID=" + trackId +
+ ", content=" + content +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ ", source=" + source +
+ ", tag=" + tag +
+ ", metadata=" + metadata +
+ ", platform='" + platform + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageDirection.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageDirection.java
new file mode 100644
index 00000000..e0337970
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageDirection.java
@@ -0,0 +1,51 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Indicates the direction of a message.
+ */
+public enum ConversationMessageDirection {
+
+ /**
+ * Received is an in bound message received from a customer.
+ */
+ RECEIVED("received"),
+
+ /**
+ * Sent is an outbound message sent through the API.
+ */
+ SENT("sent");
+
+ private final String direction;
+
+ ConversationMessageDirection(final String direction) {
+ this.direction = direction;
+ }
+
+ public String getDirection() {
+ return direction;
+ }
+
+ @JsonCreator
+ public static ConversationMessageDirection forValue(String value) {
+ if ("received".equals(value)) {
+ return ConversationMessageDirection.RECEIVED;
+ } else if ("sent".equals(value)) {
+ return ConversationMessageDirection.SENT;
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getDirection();
+ }
+
+ @Override
+ public String toString() {
+ return getDirection();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageList.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageList.java
new file mode 100644
index 00000000..63f94fba
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageList.java
@@ -0,0 +1,11 @@
+package com.messagebird.objects.conversations;
+
+import com.messagebird.objects.ListBase;
+
+/**
+ * Response object for the message list type.
+ */
+public class ConversationMessageList extends ListBase {
+
+ //
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java
new file mode 100644
index 00000000..c41e814b
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java
@@ -0,0 +1,42 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.util.Date;
+
+/**
+ * Inner metadata attached to a conversation message. Present on both incoming
+ * messages and status webhook payloads. {@code sender.userId} always contains
+ * the BSUID when Meta provides one. When both identifiers exist, the phone
+ * number appears in the parent {@code from} field, not in this object.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ConversationMessageMetadata {
+
+ private ConversationSenderMetadata sender;
+ private Date receivedAt;
+
+ public ConversationSenderMetadata getSender() {
+ return sender;
+ }
+
+ public void setSender(ConversationSenderMetadata sender) {
+ this.sender = sender;
+ }
+
+ public Date getReceivedAt() {
+ return receivedAt;
+ }
+
+ public void setReceivedAt(Date receivedAt) {
+ this.receivedAt = receivedAt;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationMessageMetadata{" +
+ "sender=" + sender +
+ ", receivedAt=" + receivedAt +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java
new file mode 100644
index 00000000..420213e3
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java
@@ -0,0 +1,85 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Map;
+
+/**
+ * Request object that is used to send new messages over a channel.
+ */
+public class ConversationMessageRequest {
+
+ private ConversationContentType type;
+ private ConversationContent content;
+ private String channelId;
+ private String reportUrl;
+ private String trackId;
+ private String ttl;
+ private Map source;
+
+ public ConversationContentType getType() {
+ return type;
+ }
+
+ public void setType(ConversationContentType type) {
+ this.type = type;
+ }
+
+ public ConversationContent getContent() {
+ return content;
+ }
+
+ public void setContent(ConversationContent content) {
+ this.content = content;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getReportUrl() {
+ return reportUrl;
+ }
+
+ public void setReportUrl(String reportUrl) {
+ this.reportUrl = reportUrl;
+ }
+
+ public Map getSource() {
+ return source;
+ }
+
+ public void setSource(Map source) {
+ this.source = source;
+ }
+
+ public String getTrackId() {
+ return trackId;
+ }
+
+ public void setTrackId(String trackId) {
+ this.trackId = trackId;
+ }
+ public String getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(String ttl) {
+ this.ttl = ttl;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationMessageRequest{" +
+ "type=" + type +
+ ", content=" + content +
+ ", channelId='" + channelId + '\'' +
+ ", reportUrl='" + reportUrl + '\'' +
+ ", source=" + source +
+ ", trackID=" + trackId +
+ ", ttl=" + ttl +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java
new file mode 100644
index 00000000..aee5c5f5
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java
@@ -0,0 +1,68 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Response object for a message's status, e.g. delivered or failed.
+ */
+public enum ConversationMessageStatus {
+
+ DELETED("deleted"),
+ DELIVERED("delivered"),
+ FAILED("failed"),
+ PENDING("pending"),
+ READ("read"),
+ RECEIVED("received"),
+ SENT("sent"),
+ UNSUPPORTED("unsupported"),
+ ACCEPTED("accepted"),
+ REJECTED("rejected"),
+ UNKNOWN("unknown"),
+ //WA specific statuses
+ TRANSMITTED("transmitted"),
+ //SMS specific statuses
+ DELIVERY_FAILED("delivery_failed"),
+ BUFFERED("buffered"),
+ EXPIRED("expired"),
+ //Email specific statuses
+ CLICKED("clicked"),
+ OPENED("opened"),
+ BOUNCE("bounce"),
+ SPAM_COMPLAINT("spam_complaint"),
+ OUT_OF_BOUNDED("out_of_bounded"),
+ DELAYED("delayed"),
+ LIST_UNSUBSCRIBE("list_unsubscribe"),
+ DISPATCHED("dispatched");
+
+ private final String status;
+
+ ConversationMessageStatus(final String status) {
+ this.status = status;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ @JsonCreator
+ public static ConversationMessageStatus forValue(final String value) {
+ for (ConversationMessageStatus status : ConversationMessageStatus.values()) {
+ if (status.getStatus().equals(value)) {
+ return status;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getStatus();
+ }
+
+ @Override
+ public String toString() {
+ return getStatus();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java
new file mode 100644
index 00000000..49314b3c
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java
@@ -0,0 +1,41 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * These allow tagging a message based on Facebook tags.
+ * For more information visit: https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags/
+ */
+public enum ConversationMessageTag {
+ EventUpdate("event.update"),
+ PurchaseUpdate("purchase.update"),
+ AccountUpdate("account.update"),
+ HumanAgent("human_agent");
+
+ @JsonValue
+ private final String tag;
+
+ ConversationMessageTag(String tag) {
+ this.tag = tag;
+ }
+
+ @JsonCreator
+ public static ConversationMessageTag forValue(final String value) {
+ for (ConversationMessageTag tag : ConversationMessageTag.values()) {
+ if (tag.getTag().equals(value)) {
+ return tag;
+ }
+ }
+ return null;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ @Override
+ public String toString() {
+ return tag;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java
new file mode 100644
index 00000000..b9c02f49
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java
@@ -0,0 +1,33 @@
+package com.messagebird.objects.conversations;
+
+/**
+ * Platforms are communication channels that a conversation can communicate through.
+ */
+public class ConversationPlatformConstants {
+ // PlatformSMS identifies the MessageBird SMS platform.
+ public static final String SMS = "sms";
+
+ // PlatformWhatsApp identifies the WhatsApp platform.
+ public static final String WHATSAPP = "whatsapp";
+
+ // PlatformFacebook identifies the Facebook platform.
+ public static final String FACEBOOK = "facebook";
+
+ // PlatformTelegram identifies the Telegram platform.
+ public static final String TELEGRAM = "telegram";
+
+ // PlatformLine identifies the LINE platform.
+ public static final String LINE = "line";
+
+ // PlatformWeChat identifies the WeChat platform.
+ public static final String WECHAT = "wechat";
+
+ // PlatformEmail identifies the Email platform.
+ public static final String EMAIL = "email";
+
+ // PlatformEvents identifies the Events platform
+ public static final String EVENTS = "events";
+
+ // PlatformWhatsAppSandbox identified the WhatsApp sandbox platform.
+ public static final String WHATSAPP_SANDBOX = "whatsapp_sandbox";
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java
new file mode 100644
index 00000000..7537da85
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java
@@ -0,0 +1,50 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * Identifies the recipient of an outbound WhatsApp message, as reported back on
+ * status webhook payloads under {@code status.metadata.recipient}. Mirrors
+ * {@link ConversationSenderMetadata} on the inbound side.
+ *
+ * {@code userId} is the recipient's BSUID (e.g. "US.13491208655302741918");
+ * {@code parentUserId} is the parent business-scoped user ID of the enterprise
+ * that owns the business portfolio it was scoped against (e.g.
+ * "US.ENT.11815799212886844830").
+ *
+ *
Either field may be {@code null}: Meta only supplies them for accounts
+ * enrolled in the BSUID rollout, and the enclosing {@code recipient} object is
+ * omitted entirely when neither is present. This is the only place a status
+ * payload carries the recipient's own identity — {@code messageMetadata.to} is
+ * an echo of the address the message was addressed to.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ConversationRecipientMetadata {
+
+ private String userId;
+ private String parentUserId;
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getParentUserId() {
+ return parentUserId;
+ }
+
+ public void setParentUserId(String parentUserId) {
+ this.parentUserId = parentUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationRecipientMetadata{" +
+ "userId='" + userId + '\'' +
+ ", parentUserId='" + parentUserId + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java
new file mode 100644
index 00000000..40ace8d7
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java
@@ -0,0 +1,125 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Map;
+
+public class ConversationSendRequest {
+ private String to;
+ private ConversationContentType type;
+ private ConversationContent content;
+ private String from;
+ private String reportUrl;
+ private String trackId;
+ private String ttl;
+ private ConversationFallbackOption fallback;
+ private Map source;
+ private ConversationMessageTag tag;
+
+ public ConversationSendRequest(String to, ConversationContentType type, ConversationContent content, String from, String reportUrl, ConversationFallbackOption fallback, Map source, ConversationMessageTag tag) {
+ this.to = to;
+ this.type = type;
+ this.content = content;
+ this.from = from;
+ this.reportUrl = reportUrl;
+ this.fallback = fallback;
+ this.source = source;
+ this.tag = tag;
+ }
+
+ public ConversationSendRequest() {
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public ConversationContentType getType() {
+ return type;
+ }
+
+ public void setType(ConversationContentType type) {
+ this.type = type;
+ }
+
+ public ConversationContent getContent() {
+ return content;
+ }
+
+ public void setContent(ConversationContent content) {
+ this.content = content;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getReportUrl() {
+ return reportUrl;
+ }
+
+ public void setReportUrl(String reportUrl) {
+ this.reportUrl = reportUrl;
+ }
+
+ public ConversationFallbackOption getFallback() {
+ return fallback;
+ }
+
+ public void setFallback(ConversationFallbackOption fallback) {
+ this.fallback = fallback;
+ }
+
+ public Map getSource() {
+ return source;
+ }
+
+ public void setSource(Map source) {
+ this.source = source;
+ }
+
+ public ConversationMessageTag getTag() {
+ return tag;
+ }
+
+ public void setTag(ConversationMessageTag tag) {
+ this.tag = tag;
+ }
+
+ public void setTrackId(String trackId) {
+ this.trackId = trackId;
+ }
+
+ public String getTrackId() {
+ return trackId;
+ }
+
+ public String getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(String ttl) {
+ this.ttl = ttl;
+ }
+ @Override
+ public String toString() {
+ return "ConversationSendRequest{" +
+ "to='" + to + '\'' +
+ ", type=" + type +
+ ", content=" + content +
+ ", from='" + from + '\'' +
+ ", reportUrl='" + reportUrl + '\'' +
+ ", trackId='" + trackId + '\'' +
+ ", ttl='" + ttl + '\'' +
+ ", fallback=" + fallback + '\'' +
+ ", tags=" + tag +
+ ", source='" + source + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java
new file mode 100644
index 00000000..8d928df6
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java
@@ -0,0 +1,60 @@
+package com.messagebird.objects.conversations;
+
+public class ConversationSendResponse {
+ private String id; //messageID
+ private String status;
+ private FallbackOptionResponse fallback;
+
+ public static class FallbackOptionResponse{
+ private String id;
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "FallbackOptionResponse{" +
+ "id='" + id + '\'' +
+ '}';
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public FallbackOptionResponse getFallback() {
+ return fallback;
+ }
+
+ public void setFallback(FallbackOptionResponse fallback) {
+ this.fallback = fallback;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationSendResponse{" +
+ "id='" + id + '\'' +
+ ", status='" + status + '\'' +
+ ", fallback=" + fallback +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java
new file mode 100644
index 00000000..b7fdcbb0
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java
@@ -0,0 +1,66 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * Metadata about the sender of a WhatsApp message. {@code userId} always
+ * contains the BSUID (e.g. "US.13491208655302741918") when Meta supplies one.
+ * When both a phone number and a BSUID are available, the phone number appears
+ * in the parent message's {@code from} field — not here.
+ *
+ * {@code parentUserId} carries the sender's parent business-scoped user ID
+ * (e.g. "US.ENT.11815799212886844830"), which identifies the enterprise that
+ * owns the business portfolio the {@code userId} was scoped against. It is only
+ * present for accounts enrolled in Meta's parent-BSUID rollout; for everyone
+ * else it stays {@code null}.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ConversationSenderMetadata {
+
+ private String userId;
+ private String parentUserId;
+ private String username;
+ private String displayName;
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getParentUserId() {
+ return parentUserId;
+ }
+
+ public void setParentUserId(String parentUserId) {
+ this.parentUserId = parentUserId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationSenderMetadata{" +
+ "userId='" + userId + '\'' +
+ ", parentUserId='" + parentUserId + '\'' +
+ ", username='" + username + '\'' +
+ ", displayName='" + displayName + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java
new file mode 100644
index 00000000..6f7c84fe
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java
@@ -0,0 +1,118 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Map;
+
+/**
+ * Request object used for starting a conversation.
+ */
+public class ConversationStartRequest {
+
+ private String to;
+ private ConversationContentType type;
+ private ConversationContent content;
+ private Map source;
+ private ConversationMessageTag tag;
+ private String channelId;
+ private String reportUrl;
+ private String trackId;
+ private String ttl;
+
+ public ConversationStartRequest(
+ final String to,
+ final ConversationContentType type,
+ final ConversationContent content,
+ final String channelId,
+ final Map source,
+ final ConversationMessageTag tag
+ ) {
+ this.to = to;
+ this.type = type;
+ this.content = content;
+ this.channelId = channelId;
+ this.source = source;
+ this.tag = tag;
+ }
+
+ public ConversationStartRequest() {
+ //
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(final String to) {
+ this.to = to;
+ }
+
+ public ConversationContentType getType() {
+ return type;
+ }
+
+ public void setType(final ConversationContentType type) {
+ this.type = type;
+ }
+
+ public ConversationContent getContent() {
+ return content;
+ }
+
+ public void setContent(final ConversationContent content) {
+ this.content = content;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(final String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getReportUrl() {
+ return reportUrl;
+ }
+
+ public void setReportUrl(final String reportUrl) {
+ this.reportUrl = reportUrl;
+ }
+
+ public Map getSource() {
+ return source;
+ }
+
+ public void setSource(Map source) {
+ this.source = source;
+ }
+
+ public ConversationMessageTag getTag() {
+ return tag;
+ }
+
+ public void setTag(ConversationMessageTag tag) {
+ this.tag = tag;
+ }
+
+ public String getTrackId() {
+ return trackId;
+ }
+
+ public void setTrackId(String trackId) {
+ this.trackId = trackId;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationStartRequest{" +
+ "to='" + to + '\'' +
+ ", type=" + type +
+ ", content=" + content +
+ ", source=" + source +
+ ", tag=" + tag +
+ ", channelId='" + channelId + '\'' +
+ ", reportUrl='" + reportUrl + '\'' +
+ ", trackId='" + trackId + '\'' +
+ ", ttl='" + ttl + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStatus.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatus.java
new file mode 100644
index 00000000..2ece4094
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatus.java
@@ -0,0 +1,44 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Indicates whether a conversation is active or if it has been archived.
+ */
+public enum ConversationStatus {
+
+ ACTIVE("active"),
+ ARCHIVED("archived");
+
+ private final String status;
+
+ ConversationStatus(final String status) {
+ this.status = status;
+ }
+
+ @JsonCreator
+ public static ConversationStatus forValue(final String value) {
+ for (ConversationStatus conversationStatus : ConversationStatus.values()) {
+ if (conversationStatus.getStatus().equals(value)) {
+ return conversationStatus;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getStatus();
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ @Override
+ public String toString() {
+ return getStatus();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java
new file mode 100644
index 00000000..47a4a8d1
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java
@@ -0,0 +1,92 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * The {@code messageMetadata} block delivered inside status webhook payloads
+ * (e.g. {@code statusSent}, {@code statusDelivered}). Reflects the original
+ * message that triggered the status update.
+ *
+ * This class is not produced by any SDK request — it is a standalone POJO
+ * intended for consumers who deserialize incoming webhook payloads in their
+ * own HTTP handlers. Use it via {@code ObjectMapper.readValue(body, ...)}.
+ *
+ *
Both {@code from} and {@code to} accept either a phone number or a
+ * WhatsApp Business-Scoped User ID (BSUID, e.g. "US.13491208655302741918").
+ * The BSUID is also available via {@code metadata.sender.userId}.
+ *
+ *
{@code to} echoes back the address the message was originally addressed
+ * to, so it is not a reliable source of the recipient's BSUID. That identity
+ * lives alongside this block, under {@code status.metadata.recipient} — see
+ * {@link ConversationStatusMetadata}.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ConversationStatusMessageMetadata {
+
+ private String id;
+ private String from;
+ private String to;
+ private String type;
+ private ConversationContent content;
+ private ConversationMessageMetadata metadata;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public ConversationContent getContent() {
+ return content;
+ }
+
+ public void setContent(ConversationContent content) {
+ this.content = content;
+ }
+
+ public ConversationMessageMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(ConversationMessageMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationStatusMessageMetadata{" +
+ "id='" + id + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", type='" + type + '\'' +
+ ", content=" + content +
+ ", metadata=" + metadata +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java
new file mode 100644
index 00000000..0b4d5f7f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java
@@ -0,0 +1,85 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * The {@code status.metadata} block delivered inside status webhook payloads
+ * (e.g. {@code statusSent}, {@code statusDelivered}).
+ *
+ *
This class is not produced by any SDK request — it is a standalone POJO
+ * intended for consumers who deserialize incoming webhook payloads in their own
+ * HTTP handlers. Use it via {@code ObjectMapper.readValue(body, ...)}.
+ *
+ *
Note the mixed casing of this object. {@code pricing} and
+ * {@code conversation} are near-verbatim passthroughs of Meta's own objects and
+ * so keep their snake_case keys ({@code pricing_model}, {@code category}, …);
+ * they are exposed here as raw maps rather than modelled types, because their
+ * contents track Meta's schema rather than ours. {@code recipient} is ours and
+ * follows the camelCase convention used everywhere else in the API. Any other
+ * key present on the payload — for example {@code biz_opaque_callback_data} —
+ * is collected into {@link #getAdditionalProperties()} rather than dropped.
+ *
+ *
{@code recipient} is absent from payloads for accounts that never receive
+ * BSUIDs, in which case {@link #getRecipient()} returns {@code null}.
+ */
+public class ConversationStatusMetadata {
+
+ private Map pricing;
+ private Map conversation;
+ private ConversationRecipientMetadata recipient;
+ private final Map additionalProperties = new LinkedHashMap<>();
+
+ public Map getPricing() {
+ return pricing;
+ }
+
+ public void setPricing(Map pricing) {
+ this.pricing = pricing;
+ }
+
+ public Map getConversation() {
+ return conversation;
+ }
+
+ public void setConversation(Map conversation) {
+ this.conversation = conversation;
+ }
+
+ public ConversationRecipientMetadata getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(ConversationRecipientMetadata recipient) {
+ this.recipient = recipient;
+ }
+
+ /**
+ * Every key on the payload that has no dedicated accessor above, in the
+ * order it was encountered. Empty when the payload holds nothing else.
+ *
+ * @return the unmodelled remainder of the metadata object
+ */
+ @JsonAnyGetter
+ public Map getAdditionalProperties() {
+ return additionalProperties;
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String name, Object value) {
+ additionalProperties.put(name, value);
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationStatusMetadata{" +
+ "pricing=" + pricing +
+ ", conversation=" + conversation +
+ ", recipient=" + recipient +
+ ", additionalProperties=" + additionalProperties +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java
new file mode 100644
index 00000000..9a67a516
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java
@@ -0,0 +1,29 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Objects;
+
+public class ConversationUpdateRequest {
+
+ private final ConversationStatus status;
+
+ public ConversationUpdateRequest(ConversationStatus status) {
+ this.status = status;
+ }
+
+ public ConversationStatus getStatus() {
+ return status;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ConversationUpdateRequest that = (ConversationUpdateRequest) o;
+ return status == that.status;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(status);
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhook.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhook.java
new file mode 100644
index 00000000..91689f97
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhook.java
@@ -0,0 +1,94 @@
+package com.messagebird.objects.conversations;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Webhooks enable real-time notifications of conversation events to be
+ * delivered to endpoints on your own server. A webhook can be configured with
+ * a URL and a list of events that should be subscribed to for notifications.
+ * It's possible to create multiple webhooks with different URLs to listen to
+ * one or more events each.
+ */
+public class ConversationWebhook {
+
+ private String id;
+ private String channelId;
+ private String url;
+ private ConversationWebhookStatus status;
+ private List events;
+ private Date createdDatetime;
+ private Date updatedDatetime;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public ConversationWebhookStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ConversationWebhookStatus status) {
+ this.status = status;
+ }
+
+ public List getEvents() {
+ return events;
+ }
+
+ public void setEvents(List events) {
+ this.events = events;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getUpdatedDatetime() {
+ return updatedDatetime;
+ }
+
+ public void setUpdatedDatetime(Date updatedDatetime) {
+ this.updatedDatetime = updatedDatetime;
+ }
+
+ @Override
+ public String toString() {
+ return "ConversationWebhook{" +
+ "id='" + id + '\'' +
+ ", channelId='" + channelId + '\'' +
+ ", url='" + url + '\'' +
+ ", status='" + status + '\'' +
+ ", events=" + events.stream().map(ConversationWebhookEvent::toString).collect(Collectors.joining(",")) +
+ ", createdDatetime=" + createdDatetime +
+ ", updatedDatetime=" + updatedDatetime +
+ '}';
+ }
+
+
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookBaseRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookBaseRequest.java
new file mode 100644
index 00000000..47b1d6b1
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookBaseRequest.java
@@ -0,0 +1,41 @@
+package com.messagebird.objects.conversations;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Contains common fields for webhook requests.
+ */
+public abstract class ConversationWebhookBaseRequest {
+ protected String url;
+ protected List events;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public List getEvents() {
+ return events;
+ }
+
+ public void setEvents(List events) {
+ this.events = events;
+ }
+
+ protected abstract String getRequestName();
+
+ protected abstract String getStringRepresentationOfExtraParameters();
+
+ @Override
+ public String toString() {
+ return getRequestName() + "{" +
+ getStringRepresentationOfExtraParameters() + '\'' +
+ ", url='" + url + '\'' +
+ ", events=" + events.stream().map(ConversationWebhookEvent::toString).collect(Collectors.joining(",")) +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java
new file mode 100644
index 00000000..24cf7f9a
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java
@@ -0,0 +1,35 @@
+package com.messagebird.objects.conversations;
+
+import java.util.List;
+
+/**
+ * Request object used to create webhooks.
+ */
+public class ConversationWebhookCreateRequest extends ConversationWebhookBaseRequest{
+
+ private String channelId;
+
+ public ConversationWebhookCreateRequest(
+ final String channelId,
+ final String url,
+ final List events
+ ) {
+ this.channelId = channelId;
+ this.url = url;
+ this.events = events;
+ }
+
+ @Override
+ protected String getRequestName() {
+ return "ConversationWebhookCreateRequest";
+ }
+
+ @Override
+ protected String getStringRepresentationOfExtraParameters() {
+ return "channelId='" + channelId;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookEvent.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookEvent.java
new file mode 100644
index 00000000..0cb4bf04
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookEvent.java
@@ -0,0 +1,62 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Represents events that webhooks can subscribe to.
+ */
+public enum ConversationWebhookEvent {
+
+ /**
+ * New conversation has been created.
+ */
+ CONVERSATION_CREATED("conversation.created"),
+
+ /**
+ * Conversation has been updated with a new status.
+ */
+ CONVERSATION_UPDATED("conversation.updated"),
+
+ /**
+ * New message has been created. Triggered for both sent and received
+ * messages.
+ */
+ MESSAGE_CREATED("message.created"),
+
+ /**
+ * Message has been updated with a new status.
+ */
+ MESSAGE_UPDATED("message.updated");
+
+ private final String event;
+
+ ConversationWebhookEvent(final String event) {
+ this.event = event;
+ }
+
+ @JsonCreator
+ public static ConversationWebhookEvent forValue(final String value) {
+ for (ConversationWebhookEvent event : ConversationWebhookEvent.values()) {
+ if (event.getEvent().equals(value)) {
+ return event;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getEvent();
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ @Override
+ public String toString() {
+ return getEvent();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookList.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookList.java
new file mode 100644
index 00000000..fd34aeaf
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookList.java
@@ -0,0 +1,11 @@
+package com.messagebird.objects.conversations;
+
+import com.messagebird.objects.ListBase;
+
+/**
+ * Response object for the webhook listing.
+ */
+public class ConversationWebhookList extends ListBase {
+
+ //
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookStatus.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookStatus.java
new file mode 100644
index 00000000..0e5dd186
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookStatus.java
@@ -0,0 +1,43 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Indicates whether a conversation webhook is enabled or disabled.
+ */
+public enum ConversationWebhookStatus {
+ ENABLED("enabled"),
+ DISABLED("disabled");
+
+ private final String status;
+
+ ConversationWebhookStatus(final String status) {
+ this.status = status;
+ }
+
+ @JsonCreator
+ public static ConversationWebhookStatus forValue(final String value) {
+ for (ConversationWebhookStatus conversationWebhookStatus : ConversationWebhookStatus.values()) {
+ if (conversationWebhookStatus.getStatus().equals(value)) {
+ return conversationWebhookStatus;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getStatus();
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ @Override
+ public String toString() {
+ return getStatus();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookUpdateRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookUpdateRequest.java
new file mode 100644
index 00000000..c882ef41
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookUpdateRequest.java
@@ -0,0 +1,38 @@
+package com.messagebird.objects.conversations;
+
+import java.util.List;
+
+/**
+ * Request object used to update webhook.
+ */
+public class ConversationWebhookUpdateRequest extends ConversationWebhookBaseRequest {
+ private ConversationWebhookStatus status;
+
+ public ConversationWebhookUpdateRequest(
+ final ConversationWebhookStatus status,
+ final String url,
+ final List events
+ ) {
+ this.status = status;
+ this.url = url;
+ this.events = events;
+ }
+
+ public ConversationWebhookStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ConversationWebhookStatus status) {
+ this.status = status;
+ }
+
+ @Override
+ protected String getRequestName() {
+ return "ConversationWebhookUpdateRequest";
+ }
+
+ @Override
+ protected String getStringRepresentationOfExtraParameters() {
+ return "status='" + status;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java b/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java
new file mode 100644
index 00000000..24ea4be6
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java
@@ -0,0 +1,31 @@
+package com.messagebird.objects.conversations;
+
+public class HSMCurrency {
+
+ private String currencyCode;
+ private int amount;
+
+ public String getCurrencyCode() {
+ return currencyCode;
+ }
+
+ public void setCurrencyCode(String currencyCode) {
+ this.currencyCode = currencyCode;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ public void setAmount(int amount) {
+ this.amount = amount;
+ }
+
+ @Override
+ public String toString() {
+ return "HSMCurrency{" +
+ "currencyCode='" + currencyCode + '\'' +
+ ", amount=" + amount +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/Media.java b/api/src/main/java/com/messagebird/objects/conversations/Media.java
new file mode 100644
index 00000000..4a9bf4ea
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/Media.java
@@ -0,0 +1,31 @@
+package com.messagebird.objects.conversations;
+
+public class Media {
+
+ private String url;
+ private String caption;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getCaption() {
+ return caption;
+ }
+
+ public void setCaption(String caption) {
+ this.caption = caption;
+ }
+
+ @Override
+ public String toString() {
+ return "Media{" +
+ "url='" + url + '\'' +
+ ", caption='" + caption + '\'' +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java b/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java
new file mode 100644
index 00000000..d3a7c20b
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java
@@ -0,0 +1,82 @@
+package com.messagebird.objects.conversations;
+
+import java.util.List;
+
+public class MessageComponent {
+
+ private MessageComponentType type;
+ private String sub_type;
+ private int index;
+ private List parameters;
+ private int card_index;
+ private List cards;
+ private List components;
+
+ public void setType(MessageComponentType type) {
+ this.type = type;
+ }
+
+ public MessageComponentType getType() {
+ return type;
+ }
+
+ public String getSub_type() {
+ return sub_type;
+ }
+
+ public void setSub_type(String sub_type) {
+ this.sub_type = sub_type;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public List getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List parameters) {
+ this.parameters = parameters;
+ }
+
+ public void setCards(List cards) {
+ this.cards = cards;
+ }
+
+ public List getCards() {
+ return cards;
+ }
+
+ public int getCard_index() {
+ return card_index;
+ }
+
+ public void setCard_index(int card_index) {
+ this.card_index = card_index;
+ }
+
+ public List getComponents() {
+ return components;
+ }
+
+ public void setComponents(List components) {
+ this.components = components;
+ }
+
+ @Override
+ public String toString() {
+ return "MessageComponent{" +
+ "type='" + type + '\'' +
+ ", sub_type='" + sub_type + '\'' +
+ ", index=" + index + '\'' +
+ ", parameters=" + parameters + '\'' +
+ ", components=" + components + '\'' +
+ ", cards=" + cards +
+ '}';
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java b/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java
new file mode 100644
index 00000000..9f9290cf
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java
@@ -0,0 +1,54 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.*;
+
+public enum MessageComponentType {
+
+ HEADER("header"),
+ BODY("body"),
+ FOOTER("footer"),
+ BUTTON("button"),
+ CARD("card"),
+ CAROUSEL("carousel"),
+ LIMITED_TIME_OFFER("limited_time_offer"),
+ COPY_CODE("copy_code");
+
+ private static final Map TYPE_MAP;
+
+ static {
+ Map map = new HashMap<>();
+ for (MessageComponentType componentType : MessageComponentType.values()) {
+ map.put(componentType.getType().toLowerCase(), componentType);
+ }
+ TYPE_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private final String type;
+
+ MessageComponentType(final String type) {
+ this.type = type;
+ }
+
+ @JsonCreator
+ public static MessageComponentType forValue(String value) {
+ Objects.requireNonNull(value, "Value cannot be null");
+ return TYPE_MAP.get(value.toLowerCase(Locale.ROOT));
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getType();
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return getType();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java b/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java
new file mode 100644
index 00000000..6432369f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java
@@ -0,0 +1,125 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.commons.lang3.StringUtils;
+
+public class MessageParam {
+
+ private TemplateMediaType type;
+ private String text;
+ private String payload;
+ private HSMCurrency currency;
+ private String dateTime;
+ private Media document;
+ private Media image;
+ private Media video;
+ @JsonProperty("expiration_time")
+ private String expirationTime;
+ @JsonProperty("coupon_code")
+ private String couponCode;
+
+ public TemplateMediaType getType() {
+ return type;
+ }
+
+ public void setType(TemplateMediaType type) {
+ this.type = type;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ if (StringUtils.isBlank(text)) {
+ throw new IllegalArgumentException("Text cannot be null or empty");
+ }
+ this.text = text;
+ }
+
+ public String getPayload() {
+ return payload;
+ }
+
+ public void setPayload(String payload) {
+ this.payload = payload;
+ }
+
+ public HSMCurrency getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(HSMCurrency currency) {
+ this.currency = currency;
+ }
+
+ public String getDateTime() {
+ return dateTime;
+ }
+
+ public void setDateTime(String dateTime) {
+ if (StringUtils.isBlank(dateTime)) {
+ throw new IllegalArgumentException("dateTime cannot be null or empty");
+ }
+ this.dateTime = dateTime;
+ }
+
+ public Media getDocument() {
+ return document;
+ }
+
+ public void setDocument(Media document) {
+ this.document = document;
+ }
+
+ public Media getImage() {
+ return image;
+ }
+
+ public void setImage(Media image) {
+ this.image = image;
+ }
+
+ public Media getVideo() { return video; }
+
+ public void setVideo(Media video) { this.video = video; }
+
+ public String getExpirationTime() {
+ return expirationTime;
+ }
+
+ public void setExpirationTime(String expirationTime) {
+ if (StringUtils.isBlank(expirationTime)) {
+ throw new IllegalArgumentException("expirationTime cannot be null or empty");
+ }
+ this.expirationTime = expirationTime;
+ }
+
+ public String getCouponCode() {
+ return couponCode;
+ }
+
+ public void setCouponCode(String couponCode) {
+ if (StringUtils.isBlank(couponCode)) {
+ throw new IllegalArgumentException("couponCode cannot be null or empty");
+ }
+ this.couponCode = couponCode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("MessageParam{");
+ sb.append("type=").append(type)
+ .append(", text='").append(text).append('\'')
+ .append(", payload='").append(payload).append('\'')
+ .append(", currency=").append(currency)
+ .append(", dateTime='").append(dateTime).append('\'')
+ .append(", document=").append(document)
+ .append(", image=").append(image)
+ .append(", video=").append(video)
+ .append(", expirationTime='").append(expirationTime).append('\'')
+ .append(", couponCode='").append(couponCode).append('\'')
+ .append('}');
+ return sb.toString();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java b/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java
new file mode 100644
index 00000000..580dcc97
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java
@@ -0,0 +1,60 @@
+package com.messagebird.objects.conversations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+
+public enum TemplateMediaType {
+
+ IMAGE("image"),
+ DOCUMENT("document"),
+ VIDEO("video"),
+ TEXT("text"),
+ CURRENCY("currency"),
+ DATETIME("date_time"),
+ PAYLOAD("payload"),
+ EXPIRATION_TIME("expiration_time"),
+ COUPON_CODE("coupon_code");
+
+ private static final Map TYPE_MAP;
+
+ static {
+ Map map = new HashMap<>();
+ for (TemplateMediaType templateMediaType : TemplateMediaType.values()) {
+ map.put(templateMediaType.getType().toLowerCase(), templateMediaType);
+ }
+ TYPE_MAP = Collections.unmodifiableMap(map);
+ }
+
+
+ private final String type;
+
+ TemplateMediaType(final String type) {
+ this.type = type;
+ }
+
+ @JsonCreator
+ public static TemplateMediaType forValue(String value) {
+ if (value == null) {
+ throw new IllegalArgumentException("Value cannot be null");
+ }
+ return TYPE_MAP.get(value.toLowerCase());
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getType();
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return getType();
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java b/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java
new file mode 100644
index 00000000..3edb1db9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java
@@ -0,0 +1,48 @@
+package com.messagebird.objects.integrations;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * An enum for HSMComponentFormat
+ *
+ * @see HSMComponentFormat
+ * @author ssk910
+ */
+public enum HSMCategory {
+
+ AUTHENTICATION("AUTHENTICATION"),
+ UTILITY("UTILITY"),
+ MARKETING("MARKETING");
+
+ private final String category;
+
+ HSMCategory(String category) {
+ this.category = category;
+ }
+
+ @JsonCreator
+ public static HSMCategory forValue(String value) {
+ for (HSMCategory hsmCategory : HSMCategory.values()) {
+ if (hsmCategory.getCategory().equals(value)) {
+ return hsmCategory;
+ }
+ }
+
+ return null;
+ }
+
+ @JsonValue
+ public String toJson() {
+ return getCategory();
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ @Override
+ public String toString() {
+ return getCategory();
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java
new file mode 100644
index 00000000..ed93847f
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java
@@ -0,0 +1,196 @@
+package com.messagebird.objects.integrations;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+/**
+ * A class for HSMComponent object
+ *
+ * @see HSMComponent
+ * @author ssk910
+ */
+public class HSMComponent {
+
+ private HSMComponentType type;
+ private HSMComponentFormat format;
+ private String text;
+ @JsonProperty("add_security_recommendation")
+ private Boolean addSecurityRecommendation;
+ @JsonProperty("code_expiration_minutes")
+ private Integer codeExpirationMinutes;
+ private List buttons;
+ @JsonProperty("has_expiration")
+ private Boolean hasExpiration;
+
+ private List cards;
+
+ private HSMExample example;
+
+ public HSMComponentType getType() {
+ return type;
+ }
+
+ public void setType(HSMComponentType type) {
+ this.type = type;
+ }
+
+ public HSMComponentFormat getFormat() {
+ return format;
+ }
+
+ public void setFormat(HSMComponentFormat format) {
+ this.format = format;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ if (StringUtils.isBlank(text)) {
+ throw new IllegalArgumentException("Text cannot be null or empty");
+ }
+ this.text = text;
+ }
+
+ public List getButtons() {
+ return buttons;
+ }
+
+ public void setButtons(List