diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..42f72dc9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+### Java ###
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# The various target folders
+*target/
+*.iml
+.idea
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..9e3235b4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: java
+dist: trusty
+before_script: cd api/
+script: mvn test -Ptest -DskipTests=false -Dhttps.protocols=TLSv1.2 -DmessageBirdAccessKey=test_iQpAp0KCs5GCsMpDhIx2leuNB -DmessageBirdMSISDN=31612345678
+jdk:
+ - oraclejdk11
+ - openjdk11
+ - openjdk8
diff --git a/README.md b/README.md
index 34901d1d..fbacf4fe 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,29 @@
MessageBird's REST API for Java
=================================
-This repository contains the open source Java client for MessageBird's REST API. Documentation can be found at: https://www.messagebird.com/developers/.
+This repository contains the open source Java client for MessageBird's REST API. Documentation can be found at: https://developers.messagebird.com.
+
+[](https://travis-ci.org/messagebird/java-rest-api)
+
+
Requirements
------------
-- [Sign up](https://www.messagebird.com/en/signup) for a free MessageBird account
+- [Sign up](https://dashboard.messagebird.com/en/sign-up) for a free MessageBird account
- Create a new access key in the developers sections
-- An application written in Java
+- An application written in Java
- Installed version of maven
Installation
------------
-The easiest way to install the messagebird package is either via github:
+The easiest way to install the MessageBird package is either via github:
```
git clone https://github.com/messagebird/java-rest-api
-cd java-rest-api
+cd java-rest-api/api
mvn install
```
-If you are using maven simply add the messagebird API to your dependencies like this:
-
-```
-
- com.messagebird
- messagebird-api
- 1.1.4
-
-```
-
-In case you are building without maven you still need maven to build the libraries but
-then simply copy the following jar's over to your project
-
-```
-messagebird-api-1.1.4.jar
-jackson-core-2.1.1.jar
-jackson-databind-2.1.1.jar
-jackson-mapper-asl-1.9.13.jar
-jackson-core-asl-1.9.13.jar
-jackson-annotations-2.1.1.jar
-jackson-dataformat-csv-2.1.1.jar
-```
-
+If you are using maven, please refer to the [mvn repository](https://mvnrepository.com/artifact/com.messagebird/messagebird-api) and choose your desired package version.
Examples
--------
@@ -76,10 +58,10 @@ Now you can query the API for information or send a request. For example, if we
// Get Balance
System.out.println("Retrieving your balance:");
final Balance balance = messageBirdClient.getBalance();
-
+
// Display balance
System.out.println(balance.toString());
-
+
} catch (UnauthorizedException unauthorized) {
if (unauthorized.getErrors() != null) {
System.out.println(unauthorized.getErrors().toString());
@@ -102,19 +84,21 @@ Balance{payment='prepaid', type='credits', amount=97}
To try out the command line examples follow the above build instructions.
When everything did build successful you can try out the API like this:
```shell
-cd examples/target
-java -cp examples-1.0.0-jar-with-dependencies.jar ExampleSendMessage test_JHWhczvG29P3GUpywWHmR9H4a 31682053703 "This is a test message"
+cd examples/target
+java -cp examples-1.2.0-jar-with-dependencies.jar ExampleSendMessage test_gshuPaZoeEG6ovbc8M79w0QyM 31612345678 "This is a test message"
```
Please see the other examples for a complete overview of all the available API calls.
Running with unit tests
-----------------------
-Running unit tests requires you to create a developer key. You can create one for free on https://www.messagebird.com/nl/signup,
-or when you have one already you can skip registration and go to https://www.messagebird.com/nl/settings/developers/access
+Running unit tests requires you to create a developer key. You can create one for free at https://dashboard.messagebird.com/en/sign-up,
+or when you have one already you can skip registration and go to https://dashboard.messagebird.com/en/settings/developers/access
to create or get your test access key. Once you have such key you can run the unit tests like this:
+```shell
mvn test -Ptest -DmessageBirdAccessKey=[your access key] -DmessageBirdMSISDN=[your phone]
+```
This will run all unit tests and verifies if everything is running as expected.
@@ -129,13 +113,13 @@ If you server doesn't have a direct connection to the internet you can setup a p
final Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.0.0.1", 8080));
final MessageBirdService messageBirdService = new MessageBirdServiceImpl("test_gshuPaZoeEG6ovbc8M79w0QyM");
messageBirdService.setProxy(proxy);
-```
+```
Documentation
-------------
Complete documentation, instructions, and examples are available at:
-[https://www.messagebird.com/developers/java](https://www.messagebird.com/developers/java).
+[https://developers.messagebird.com](https://developers.messagebird.com).
License
-------
-The MessageBird REST Client for Java is licensed under [The BSD 2-Clause License](http://opensource.org/licenses/BSD-2-Clause). Copyright (c) 2015, MessageBird
+The MessageBird REST Client for Java is licensed under [The BSD 2-Clause License](http://opensource.org/licenses/BSD-2-Clause). Copyright (c) 2015-2019, MessageBird
diff --git a/api/pom.xml b/api/pom.xml
index ad329412..b64ee8b0 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -1,18 +1,52 @@
-
+4.0.0
-
- com.messagebird
- api_root
- 1.0.0
-
-
com.messagebirdmessagebird-api
- 1.0
+ 6.4.0
+ jar
+
+ ${project.groupId}:${project.artifactId}
+ The MessageBird API provides an API to the MessageBird SMS, Chat and Voice services located at https://www.messagebird.com.
+
+ https://www.messagebird.com/
+
+
+
+ The BSD 2-Clause License
+ http://opensource.org/licenses/BSD-2-Clause
+ repo
+
+
+
+
+
+ Bob Violier
+ bob@messagebird.com
+ MessageBird
+ https://www.messagebird.com
+
+
+ Venkateswaran S
+ venkat.sankaran@bird.com
+ MessageBird
+ https://www.messagebird.com
+
+
+ Sam Wierema
+ sam@messagebird.com
+ MessageBird
+ https://www.messagebird.com
+
+
+
+
+ scm:git:git@github.com:messagebird/java-rest-api.git
+ scm:git:git@github.com:messagebird/java-rest-api.git
+ git@github.com:messagebird/java-rest-api.git
+ HEAD
+
@@ -22,58 +56,207 @@
true
-
-
+
+
+ UTF-8
+
testfalse
+
+
+ UTF-8
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ false
+
+ **/ContactTest.java
+ **/MessageBirdClientTest.java
+
+
+
+
+
+
+
+
+ integration
+
+ false
+ UTF-8
+
+
+
+
+ disable-doclint
+
+ [1.8,11)
+
+
+ none
+
+
+ UTF-8
+
+
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 3.6.1
+
+
+
+
- com.fasterxml.jackson.dataformat
- jackson-dataformat-csv
- 2.1.1
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.13.2
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.14.0-rc1
- org.codehaus.jackson
- jackson-mapper-asl
- 1.9.13
+ com.auth0
+ java-jwt
+ 4.4.0
- org.codehaus.jackson
- jackson-core-asl
- 1.9.13
+ org.apache.maven
+ maven-artifact
+ 3.9.6junitjunit
- 4.11
+ 4.13.1test
-
+
+ org.unitils
+ unitils-core
+ 3.4.2
+ test
+
+
+ org.mockito
+ mockito-core
+ 2.21.0
+ test
+
+
+ org.jetbrains
+ annotations
+ 13.0
+ provided
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.1.0
+
+
+ package
+
+ jar
+
+
+
+
+ none
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.7
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+ org.apache.maven.pluginsmaven-compiler-plugin
- 3.1
+ 3.7.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ 3.1.0
+ maven-assembly-plugin
- 1.6
- 1.6
+
+ jar-with-dependencies
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+ org.apache.maven.pluginsmaven-surefire-plugin
- 2.18.1
+ 2.21.0${skipTests}
@@ -82,6 +265,25 @@
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.7.0
+ true
+
+ central
+ false
+ validated
+
+
diff --git a/api/src/main/java/com/messagebird/APIResponse.java b/api/src/main/java/com/messagebird/APIResponse.java
new file mode 100644
index 00000000..0bd07626
--- /dev/null
+++ b/api/src/main/java/com/messagebird/APIResponse.java
@@ -0,0 +1,75 @@
+package com.messagebird;
+
+/**
+ * Represents a raw response received from the API.
+ */
+public class APIResponse {
+
+ /**
+ * Status indicates a successful request.
+ */
+ private static final int STATUS_OK = 200;
+
+ private static final int STATUS_NO_CONTENT = 204;
+
+ private static final int STATUS_RANGE_SUCCESS_START = 200;
+ private static final int STATUS_RANGE_SUCCESS_END = 299;
+
+ private final String body;
+ private final int status;
+
+ APIResponse(final String body, final int status) {
+ this.body = body;
+ this.status = status;
+ }
+
+ /**
+ * Initializes an APIResponse object and sets the HTTP status code to 200.
+ * @param body response body
+ */
+ APIResponse(final String body) {
+ this(body, STATUS_OK);
+ }
+
+ /**
+ * Determines whether the provided HTTP status code indicates success.
+ *
+ * @param status response status
+ *
+ * @return True if the status indicates success.
+ */
+ static boolean isSuccessStatus(final int status) {
+ return status >= STATUS_RANGE_SUCCESS_START
+ && status <= STATUS_RANGE_SUCCESS_END;
+ }
+
+ /**
+ * Determines whether this request is successful based on its HTTP status
+ * code.
+ *
+ * @return True if the request is successful.
+ */
+ public boolean isSuccess() {
+ return isSuccessStatus(status);
+ }
+
+ /**
+ * Determines whether this request has content, based on its HTTP status
+ * code and otherwise by inspecting the response body.
+ *
+ * @return True if the request has content.
+ */
+ public boolean hasContent() {
+ return status == STATUS_NO_CONTENT
+ || body == null
+ || body.isEmpty();
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+}
diff --git a/api/src/main/java/com/messagebird/MessageBirdClient.java b/api/src/main/java/com/messagebird/MessageBirdClient.java
index 52f3c694..583dcb40 100644
--- a/api/src/main/java/com/messagebird/MessageBirdClient.java
+++ b/api/src/main/java/com/messagebird/MessageBirdClient.java
@@ -4,16 +4,53 @@
import com.messagebird.exceptions.NotFoundException;
import com.messagebird.exceptions.UnauthorizedException;
import com.messagebird.objects.*;
-
+import com.messagebird.objects.conversations.Conversation;
+import com.messagebird.objects.conversations.ConversationList;
+import com.messagebird.objects.conversations.ConversationMessage;
+import com.messagebird.objects.conversations.ConversationMessageList;
+import com.messagebird.objects.conversations.ConversationMessageRequest;
+import com.messagebird.objects.conversations.ConversationSendRequest;
+import com.messagebird.objects.conversations.ConversationSendResponse;
+import com.messagebird.objects.conversations.ConversationStartRequest;
+import com.messagebird.objects.conversations.ConversationStatus;
+import com.messagebird.objects.conversations.ConversationUpdateRequest;
+import com.messagebird.objects.conversations.ConversationWebhook;
+import com.messagebird.objects.conversations.ConversationWebhookCreateRequest;
+import com.messagebird.objects.conversations.ConversationWebhookList;
+import com.messagebird.objects.conversations.ConversationWebhookUpdateRequest;
+import com.messagebird.objects.integrations.Template;
+import com.messagebird.objects.integrations.TemplateList;
+import com.messagebird.objects.integrations.TemplateResponse;
+import com.messagebird.objects.voicecalls.RecordingResponse;
+import com.messagebird.objects.voicecalls.TranscriptionResponse;
+import com.messagebird.objects.voicecalls.VoiceCall;
+import com.messagebird.objects.voicecalls.VoiceCallFlowList;
+import com.messagebird.objects.voicecalls.VoiceCallFlowRequest;
+import com.messagebird.objects.voicecalls.VoiceCallFlowResponse;
+import com.messagebird.objects.voicecalls.VoiceCallLeg;
+import com.messagebird.objects.voicecalls.VoiceCallLegResponse;
+import com.messagebird.objects.voicecalls.VoiceCallResponse;
+import com.messagebird.objects.voicecalls.VoiceCallResponseList;
+import com.messagebird.objects.voicecalls.Webhook;
+import com.messagebird.objects.voicecalls.WebhookList;
+import com.messagebird.objects.voicecalls.WebhookResponseData;
+import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
-import java.text.SimpleDateFormat;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
* Message bird general client
- *
+ *
*
* Initialise this class with a MessageBirdService
* // First create your service object
@@ -24,21 +61,76 @@
* Then read your balance like this:
* final Balance balance = messageBirdClient.getBalance();
*
- *
+ *
* Created by rvt on 1/5/15.
*/
public class MessageBirdClient {
+
+ /**
+ * The Conversations API has a different URL scheme from the other
+ * APIs/endpoints. By default, the service prefixes paths with that URL. We
+ * can, however, override this behaviour by providing absolute URLs
+ * ourselves.
+ */
+ static final String CONVERSATIONS_BASE_URL = "https://conversations.messagebird.com/v1";
+ static final String VOICE_CALLS_BASE_URL = "https://voice.messagebird.com";
+ static final String NUMBERS_CALLS_BASE_URL = "https://numbers.messagebird.com/v1";
+ static final String MESSAGING_BASE_URL = "https://messaging.messagebird.com/v1";
+ static final String INTEGRATIONS_BASE_URL_V2 = "https://integrations.messagebird.com/v2";
+ static final String INTEGRATIONS_BASE_URL_V3 = "https://integrations.messagebird.com/v3";
+ private static String[] supportedLanguages = {"de-DE", "en-AU", "en-UK", "en-US", "es-ES", "es-LA", "fr-FR", "it-IT", "nl-NL", "pt-BR"};
+ static final String PARTNER_ACCOUNTS_BASE_URL = "https://partner-accounts.messagebird.com";
+
private static final String BALANCEPATH = "/balance";
+ private static final String CONTACTPATH = "/contacts";
+ private static final String GROUPPATH = "/groups";
private static final String HLRPATH = "/hlr";
+ private static final String LOOKUPHLRPATH = "/lookup/%s/hlr";
+ private static final String LOOKUPPATH = "/lookup";
private static final String MESSAGESPATH = "/messages";
+ private static final String VERIFYPATH = "/verify";
+ private static final String VERIFYEMAILPATH = "/verify/messages/email";
private static final String VOICEMESSAGESPATH = "/voicemessages";
+ static final String CONVERSATION_PATH = "/conversations";
+ static final String CONVERSATION_SEND_PATH = "/send";
+ static final String CONVERSATION_MESSAGE_PATH = "/messages";
+ static final String CONVERSATION_WEBHOOK_PATH = "/webhooks";
+ static final String INTEGRATIONS_WHATSAPP_PATH = "/platforms/whatsapp";
+ static final String VOICECALLSPATH = "/calls";
+ static final String LEGSPATH = "/legs";
+ static final String RECORDINGPATH = "/recordings";
+ static final String TRANSCRIPTIONPATH = "/transcriptions";
+ static final String WEBHOOKS = "/webhooks";
+ static final String VOICECALLFLOWPATH = "/call-flows";
+ private static final String VOICELEGS_SUFFIX_PATH = "/legs";
+ static final String FILES_PATH = "/files";
+ static final String TEMPLATES_PATH = "/templates";
+ static final String UNPAUSE_TEMAPLATE_PATH = "/unpause";
+ static final String OUTBOUND_SMS_PRICING_PATH = "/pricing/sms/outbound";
+ static final String OUTBOUND_SMS_PRICING_SMPP_PATH = "/pricing/sms/outbound/smpp/%s";
+
+ static final String RECORDING_DOWNLOAD_FORMAT = ".wav";
+
+ static final String TRANSCRIPTION_DOWNLOAD_FORMAT = ".txt";
+
+ private static final int DEFAULT_MACHINE_TIMEOUT_VALUE = 7000;
+ private static final int MIN_MACHINE_TIMEOUT_VALUE = 400;
+ private static final int MAX_MACHINE_TIMEOUT_VALUE = 10000;
+
+ private static final String[] MESSAGE_LIST_FILTERS_VALS = {"originator", "recipient", "direction", "searchterm", "type", "contact_id", "status", "from", "until"};
+ private static final Set MESSAGE_LIST_FILTERS = new HashSet<>(Arrays.asList(MESSAGE_LIST_FILTERS_VALS));
+
+ private static final String[] CONVERSATION_MESSAGE_LIST_FILTERS_VALS = {"ids", "from"};
+ private static final Set CONVERSATION_MESSAGE_LIST_FILTERS = new HashSet<>(Arrays.asList(CONVERSATION_MESSAGE_LIST_FILTERS_VALS));
+
+ private final String DOWNLOADS = "Downloads";
+
private MessageBirdService messageBirdService;
public MessageBirdClient(final MessageBirdService messageBirdService) {
this.messageBirdService = messageBirdService;
}
-
/****************************************************************************************************/
/** Balance and HRL methods **/
/****************************************************************************************************/
@@ -47,8 +139,8 @@ public MessageBirdClient(final MessageBirdService messageBirdService) {
* MessageBird provides an API to get the balance information of your account.
*
* @return Balance object
- * @throws GeneralException
- * @throws com.messagebird.exceptions.UnauthorizedException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public Balance getBalance() throws GeneralException, UnauthorizedException, NotFoundException {
return messageBirdService.requestByID(BALANCEPATH, "", Balance.class);
@@ -61,8 +153,8 @@ public Balance getBalance() throws GeneralException, UnauthorizedException, NotF
* @param msisdn The telephone number.
* @param reference A client reference
* @return Hlr Object
- * @throws GeneralException
- * @throws UnauthorizedException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public Hlr getRequestHlr(final BigInteger msisdn, final String reference) throws GeneralException, UnauthorizedException {
if (msisdn == null) {
@@ -82,8 +174,8 @@ public Hlr getRequestHlr(final BigInteger msisdn, final String reference) throws
*
* @param hlrId ID as returned by getRequestHlr in the id variable
* @return Hlr Object
- * @throws GeneralException
- * @throws UnauthorizedException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public Hlr getViewHlr(final String hlrId) throws GeneralException, UnauthorizedException, NotFoundException {
if (hlrId == null) {
@@ -101,8 +193,8 @@ public Hlr getViewHlr(final String hlrId) throws GeneralException, UnauthorizedE
*
* @param message Message object to be send
* @return Message Response
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse sendMessage(final Message message) throws UnauthorizedException, GeneralException {
return messageBirdService.sendPayLoad(MESSAGESPATH, message, MessageResponse.class);
@@ -115,8 +207,8 @@ public MessageResponse sendMessage(final Message message) throws UnauthorizedExc
* @param body Body of the message
* @param recipients List of recipients
* @return MessageResponse
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse sendMessage(final String originator, final String body, final List recipients) throws UnauthorizedException, GeneralException {
return messageBirdService.sendPayLoad(MESSAGESPATH, new Message(originator, body, recipients), MessageResponse.class);
@@ -129,9 +221,9 @@ public MessageResponse sendMessage(final String originator, final String body, f
* @param body Body of the message
* @param recipients List of recipients
* @param reference your reference
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return MessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse sendMessage(final String originator, final String body, final List recipients, final String reference) throws UnauthorizedException, GeneralException {
final Message message = new Message(originator, body, recipients);
@@ -145,9 +237,9 @@ public MessageResponse sendMessage(final String originator, final String body, f
* @param originator Originator of the message, this will get truncated to 11 chars
* @param body Body of the message
* @param recipients List of recipients
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return MessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse sendFlashMessage(final String originator, final String body, final List recipients) throws UnauthorizedException, GeneralException {
final Message message = new Message(originator, body, recipients);
@@ -162,9 +254,9 @@ public MessageResponse sendFlashMessage(final String originator, final String bo
* @param body Body of the message
* @param recipients List of recipients
* @param reference your reference
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return MessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse sendFlashMessage(final String originator, final String body, final List recipients, final String reference) throws UnauthorizedException, GeneralException {
final Message message = new Message(originator, body, recipients);
@@ -174,22 +266,28 @@ public MessageResponse sendFlashMessage(final String originator, final String bo
}
public MessageList listMessages(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException {
- if (offset != null && offset < 0) {
- throw new IllegalArgumentException("Offset must be > 0");
- }
- if (limit != null && limit < 0) {
- throw new IllegalArgumentException("Limit must be > 0");
- }
+ verifyOffsetAndLimit(offset, limit);
return messageBirdService.requestList(MESSAGESPATH, offset, limit, MessageList.class);
}
+ public MessageList listMessagesFiltered(final Integer offset, final Integer limit, final Map filters) throws UnauthorizedException, GeneralException {
+ verifyOffsetAndLimit(offset, limit);
+
+ for (String filter : filters.keySet()) {
+ if (!MESSAGE_LIST_FILTERS.contains(filter)) {
+ throw new IllegalArgumentException("Invalid filter name: " + filter);
+ }
+ }
+
+ return messageBirdService.requestList(MESSAGESPATH, filters, offset, limit, MessageList.class);
+ }
+
/**
* Delete a message from the Messagebird server
*
- * @param id
- * @return void
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public void deleteMessage(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
if (id == null) {
@@ -201,10 +299,10 @@ public void deleteMessage(final String id) throws UnauthorizedException, General
/**
* View a message from the Messagebird server
*
- * @param id
- * @return void
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @return MessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public MessageResponse viewMessage(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
if (id == null) {
@@ -221,11 +319,13 @@ public MessageResponse viewMessage(final String id) throws UnauthorizedException
* Convenient function to send a simple message to a list of recipients
*
* @param voiceMessage Voice message object
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return VoiceMessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public VoiceMessageResponse sendVoiceMessage(final VoiceMessage voiceMessage) throws UnauthorizedException, GeneralException {
+ addDefaultMachineTimeoutValueIfNotExists(voiceMessage);
+ checkMachineTimeoutValueIsInRange(voiceMessage);
return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, voiceMessage, VoiceMessageResponse.class);
}
@@ -234,12 +334,14 @@ public VoiceMessageResponse sendVoiceMessage(final VoiceMessage voiceMessage) th
*
* @param body Body of the message
* @param recipients List of recipients
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return VoiceMessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public VoiceMessageResponse sendVoiceMessage(final String body, final List recipients) throws UnauthorizedException, GeneralException {
final VoiceMessage message = new VoiceMessage(body, recipients);
+ addDefaultMachineTimeoutValueIfNotExists(message);
+ checkMachineTimeoutValueIsInRange(message);
return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, message, VoiceMessageResponse.class);
}
@@ -249,23 +351,36 @@ public VoiceMessageResponse sendVoiceMessage(final String body, final List recipients, final String reference) throws UnauthorizedException, GeneralException {
final VoiceMessage message = new VoiceMessage(body, recipients);
message.setReference(reference);
+ addDefaultMachineTimeoutValueIfNotExists(message);
+ checkMachineTimeoutValueIsInRange(message);
return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, message, VoiceMessageResponse.class);
}
+ private void addDefaultMachineTimeoutValueIfNotExists(final VoiceMessage voiceMessage){
+ if (voiceMessage.getMachineTimeout() == 0){
+ voiceMessage.setMachineTimeout(DEFAULT_MACHINE_TIMEOUT_VALUE); //default machine timeout value
+ }
+ }
+
+ private void checkMachineTimeoutValueIsInRange(final VoiceMessage voiceMessage){
+ if (voiceMessage.getMachineTimeout() < MIN_MACHINE_TIMEOUT_VALUE || voiceMessage.getMachineTimeout() > MAX_MACHINE_TIMEOUT_VALUE){
+ throw new IllegalArgumentException("Please define machine timeout value between " + MIN_MACHINE_TIMEOUT_VALUE + " and " + MAX_MACHINE_TIMEOUT_VALUE);
+ }
+ }
+
/**
* Delete a voice message from the Messagebird server
*
- * @param id
- * @return void
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public void deleteVoiceMessage(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
if (id == null) {
@@ -277,10 +392,10 @@ public void deleteVoiceMessage(final String id) throws UnauthorizedException, Ge
/**
* View a message from the Messagebird server
*
- * @param id
- * @return void
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @return VoiceMessageResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public VoiceMessageResponse viewVoiceMessage(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
if (id == null) {
@@ -292,20 +407,1881 @@ public VoiceMessageResponse viewVoiceMessage(final String id) throws Unauthorize
/**
* List voice messages
*
- * @param offset
- * @param limit
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @param offset offset for result list
+ * @param limit limit for result list
+ * @return VoiceMessageList
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
public VoiceMessageList listVoiceMessages(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException {
- if (offset != null && offset < 0) {
- throw new IllegalArgumentException("Offset must be > 0");
+ verifyOffsetAndLimit(offset, limit);
+ return messageBirdService.requestList(VOICEMESSAGESPATH, offset, limit, VoiceMessageList.class);
+ }
+
+ /**
+ * @param verifyRequest includes recipient, originator, reference, type, datacoding, template, timeout, tokenLenght, voice, language
+ * @return Verify object
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public Verify sendVerifyToken(VerifyRequest verifyRequest) throws UnauthorizedException, GeneralException {
+ if (verifyRequest == null) {
+ throw new IllegalArgumentException("Verify request cannot be null");
+ } else if (verifyRequest.getRecipient() == null || verifyRequest.getRecipient().isEmpty()) {
+ throw new IllegalArgumentException("Recipient cannot be empty for verify");
}
- if (limit != null && limit < 0) {
- throw new IllegalArgumentException("Limit must be > 0");
+ return messageBirdService.sendPayLoad(VERIFYPATH, verifyRequest, Verify.class);
+ }
+
+ /**
+ * @param recipient The telephone number that you want to verify.
+ * @return Verify object
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public Verify sendVerifyToken(String recipient) throws UnauthorizedException, GeneralException {
+ if (recipient == null || recipient.isEmpty()) {
+ throw new IllegalArgumentException("Recipient cannot be empty for verify");
}
- return messageBirdService.requestList(VOICEMESSAGESPATH, offset, limit, VoiceMessageList.class);
+ VerifyRequest verifyRequest = new VerifyRequest(recipient);
+ return this.sendVerifyToken(verifyRequest);
+ }
+
+ /**
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @param token An unique token which was sent to the recipient upon creation of the object.
+ * @return Verify object
+ * @throws NotFoundException if id is not found
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public Verify verifyToken(String id, String token) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null || id.isEmpty()) {
+ throw new IllegalArgumentException("ID cannot be empty for verify");
+ } else if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException("ID cannot be empty for verify");
+ }
+ final Map params = new LinkedHashMap();
+ params.put("token", token);
+ return messageBirdService.requestByID(VERIFYPATH, id, params, Verify.class);
+ }
+
+ /**
+ * @param id id is for getting verify object
+ * @return Verify object
+ * @throws NotFoundException if id is not found
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public Verify getVerifyObject(String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null || id.isEmpty()) {
+ throw new IllegalArgumentException("ID cannot be empty for verify");
+ }
+ return messageBirdService.requestByID(VERIFYPATH, id, Verify.class);
+ }
+
+ /**
+ * @param messageId is for the email message part of a verify object
+ * @return Verify object
+ * @throws NotFoundException if id is not found
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VerifyMessage getVerifyEmailMessage(String messageId) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (messageId == null || messageId.isEmpty()) {
+ throw new IllegalArgumentException("ID cannot be empty for verify email message");
+ }
+ return messageBirdService.requestByID(VERIFYEMAILPATH, messageId, VerifyMessage.class);
+ }
+
+ /**
+ * @param id id for deleting verify object
+ * @throws NotFoundException if id is not found
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public void deleteVerifyObject(String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null || id.isEmpty()) {
+ throw new IllegalArgumentException("ID cannot be empty for verify");
+ }
+ messageBirdService.deleteByID(VERIFYPATH, id);
+ }
+
+ /**
+ * Send a Lookup request
+ *
+ * @param lookup including with country code, country prefix, phone number, type, formats, country code
+ * @return Lookup
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if lookup not found
+ */
+ public Lookup viewLookup(final Lookup lookup) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (lookup.getPhoneNumber() == null) {
+ throw new IllegalArgumentException("PhoneNumber must be specified.");
+ }
+ final Map params = new LinkedHashMap();
+ if (lookup.getCountryCode() != null) {
+ params.put("countryCode", lookup.getCountryCode());
+ }
+ return messageBirdService.requestByID(LOOKUPPATH, String.valueOf(lookup.getPhoneNumber()), params, Lookup.class);
+ }
+
+ /**
+ * Send a Lookup request
+ *
+ * @param phoneNumber phone number is for viewing lookup
+ * @return Lookup
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public Lookup viewLookup(final BigInteger phoneNumber) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (phoneNumber == null) {
+ throw new IllegalArgumentException("PhoneNumber must be specified.");
+ }
+ final Lookup lookup = new Lookup(phoneNumber);
+ return this.viewLookup(lookup);
+ }
+
+ /**
+ * Request a Lookup HLR (lookup)
+ *
+ * @param lookupHlr country code for request lookup hlr
+ * @return lookupHlr
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public LookupHlr requestLookupHlr(final LookupHlr lookupHlr) throws UnauthorizedException, GeneralException {
+ if (lookupHlr.getPhoneNumber() == null) {
+ throw new IllegalArgumentException("PhoneNumber must be specified.");
+ }
+ if (lookupHlr.getReference() == null) {
+ throw new IllegalArgumentException("Reference must be specified.");
+ }
+ final Map payload = new LinkedHashMap();
+ payload.put("phoneNumber", lookupHlr.getPhoneNumber());
+ payload.put("reference", lookupHlr.getReference());
+ if (lookupHlr.getCountryCode() != null) {
+ payload.put("countryCode", lookupHlr.getCountryCode());
+ }
+ return messageBirdService.sendPayLoad(String.format(LOOKUPHLRPATH, lookupHlr.getPhoneNumber()), payload, LookupHlr.class);
+ }
+
+ /**
+ * Request a Lookup HLR (lookup)
+ *
+ * @param phoneNumber phone number is for request hlr
+ * @param reference reference for request hlr
+ * @return lookupHlr
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public LookupHlr requestLookupHlr(final BigInteger phoneNumber, final String reference) throws UnauthorizedException, GeneralException {
+ if (phoneNumber == null) {
+ throw new IllegalArgumentException("Phonenumber must be specified.");
+ }
+ if (reference == null) {
+ throw new IllegalArgumentException("Reference must be specified.");
+ }
+ final LookupHlr lookupHlr = new LookupHlr();
+ lookupHlr.setPhoneNumber(phoneNumber);
+ lookupHlr.setReference(reference);
+ return this.requestLookupHlr(lookupHlr);
+ }
+
+ /**
+ * View a Lookup HLR (lookup)
+ *
+ * @param lookupHlr search with country code
+ * @return LookupHlr
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if there is no lookup hlr with given country code
+ */
+ public LookupHlr viewLookupHlr(final LookupHlr lookupHlr) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (lookupHlr.getPhoneNumber() == null) {
+ throw new IllegalArgumentException("Phonenumber must be specified");
+ }
+ final Map params = new LinkedHashMap();
+ if (lookupHlr.getCountryCode() != null) {
+ params.put("countryCode", lookupHlr.getCountryCode());
+ }
+ return messageBirdService.requestByID(String.format(LOOKUPHLRPATH, lookupHlr.getPhoneNumber()), "", params, LookupHlr.class);
+ }
+
+ /**
+ * View a Lookup HLR (lookup)
+ *
+ * @param phoneNumber phone number for searching on hlr
+ * @return LookupHlr
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException phone number not found on hlr
+ */
+ public LookupHlr viewLookupHlr(final BigInteger phoneNumber) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (phoneNumber == null) {
+ throw new IllegalArgumentException("phoneNumber must be specified");
+ }
+ final LookupHlr lookupHlr = new LookupHlr();
+ lookupHlr.setPhoneNumber(phoneNumber);
+ return this.viewLookupHlr(lookupHlr);
+ }
+
+ /**
+ * Convenient function to list all call flows
+ *
+ * @param offset
+ * @param limit
+ * @return VoiceCallFlowList
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallFlowList listVoiceCallFlows(final Integer offset, final Integer limit)
+ throws UnauthorizedException, GeneralException {
+ verifyOffsetAndLimit(offset, limit);
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH);
+
+ return messageBirdService.requestList(url, offset, limit, VoiceCallFlowList.class);
+ }
+
+ /**
+ * Retrieves the information of an existing Call Flow. You only need to supply
+ * the unique call flow ID that was returned upon creation or receiving.
+ * @param id String
+ * @return VoiceCallFlowResponse
+ * @throws NotFoundException
+ * @throws GeneralException
+ * @throws UnauthorizedException
+ */
+ public VoiceCallFlowResponse viewVoiceCallFlow(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Call Flow ID must be specified.");
+ }
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH);
+
+ return messageBirdService.requestByID(url, id, VoiceCallFlowResponse.class);
+ }
+
+ /**
+ * Convenient function to create a call flow
+ *
+ * @param voiceCallFlowRequest VoiceCallFlowRequest
+ * @return VoiceCallFlowResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallFlowResponse sendVoiceCallFlow(final VoiceCallFlowRequest voiceCallFlowRequest)
+ throws UnauthorizedException, GeneralException {
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH);
+
+ return messageBirdService.sendPayLoad(url, voiceCallFlowRequest, VoiceCallFlowResponse.class);
+ }
+
+ /**
+ * Updates an existing Call Flow. You only need to supply the unique id that
+ * was returned upon creation.
+ * @param id String
+ * @param voiceCallFlowRequest VoiceCallFlowRequest
+ * @return VoiceCallFlowResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallFlowResponse updateVoiceCallFlow(String id, VoiceCallFlowRequest voiceCallFlowRequest)
+ throws UnauthorizedException, GeneralException {
+ if (id == null) {
+ throw new IllegalArgumentException("Call Flow ID must be specified.");
+ }
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH);
+ String request = url + "/" + id;
+
+ return messageBirdService.sendPayLoad("PUT", request, voiceCallFlowRequest, VoiceCallFlowResponse.class);
+ }
+
+ /**
+ * Convenient function to delete call flow
+ *
+ * @param id String
+ * @return void
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public void deleteVoiceCallFlow(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Voice Call Flow ID must be specified.");
+ }
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH);
+ messageBirdService.deleteByID(url, id);
+ }
+
+ /**
+ * Deletes an existing contact. You only need to supply the unique id that
+ * was returned upon creation.
+ */
+ void deleteContact(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Contact ID must be specified.");
+ }
+ messageBirdService.deleteByID(CONTACTPATH, id);
+ }
+
+ /**
+ * Gets a contact listing with specified pagination options.
+ *
+ * @return Listing of Contact objects.
+ */
+ public ContactList listContacts(int offset, int limit) throws UnauthorizedException, GeneralException {
+ return messageBirdService.requestList(CONTACTPATH, offset, limit, ContactList.class);
+ }
+
+ /**
+ * Gets a contact listing with default pagination options.
+ */
+ public ContactList listContacts() throws UnauthorizedException, GeneralException {
+ final int limit = 20;
+ final int offset = 0;
+
+ return listContacts(offset, limit);
+ }
+
+ /**
+ * Creates a new contact object. MessageBird returns the created contact
+ * object with each request.
+ */
+ public Contact sendContact(final ContactRequest contactRequest) throws UnauthorizedException, GeneralException {
+ return messageBirdService.sendPayLoad(CONTACTPATH, contactRequest, Contact.class);
+ }
+
+ /**
+ * Updates an existing contact. You only need to supply the unique id that
+ * was returned upon creation.
+ */
+ public Contact updateContact(final String id, ContactRequest contactRequest) throws UnauthorizedException, GeneralException {
+ if (id == null) {
+ throw new IllegalArgumentException("Contact ID must be specified.");
+ }
+ String request = CONTACTPATH + "/" + id;
+ return messageBirdService.sendPayLoad("PATCH", request, contactRequest, Contact.class);
+ }
+
+ /**
+ * Retrieves the information of an existing contact. You only need to supply
+ * the unique contact ID that was returned upon creation or receiving.
+ */
+ public Contact viewContact(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Contact ID must be specified.");
+ }
+ return messageBirdService.requestByID(CONTACTPATH, id, Contact.class);
+ }
+
+ /**
+ * Deletes an existing group. You only need to supply the unique id that
+ * was returned upon creation.
+ */
+ public void deleteGroup(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Group ID must be specified.");
+ }
+ messageBirdService.deleteByID(GROUPPATH, id);
+ }
+
+ /**
+ * Removes a contact from group. You need to supply the IDs of the group
+ * and contact. Does not delete the contact.
+ */
+ public void deleteGroupContact(final String groupId, final String contactId) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (groupId == null) {
+ throw new IllegalArgumentException("Group ID must be specified.");
+ }
+ if (contactId == null) {
+ throw new IllegalArgumentException("Contact ID must be specified.");
+ }
+
+ String id = String.format("%s%s/%s", groupId, CONTACTPATH, contactId);
+ messageBirdService.deleteByID(GROUPPATH, id);
+ }
+
+ /**
+ * Gets a contact listing with specified pagination options.
+ */
+ public GroupList listGroups(final int offset, final int limit) throws UnauthorizedException, GeneralException {
+ return messageBirdService.requestList(GROUPPATH, offset, limit, GroupList.class);
+ }
+
+ /**
+ * Gets a contact listing with default pagination options.
+ */
+ public GroupList listGroups() throws UnauthorizedException, GeneralException {
+ final int offset = 0;
+ final int limit = 20;
+
+ return listGroups(offset, limit);
+ }
+
+ /**
+ * Creates a new group object. MessageBird returns the created group object
+ * with each request.
+ */
+ public Group sendGroup(final GroupRequest groupRequest) throws UnauthorizedException, GeneralException {
+ return messageBirdService.sendPayLoad(GROUPPATH, groupRequest, Group.class);
+ }
+
+ /**
+ * Adds contact to group. You need to supply the IDs of the group and
+ * contact.
+ */
+ public void sendGroupContact(final String groupId, final String[] contactIds) throws NotFoundException, GeneralException, UnauthorizedException {
+ // reuestByID appends the "ID" to the base path, so this workaround
+ // lets us add a query string.
+ String path = String.format("%s%s?%s", groupId, CONTACTPATH, getQueryString(contactIds));
+ messageBirdService.requestByID(GROUPPATH, path, null);
+ }
+
+ /**
+ * Builds a query string to add contacts to a group. We're using the
+ * alternative "/foo?_method=PUT&ids[]=foo&ids[]=bar" format to send the
+ * contact IDs as GET params. Sending these in the request body would
+ * require a painful workaround, as sendPayload sends request bodies as
+ * JSON by default. See also:
+ * https://developers.messagebird.com/docs/alternatives.
+ */
+ private String getQueryString(final String[] contactIds) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("_method=PUT");
+
+ for (String groupId : contactIds) {
+ stringBuilder.append("&ids[]=").append(groupId);
+ }
+
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Updates an existing group. You only need to supply the unique ID that
+ * was returned upon creation.
+ */
+ public Group updateGroup(final String id, final GroupRequest groupRequest) throws UnauthorizedException, GeneralException {
+ if (id == null) {
+ throw new IllegalArgumentException("Group ID must be specified.");
+ }
+ String path = String.format("%s/%s", GROUPPATH, id);
+ return messageBirdService.sendPayLoad("PATCH", path, groupRequest, Group.class);
}
-}
+ /**
+ * Retrieves the information of an existing group. You only need to supply
+ * the unique group ID that was returned upon creation or receiving.
+ */
+ public Group viewGroup(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Group ID must be specified.");
+ }
+ return messageBirdService.requestByID(GROUPPATH, id, Group.class);
+ }
+
+ /**
+ * Gets a single conversation.
+ *
+ * @param id Conversation to retrieved.
+ * @return The retrieved conversation.
+ */
+ public Conversation viewConversation(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Id must be specified");
+ }
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_PATH;
+ return messageBirdService.requestByID(url, id, Conversation.class);
+ }
+
+ /**
+ * Updates a conversation.
+ *
+ * @param id Conversation to update.
+ * @param status New status for the conversation.
+ * @return The updated Conversation.
+ */
+ public Conversation updateConversation(final String id, final ConversationStatus status)
+ throws UnauthorizedException, GeneralException {
+ if (id == null) {
+ throw new IllegalArgumentException("Id must be specified.");
+ }
+ if (status == null) {
+ throw new IllegalArgumentException("An updated conversation status must be specified");
+ }
+ ConversationUpdateRequest payload = new ConversationUpdateRequest(status);
+ String url = String.format("%s%s/%s", CONVERSATIONS_BASE_URL, CONVERSATION_PATH, id);
+ return messageBirdService.sendPayLoad("PATCH", url, payload, Conversation.class);
+ }
+
+ /**
+ * Gets a Conversation listing with specified pagination options.
+ *
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to take.
+ * @return List of conversations.
+ */
+ public ConversationList listConversations(final int offset, final int limit)
+ throws UnauthorizedException, GeneralException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_PATH;
+ return messageBirdService.requestList(url, offset, limit, ConversationList.class);
+ }
+
+ /**
+ * Gets a contact listing with default pagination options.
+ *
+ * @return List of conversations.
+ */
+ public ConversationList listConversations() throws UnauthorizedException, GeneralException {
+ final int offset = 0;
+ final int limit = 10;
+
+ return listConversations(offset, limit);
+ }
+
+ /**
+ * Starts a conversation by sending an initial message.
+ *
+ * @param request Data for this request.
+ * @return The created Conversation.
+ */
+ public Conversation startConversation(ConversationStartRequest request)
+ throws UnauthorizedException, GeneralException {
+ String url = String.format("%s%s/start", CONVERSATIONS_BASE_URL, CONVERSATION_PATH);
+ return messageBirdService.sendPayLoad(url, request, Conversation.class);
+ }
+
+ /**
+ * sendMessage allows you to send message to users over any communication platform supported by Programmable Conversations
+ *
+ * @param request Data for this request.
+ * @return The created Message in ConversationSendResponse object.
+ */
+ public ConversationSendResponse sendMessage(ConversationSendRequest request)
+ throws UnauthorizedException, GeneralException {
+ String url = String.format("%s%s", CONVERSATIONS_BASE_URL, CONVERSATION_SEND_PATH);
+ return messageBirdService.sendPayLoad(url, request, ConversationSendResponse.class);
+ }
+
+ /**
+ * Gets a ConversationMessage listing with specified pagination options.
+ *
+ * @param conversationId Conversation to get messages for.
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to take.
+ * @return List of messages.
+ */
+ public ConversationMessageList listConversationMessages(
+ final String conversationId,
+ final int offset,
+ final int limit
+ ) throws UnauthorizedException, GeneralException {
+ String url = String.format(
+ "%s%s/%s%s",
+ CONVERSATIONS_BASE_URL,
+ CONVERSATION_PATH,
+ conversationId,
+ CONVERSATION_MESSAGE_PATH
+ );
+ return messageBirdService.requestList(url, offset, limit, ConversationMessageList.class);
+ }
+
+ /**
+ * Gets a ConversationMessage listing with default pagination options.
+ *
+ * @param conversationId Conversation to get messages for.
+ * @return List of messages.
+ */
+ public ConversationMessageList listConversationMessages(
+ final String conversationId
+ ) throws UnauthorizedException, GeneralException {
+ final int offset = 0;
+ final int limit = 10;
+
+ return listConversationMessages(conversationId, offset, limit);
+ }
+
+ /**
+ * Gets a single message.
+ *
+ * @param messageId Message to retrieve.
+ * @return The retrieved message.
+ */
+ public ConversationMessage viewConversationMessage(final String messageId)
+ throws NotFoundException, GeneralException, UnauthorizedException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_MESSAGE_PATH;
+ return messageBirdService.requestByID(url, messageId, ConversationMessage.class);
+ }
+
+ /**
+ * Gets conversation messages based on query param.
+ *
+ * @param queryParams only `ids` and `from` is available as an option
+ * @return The retrieved messages.
+ */
+ public ConversationMessageList listConversationMessagesWithQueryParam(Map queryParams)
+ throws NotFoundException, GeneralException, UnauthorizedException {
+ for (String queryParam : queryParams.keySet()) {
+ if (!CONVERSATION_MESSAGE_LIST_FILTERS.contains(queryParam)) {
+ throw new IllegalArgumentException("Invalid filter name: " + queryParam);
+ }
+ }
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_MESSAGE_PATH;
+ return messageBirdService.requestByID(url, null, queryParams, ConversationMessageList.class);
+ }
+
+ /**
+ * Sends a message to an existing Conversation.
+ *
+ * @param conversationId Conversation to send message to.
+ * @param request Message to send.
+ * @return The newly created message.
+ */
+ public ConversationMessage sendConversationMessage(
+ final String conversationId,
+ final ConversationMessageRequest request
+ ) throws UnauthorizedException, GeneralException {
+ String url = String.format(
+ "%s%s/%s%s",
+ CONVERSATIONS_BASE_URL,
+ CONVERSATION_PATH,
+ conversationId,
+ CONVERSATION_MESSAGE_PATH
+ );
+ return messageBirdService.sendPayLoad(url, request, ConversationMessage.class);
+ }
+
+ /**
+ * Deletes a webhook.
+ *
+ * @param webhookId Webhook to delete.
+ */
+ public void deleteConversationWebhook(final String webhookId)
+ throws NotFoundException, GeneralException, UnauthorizedException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH;
+ messageBirdService.deleteByID(url, webhookId);
+ }
+
+ /**
+ * Creates a new webhook.
+ *
+ * @param request Webhook to create.
+ * @return Newly created webhook.
+ */
+ public ConversationWebhook sendConversationWebhook(final ConversationWebhookCreateRequest request)
+ throws UnauthorizedException, GeneralException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH;
+ return messageBirdService.sendPayLoad(url, request, ConversationWebhook.class);
+ }
+
+ /**
+ * Update an existing webhook.
+ *
+ * @param request update request.
+ * @return Updated webhook.
+ */
+ public ConversationWebhook updateConversationWebhook(final String id, final ConversationWebhookUpdateRequest request) throws UnauthorizedException, GeneralException {
+ if (id == null || id.isEmpty()) {
+ throw new IllegalArgumentException("Conversation webhook ID must be specified.");
+ }
+
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH + "/" + id;
+ return messageBirdService.sendPayLoad("PATCH", url, request, ConversationWebhook.class);
+ }
+
+ /**
+ * Gets a single webhook.
+ *
+ * @param webhookId Webhook to retrieve.
+ * @return The retrieved webhook.
+ */
+ public ConversationWebhook viewConversationWebhook(final String webhookId) throws NotFoundException, GeneralException, UnauthorizedException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH;
+ return messageBirdService.requestByID(url, webhookId, ConversationWebhook.class);
+ }
+
+ /**
+ * Gets a ConversationWebhook listing with the specified pagination options.
+ *
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to skip.
+ * @return List of webhooks.
+ */
+ public ConversationWebhookList listConversationWebhooks(final int offset, final int limit)
+ throws UnauthorizedException, GeneralException {
+ String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH;
+ return messageBirdService.requestList(url, offset, limit, ConversationWebhookList.class);
+ }
+
+ /**
+ * Gets a ConversationWebhook listing with default pagination options.
+ *
+ * @return List of webhooks.
+ */
+ public ConversationWebhookList listConversationWebhooks() throws UnauthorizedException, GeneralException {
+ final int offset = 0;
+ final int limit = 10;
+
+ return listConversationWebhooks(offset, limit);
+ }
+
+ /****************************************************************************************************/
+ /** Voice Calling
+ /****************************************************************************************************/
+
+ /**
+ * Function for voice call to a number
+ *
+ * @param voiceCall Voice call object
+ * @return VoiceCallResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallResponse sendVoiceCall(final VoiceCall voiceCall) throws UnauthorizedException, GeneralException {
+ if (voiceCall.getSource() == null) {
+ throw new IllegalArgumentException("Source of voice call must be specified.");
+ }
+ if (voiceCall.getDestination() == null) {
+ throw new IllegalArgumentException("Destination of voice call must be specified.");
+ }
+ if (voiceCall.getCallFlow() == null) {
+ throw new IllegalArgumentException("Call flow of voice call must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH);
+
+ return messageBirdService.sendPayLoad(url, voiceCall, VoiceCallResponse.class);
+ }
+
+ /**
+ * Function to list all voice calls
+ *
+ * @return VoiceCallResponseList
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallResponseList listAllVoiceCalls(Integer page, Integer pageSize) throws GeneralException, UnauthorizedException {
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH);
+ return messageBirdService.requestList(url, new PagedPaging(page, pageSize), VoiceCallResponseList.class);
+ }
+
+ /**
+ * Function to view voice call by id
+ *
+ * @param id Voice call ID
+ * @return VoiceCallResponse
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallResponse viewVoiceCall(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Voice Message ID must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH);
+ return messageBirdService.requestByID(url, id, VoiceCallResponse.class);
+ }
+
+ /**
+ * Function to delete voice call by id
+ *
+ * @param id Voice call ID
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public void deleteVoiceCall(final String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Voice Message ID must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH);
+ messageBirdService.deleteByID(url, id);
+ }
+
+ /**
+ * Retrieves a listing of all legs.
+ *
+ * @param callId Voice call ID
+ * @param page page to fetch (can be null - will return first page), number of first page is 1
+ * @param pageSize page size
+ * @return VoiceCallLegResponse
+ * @throws UnsupportedEncodingException no UTF8 supported url
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallLegResponse viewCallLegsByCallId(String callId, Integer page, Integer pageSize) throws UnsupportedEncodingException, UnauthorizedException, GeneralException {
+ if (callId == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ urlEncode(callId),
+ VOICELEGS_SUFFIX_PATH);
+
+ return messageBirdService.requestList(url, new PagedPaging(page, pageSize), VoiceCallLegResponse.class);
+ }
+
+ /**
+ * Retrieves a leg resource.
+ * The parameters are the unique ID of the call and of the leg that were returned upon their respective creation.
+ *
+ * @param callId Voice call ID
+ * @param legId ID of leg of specified call {callId}
+ * @return VoiceCallLeg
+ * @throws UnsupportedEncodingException no UTF8 supported url
+ * @throws NotFoundException not found with callId and legId
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public VoiceCallLeg viewCallLegByCallIdAndLegId(final String callId, String legId) throws UnsupportedEncodingException, NotFoundException, GeneralException, UnauthorizedException {
+ if (callId == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ urlEncode(callId),
+ VOICELEGS_SUFFIX_PATH);
+
+ VoiceCallLegResponse response = messageBirdService.requestByID(url, legId, VoiceCallLegResponse.class);
+
+ if (response.getData().size() == 1) {
+ return response.getData().get(0);
+ } else {
+ throw new NotFoundException("No such leg", new LinkedList());
+ }
+ }
+
+ private String urlEncode(String s) throws UnsupportedEncodingException {
+ return URLEncoder.encode(s, String.valueOf(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Function to view recording by call id , leg id and recording id
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @return Recording
+ * @throws NotFoundException not found with callId and legId
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public RecordingResponse viewRecording(String callID, String legId, String recordingId) throws NotFoundException, GeneralException, UnauthorizedException {
+
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH);
+ Map params = new LinkedHashMap<>();
+ params.put("legs", legId);
+ params.put("recordings", recordingId);
+
+ return messageBirdService.requestByID(url, callID, params, RecordingResponse.class);
+ }
+
+ /**
+ * Downloads the record in .wav format by using callId, legId and recordId and stores to basePath. basePath is not mandatory to set.
+ * If basePath is not set, default download will be the /Download folder in user group.
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @param basePath store location. It should be directory. Property is Optional if $HOME is accessible
+ * @return the path that file is stored
+ * @throws NotFoundException if the recording does not found
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ */
+ public String downloadRecording(String callID, String legId, String recordingId, String basePath) throws NotFoundException, GeneralException, UnauthorizedException {
+
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH,
+ recordingId,
+ RECORDING_DOWNLOAD_FORMAT
+ );
+ String fileName = String.format("%s%s",recordingId, RECORDING_DOWNLOAD_FORMAT);
+ return messageBirdService.getBinaryData(url, basePath, fileName);
+ }
+
+ /**
+ * List the all recordings related to CallID and LegId
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to take.
+ * @return Recordings for CallID and LegID
+ * @throws GeneralException if client is unauthorized
+ * @throws UnauthorizedException general exception
+ */
+ public RecordingResponse listRecordings(String callID, String legId, final Integer offset, final Integer limit)
+ throws GeneralException, UnauthorizedException {
+ verifyOffsetAndLimit(offset, limit);
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH);
+
+ return messageBirdService.requestList(url, offset, limit, RecordingResponse.class);
+ }
+
+ /**
+ * Deletes a voice recording
+ *
+ * @param callID Voice call ID
+ * @param legID Leg ID
+ * @param recordingID Recording ID
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public void deleteRecording(final String callID, final String legID, final String recordingID)
+ throws NotFoundException, GeneralException, UnauthorizedException {
+ if (callID == null) {
+ throw new IllegalArgumentException("Call ID must be specified.");
+ }
+ if (legID == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+ if (recordingID == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+ String url = String.format(
+ "%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legID,
+ RECORDINGPATH
+ );
+ messageBirdService.deleteByID(url, recordingID);
+ }
+
+ /**
+ * Function to view recording by call id , leg id and recording id
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @param language Language
+ * @return TranscriptionResponseList
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public TranscriptionResponse createTranscription(String callID, String legId, String recordingId, String language)
+ throws UnauthorizedException, GeneralException {
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ if (language == null) {
+ throw new IllegalArgumentException("Language must be specified.");
+ }
+
+ if (!Arrays.asList(supportedLanguages).contains(language)) {
+ throw new IllegalArgumentException("Your language is not allowed.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH,
+ recordingId,
+ TRANSCRIPTIONPATH);
+
+ return messageBirdService.sendPayLoad(url, language, TranscriptionResponse.class);
+ }
+
+ /**
+ * Function to view recording by call id, leg id and recording id
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @param transcriptionId Transcription ID
+ * @return Transcription
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException If transcription is not found
+ */
+ public TranscriptionResponse viewTranscription(String callID, String legId, String recordingId, String transcriptionId) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ if(transcriptionId == null) {
+ throw new IllegalArgumentException("Transcription ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH,
+ recordingId,
+ TRANSCRIPTIONPATH);
+
+ return messageBirdService.requestByID(url, transcriptionId, TranscriptionResponse.class);
+ }
+
+ /**
+ * Lists the Transcription of callId, legId and recordId
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @param page page to fetch (can be null - will return first page), number of first page is 1
+ * @param pageSize page size
+ * @return List of Transcription
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public TranscriptionResponse listTranscriptions(String callID, String legId, String recordingId, Integer page, Integer pageSize) throws UnauthorizedException, GeneralException {
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s/%s%s/%s%s/%s%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH,
+ recordingId,
+ TRANSCRIPTIONPATH);
+
+ return messageBirdService.requestList(url, new PagedPaging(page, pageSize), TranscriptionResponse.class);
+ }
+
+ /**
+ * Downloads the transcription in .txt format by using callId, legId, recordId and transcriptionId and stores to basePath. basePath is not mandatory to set.
+ * If basePath is not set, default download will be the /Download folder in user group.
+ *
+ * @param callID Voice call ID
+ * @param legId Leg ID
+ * @param recordingId Recording ID
+ * @param transcriptionId Transcription ID
+ * @param basePath store location. It should be directory. Property is Optional if $HOME is accessible
+ * @return the path that file is stored
+ * @throws NotFoundException if the recording does not found
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ */
+ public String downloadTranscription(String callID, String legId, String recordingId, String transcriptionId, String basePath)
+ throws UnauthorizedException, GeneralException, NotFoundException {
+ if (callID == null) {
+ throw new IllegalArgumentException("Voice call ID must be specified.");
+ }
+
+ if (legId == null) {
+ throw new IllegalArgumentException("Leg ID must be specified.");
+ }
+
+ if (recordingId == null) {
+ throw new IllegalArgumentException("Recording ID must be specified.");
+ }
+
+ if (transcriptionId == null) {
+ throw new IllegalArgumentException("Transcription ID must be specified.");
+ }
+ String fileName = String.format("%s%s", transcriptionId, TRANSCRIPTION_DOWNLOAD_FORMAT);
+ String url = String.format(
+ "%s%s/%s%s/%s%s/%s%s/%s",
+ VOICE_CALLS_BASE_URL,
+ VOICECALLSPATH,
+ callID,
+ LEGSPATH,
+ legId,
+ RECORDINGPATH,
+ recordingId,
+ TRANSCRIPTIONPATH,
+ fileName);
+
+ return messageBirdService.getBinaryData(url, basePath, fileName);
+ }
+
+ /**
+ * Function to create a webhook
+ *
+ * @param webhook webhook to create
+ * @return WebhookResponseData created webhook
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public WebhookResponseData createWebhook(Webhook webhook) throws UnauthorizedException, GeneralException {
+ if (webhook.getUrl() == null) {
+ throw new IllegalArgumentException("URL of webhook must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS);
+ return messageBirdService.sendPayLoad(url, webhook, WebhookResponseData.class);
+ }
+
+ /**
+ * Function to update a webhook
+ *
+ * @param webhook webhook fields to update
+ * @return WebhookResponseData updated webhook
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public WebhookResponseData updateWebhook(String id, Webhook webhook) throws UnauthorizedException, GeneralException {
+ if (id == null) {
+ throw new IllegalArgumentException("Id of webhook must be specified.");
+ }
+
+ String url = String.format("%s%s/%s", VOICE_CALLS_BASE_URL, WEBHOOKS, id);
+ return messageBirdService.sendPayLoad("PUT", url, webhook, WebhookResponseData.class);
+ }
+
+ /**
+ * Function to view a webhook
+ *
+ * @param id id of a webhook
+ * @return WebhookResponseData
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public WebhookResponseData viewWebhook(String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Id of webhook must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS);
+ return messageBirdService.requestByID(url, id, WebhookResponseData.class);
+ }
+
+ /**
+ * Function to list webhooks
+ *
+ * @param offset offset for result list
+ * @param limit limit for result list
+ * @return WebhookList
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public WebhookList listWebhooks(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException {
+ verifyOffsetAndLimit(offset, limit);
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS);
+ return messageBirdService.requestList(url, offset, limit, WebhookList.class);
+ }
+
+ /**
+ * Function to delete a webhook
+ *
+ * @param id A unique random ID which is created on the MessageBird platform
+ * @throws NotFoundException if id is not found
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ */
+ public void deleteWebhook(String id) throws NotFoundException, GeneralException, UnauthorizedException {
+ if (id == null) {
+ throw new IllegalArgumentException("Webhook ID must be specified.");
+ }
+
+ String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS);
+ messageBirdService.deleteByID(url, id);
+ }
+
+ private void verifyOffsetAndLimit(Integer offset, Integer limit) {
+ if (offset != null && offset < 0) {
+ throw new IllegalArgumentException("Offset must be > 0");
+ }
+ if (limit != null && limit < 0) {
+ throw new IllegalArgumentException("Limit must be > 0");
+ }
+ }
+
+ /**
+ * Checks whether a particular country code is a recognized ISO Country.
+ *
+ * @param countryCode The country code in which the Number should be purchased.
+ * @throws IllegalArgumentException for invalid country code
+ */
+ private void countryCodeIsValid(String countryCode) throws IllegalArgumentException {
+ final boolean isValid = Arrays.asList(Locale.getISOCountries()).contains(countryCode);
+ if (!isValid) {
+ throw new IllegalArgumentException("Invalid Country Code Provided.");
+ }
+ }
+
+ /**
+ * Lists Numbers that are available to purchase in a particular country code, without any filters.
+ *
+ * @param countryCode The country code in which the Number should be purchased.
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws NotFoundException if the resource is missing
+ * @throws IllegalArgumentException if the country code provided is invalid
+ */
+ public PhoneNumbersResponse listNumbersForPurchase(String countryCode) throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ countryCodeIsValid(countryCode);
+ final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL);
+ return messageBirdService.requestByID(url, countryCode, PhoneNumbersResponse.class);
+ }
+
+ /**
+ * Lists Numbers that are available to purchase in a particular country code, according to specified search criteria.
+ *
+ * @param countryCode The country code in which the Number should be purchased.
+ * @param params Parameters to filter the resulting phone numbers returned.
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws NotFoundException if the resource is missing
+ * @throws IllegalArgumentException if the country code provided is invalid
+ */
+ public PhoneNumbersResponse listNumbersForPurchase(String countryCode, PhoneNumbersLookup params) throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ countryCodeIsValid(countryCode);
+ final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL);
+ return messageBirdService.requestByID(url, countryCode, params.toHashMap(), PhoneNumbersResponse.class);
+ }
+
+ /**
+ * Purchases a phone number. To be used in conjunction with listNumbersForPurchase to identify available numbers.
+ *
+ * @param number The number to purchase.
+ * @param countryCode The country code in which the Number should be purchased.
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws IllegalArgumentException if the country code provided is invalid
+ */
+ public PurchasedNumberCreatedResponse purchaseNumber(String number, String countryCode, int billingIntervalMonths) throws UnauthorizedException, GeneralException, IllegalArgumentException {
+ countryCodeIsValid(countryCode);
+ final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL);
+ final Map payload = new LinkedHashMap();
+ payload.put("number", number);
+ payload.put("countryCode", countryCode);
+ if (!Arrays.asList(1, 3, 6, 9).contains(billingIntervalMonths)) {
+ throw new IllegalArgumentException("Billing Interval Must Be Either 1, 3, 6, or 9.");
+ }
+ payload.put("billingIntervalMonths", billingIntervalMonths);
+
+ return messageBirdService.sendPayLoad(url, payload, PurchasedNumberCreatedResponse.class);
+ }
+
+ /**
+ * Lists Numbers that were purchased using the account credentials that the client was initialized with.
+ *
+ * @param filter Filters the list of purchased numbers according to search criteria.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if the resource is missing
+ */
+ public PurchasedNumbersResponse listPurchasedNumbers(PurchasedNumbersFilter filter) throws UnauthorizedException, GeneralException, NotFoundException {
+ final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL);
+ return messageBirdService.requestByID(url, null, filter.toHashMap(), PurchasedNumbersResponse.class);
+ }
+
+ /**
+ * Returns a Number that has already been purchased on the initialized account.
+ *
+ * @param number The number whose data should be returned.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if the Number is missing
+ */
+ public PurchasedNumber viewPurchasedNumber(String number) throws UnauthorizedException, GeneralException, NotFoundException {
+ final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL);
+ return messageBirdService.requestByID(url, number, PurchasedNumber.class);
+ }
+
+ /**
+ * Updates tags on a particular existing Number. Any number of parameters after the number can be given to apply multiple tags.
+ *
+ * @param number The number to update.
+ * @param tags A tag to apply to the number.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public PurchasedNumber updateNumber(String number, String... tags) throws UnauthorizedException, GeneralException {
+ final String url = String.format("%s/phone-numbers/%s", NUMBERS_CALLS_BASE_URL, number);
+ final Map> payload = new HashMap>();
+ payload.put("tags", Arrays.asList(tags));
+ return messageBirdService.sendPayLoad("PATCH", url, payload, PurchasedNumber.class);
+ }
+
+ /**
+ * Cancels a particular number.
+ *
+ * @param number The number to cancel.
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws NotFoundException if the resource is missing
+ */
+ public void cancelNumber(String number) throws UnauthorizedException, GeneralException, NotFoundException {
+ final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL);
+ messageBirdService.deleteByID(url, number);
+ }
+
+ /**
+ * Uploads a file and returns the assigned ID.
+ *
+ * @param binary the bytes of the file to upload.
+ * @param contentType the content type of the file (e.g. "image/png").
+ * @param filename optional filename to set in the upload request headers.
+ * @return FileUploadResponse
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @see #downloadFile
+ */
+ public FileUploadResponse uploadFile(byte[] binary, String contentType, String filename) throws GeneralException, UnauthorizedException {
+ if (binary == null) {
+ throw new IllegalArgumentException("File binary must be specified.");
+ }
+ if (contentType == null) {
+ throw new IllegalArgumentException("Content type must be specified.");
+ }
+
+ final String url = MESSAGING_BASE_URL + FILES_PATH;
+ final Map headers = new HashMap<>();
+ headers.put("Content-Type", contentType);
+ if (filename != null) {
+ headers.put("filename", filename);
+ }
+ return messageBirdService.sendPayLoad("POST", url, headers, binary, FileUploadResponse.class);
+ }
+
+ /**
+ * Downloads a file and stores it with the provided filename in the basePath directory. The
+ * basePath may be null. If basePath is null, the default download directory will be the
+ * /Download folder in the user home directory. The filename may be null. If filename is null,
+ * the provided id will be used as the filename instead.
+ *
+ * @param id the ID of the file, provided when the file was uploaded
+ * @param basePath store location. It should be a directory. Property is nullable if $HOME is accessible
+ * @param filename the name of the file to download to.
+ * @return the path where the downloaded file is stored
+ * @throws NotFoundException if the file does not exist
+ * @throws GeneralException general exception
+ * @throws UnauthorizedException if client is unauthorized
+ * @see #uploadFile
+ */
+ public String downloadFile(String id, String filename, String basePath) throws GeneralException, UnauthorizedException, NotFoundException {
+ if (id == null) {
+ throw new IllegalArgumentException("File ID must be specified.");
+ }
+
+ if (filename == null) {
+ filename = id;
+ }
+
+ final String url = String.format("%s%s/%s", MESSAGING_BASE_URL, FILES_PATH, id);
+ return messageBirdService.getBinaryData(url, basePath, filename);
+ }
+
+ /****************************************************************************************************/
+ /** WhatsApp Templates **/
+ /****************************************************************************************************/
+
+ /**
+ * Create a WhatsApp message template through messagebird.
+ *
+ * @param template {@link Template} object to be created
+ * @return {@link TemplateResponse} response object
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws IllegalArgumentException invalid template format
+ */
+ public TemplateResponse createWhatsAppTemplate(final Template template)
+ throws UnauthorizedException, GeneralException, IllegalArgumentException {
+ template.validate();
+
+ String url = String.format(
+ "%s%s%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH
+ );
+ return messageBirdService.sendPayLoad(url, template, TemplateResponse.class);
+ }
+
+ /**
+ * Update a WhatsApp message template through MessageBird.
+ *
+ * @param template {@link Template} object to be created
+ * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable
+ * @param language A language code as returned by getWhatsAppTemplateBy in the language variable
+ * @return {@link TemplateResponse} response object
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws IllegalArgumentException invalid template format
+ */
+ public TemplateResponse updateWhatsAppTemplate(final Template template, final String templateName, final String language)
+ throws UnauthorizedException, GeneralException, IllegalArgumentException {
+ template.validate();
+
+ String url = String.format(
+ "%s%s%s/%s/%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH,
+ templateName,
+ language);
+
+ return messageBirdService.sendPayLoad("PUT",url, template, TemplateResponse.class);
+ }
+ /**
+ * Gets a WhatsAppTemplate listing with specified pagination options.
+ *
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to take.
+ * @return List of templates.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public TemplateList listWhatsAppTemplates(final int offset, final int limit)
+ throws UnauthorizedException, GeneralException {
+ String url = String.format(
+ "%s%s%s",
+ INTEGRATIONS_BASE_URL_V3,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH
+ );
+ return messageBirdService.requestList(url, offset, limit, TemplateList.class);
+ }
+
+ /**
+ * Gets a WhatsAppTemplate listing with specified pagination options and a wabaID or channelID filter.
+ *
+ * @param offset Number of objects to skip.
+ * @param limit Number of objects to take.
+ * @param wabaID The WABA ID to filter templates by.
+ * @param channelID A channel ID filter to return only templates that can be sent via that channel.
+ * @return List of templates.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws IllegalArgumentException if the provided arguments are not valid
+ */
+ public TemplateList listWhatsAppTemplates(final int offset, final int limit, final String wabaID, final String channelID)
+ throws UnauthorizedException, GeneralException, IllegalArgumentException {
+ validateWABAIDAndChannelIDArguments(wabaID, channelID);
+
+ Map map = new LinkedHashMap<>();
+ if (wabaID != null) map.put("wabaId", wabaID);
+ if (channelID != null) map.put("channelId", channelID);
+ String url = String.format(
+ "%s%s%s",
+ INTEGRATIONS_BASE_URL_V3,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH
+ );
+ return messageBirdService.requestList(url, map, offset, limit, TemplateList.class);
+ }
+
+ /**
+ * Gets a template listing with default pagination options.
+ *
+ * @return List of whatsapp templates.
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public TemplateList listWhatsAppTemplates() throws UnauthorizedException, GeneralException {
+ final int offset = 0;
+ final int limit = 10;
+
+ return listWhatsAppTemplates(offset, limit);
+ }
+
+ /**
+ * Retrieves the template of an existing template name.
+ *
+ * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable
+ * @return {@code List} template list
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if template name is not found
+ * @throws IllegalArgumentException if the provided arguments are not valid
+ */
+ public List getWhatsAppTemplatesBy(final String templateName)
+ throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ if (templateName == null) {
+ throw new IllegalArgumentException("Template name must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH
+ );
+
+ return messageBirdService.requestByIdAsList(url, templateName, TemplateResponse.class);
+ }
+
+ /**
+ * Retrieves the template of an existing template name.
+ *
+ * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable
+ * @param wabaID An optional WABA ID to look for the template ID under.
+ * @param channelID An optional channel ID to specify. If the template can be sent via the channel, it will return the template.
+ *
+ * @return {@code List} template list
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if template name is not found under the given WABA or cannot be sent under the supplied channel ID
+ * @throws IllegalArgumentException if the provided arguments are not valid
+ */
+ public List getWhatsAppTemplatesBy(final String templateName, final String wabaID, final String channelID)
+ throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ if (templateName == null) {
+ throw new IllegalArgumentException("Template name must be specified.");
+ }
+
+ String id = String.format("%s%s", templateName, getWabaIDOrChannelIDQuery(wabaID, channelID));
+ String url = String.format(
+ "%s%s%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH
+ );
+
+ return messageBirdService.requestByIdAsList(url, id, TemplateResponse.class);
+ }
+
+ /**
+ * Retrieves the template of an existing template name and language under the first waba connected to the requesting user.
+ *
+ * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable
+ * @param language A language code as returned by getWhatsAppTemplateBy in the language variable
+ *
+ * @return {@code TemplateResponse} template
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if template name and language are not found under the first waba connected to the requesting user.
+ * @throws IllegalArgumentException if the provided arguments are not valid
+ */
+ public TemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language)
+ throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ if (templateName == null || language == null) {
+ throw new IllegalArgumentException("Template name and language must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s%s/%s/%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH,
+ templateName,
+ language
+ );
+ return messageBirdService.request(url, TemplateResponse.class);
+ }
+
+ /**
+ * Retrieves the template of an existing template name and language under a WABA or for a channel.
+ *
+ * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable
+ * @param language A language code as returned by getWhatsAppTemplateBy in the language variable
+ * @param wabaID An optional WABA ID to look for the template ID under.
+ * @param channelID An optional channel ID to specify. If the template can be sent via the channel, it will return the template.
+ *
+ * @return {@code TemplateResponse} template
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if template name and language are not found under the given WABA or cannot be sent under the supplied channel ID.
+ * @throws IllegalArgumentException if the provided arguments are not valid
+ */
+ public TemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language, final String wabaID, final String channelID)
+ throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException {
+ if (templateName == null || language == null) {
+ throw new IllegalArgumentException("Template name and language must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s%s/%s/%s%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH,
+ templateName,
+ language,
+ getWabaIDOrChannelIDQuery(wabaID, channelID)
+ );
+ return messageBirdService.request(url, TemplateResponse.class);
+ }
+
+ /**
+ * Validates the WABA ID and Channel ID argument pair.
+ *
+ * @param wabaID A WABA ID.
+ * @param channelID A channel ID.
+ * @throws IllegalArgumentException if the argument pair is invalid.
+ */
+ private void validateWABAIDAndChannelIDArguments(String wabaID, String channelID)
+ throws IllegalArgumentException {
+ if (wabaID == null && channelID == null) {
+ throw new IllegalArgumentException("wabaID or channelID must be specified");
+ }
+
+ if (wabaID != null && channelID != null) {
+ throw new IllegalArgumentException("only supply wabaID or channelID - not both");
+ }
+ }
+
+ /**
+ * Validates the WABA ID and Channel ID argument pair and returns a valid query parameter string.
+ *
+ * @param wabaID A WABA ID.
+ * @param channelID A channel ID.
+ * @throws IllegalArgumentException if the argument pair is invalid.
+ */
+ private String getWabaIDOrChannelIDQuery(String wabaID, String channelID)
+ throws IllegalArgumentException {
+ validateWABAIDAndChannelIDArguments(wabaID, channelID);
+
+ String query = "";
+
+ if (wabaID != null) {
+ query = String.format("?wabaId=%s", wabaID);
+ }
+ if (channelID != null) {
+ query = String.format("?channelId=%s", channelID);
+ }
+
+ return query;
+ }
+
+ /**
+ * Delete templates of an existing template name.
+ *
+ * @param templateName A template name which is created on the MessageBird platform
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if template name is not found
+ */
+ public void deleteTemplatesBy(final String templateName)
+ throws UnauthorizedException, GeneralException, NotFoundException {
+ if (templateName == null) {
+ throw new IllegalArgumentException("Template name must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s%s/%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH,
+ templateName
+ );
+ messageBirdService.delete(url, null);
+ }
+
+ public void unpauseTemplatesByTemplateName(final String templateName)
+ throws UnauthorizedException, GeneralException {
+ if (templateName == null) {
+ throw new IllegalArgumentException("Template name must be specified.");
+ }
+
+ String url = String.format(
+ "%s%s%s%s/%s",
+ INTEGRATIONS_BASE_URL_V2,
+ INTEGRATIONS_WHATSAPP_PATH,
+ TEMPLATES_PATH,
+ UNPAUSE_TEMAPLATE_PATH,
+ templateName
+ );
+ messageBirdService.sendPayLoad("POST", url, "", null);
+ }
+
+ /**
+ * Function to create a child account
+ *
+ * @param childAccountRequest of child account to create
+ * @return ChildAccountResponse created
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public ChildAccountCreateResponse createChildAccount(final ChildAccountRequest childAccountRequest) throws UnauthorizedException, GeneralException {
+ if (childAccountRequest.getName() == null || childAccountRequest.getName().isEmpty()) {
+ throw new IllegalArgumentException("Name must be specified.");
+ }
+
+ String url = String.format("%s%s", PARTNER_ACCOUNTS_BASE_URL, "/child-accounts");
+ return messageBirdService.sendPayLoad(url, childAccountRequest, ChildAccountCreateResponse.class);
+ }
+
+ /**
+ * Function to update a child account
+ *
+ * @param id of child account to update
+ * @return ChildAccountResponse created
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public ChildAccountResponse updateChildAccount(final String name, final String id) throws UnauthorizedException, GeneralException {
+ if (name == null) {
+ throw new IllegalArgumentException("Name must be specified.");
+ }
+
+ if (id == null) {
+ throw new IllegalArgumentException("Child account id must be specified.");
+ }
+ final ChildAccountRequest childAccountRequest = new ChildAccountRequest();
+ childAccountRequest.setName(name);
+ final String url = String.format("%s/child-accounts/%s", PARTNER_ACCOUNTS_BASE_URL, id);
+ return messageBirdService.sendPayLoad("PATCH", url, childAccountRequest, ChildAccountResponse.class);
+ }
+
+ /**
+ * Function to get a child account
+ *
+ * @param id of child account to update
+ * @return ChildAccountResponse created
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if id is not found
+ */
+ public ChildAccountDetailedResponse getChildAccountById(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (id == null) {
+ throw new IllegalArgumentException("Child account id must be specified.");
+ }
+ return messageBirdService.requestByID(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", id, ChildAccountDetailedResponse.class);
+ }
+
+ /**
+ * Function to get a child account
+ *
+ * @return ChildAccountResponse created
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ public List getChildAccounts(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException {
+ verifyOffsetAndLimit(offset, limit);
+ return messageBirdService.requestList(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", offset, limit, List.class);
+ }
+
+ /**
+ * Function to delete a child account
+ *
+ * @param id of child account to delete
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if id is not found
+ */
+ public void deleteChildAccount(final String id) throws UnauthorizedException, GeneralException, NotFoundException {
+ if (id == null) {
+ throw new IllegalArgumentException("Child account id must be specified.");
+ }
+
+ String url = String.format("%s/child-accounts", PARTNER_ACCOUNTS_BASE_URL);
+ System.out.println("url: " + url);
+ messageBirdService.deleteByID(url, id);
+ }
+
+ /**
+ * Returns outbound pricing for the default SMS configuration for the authenticated account.
+ *
+ * @return outbound pricing for the default SMS configuration for the authenticated account
+ *
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if pricing information could not be found
+ *
+ * @see Pricing API
+ */
+ public OutboundSmsPriceResponse getOutboundSmsPrices() throws GeneralException, UnauthorizedException, NotFoundException {
+ return messageBirdService.request(OUTBOUND_SMS_PRICING_PATH, OutboundSmsPriceResponse.class);
+ }
+
+ /**
+ * Returns outbound SMS pricing for a specific SMPP username.
+ *
+ * @param smppUsername the SMPP SystemID provided by MessageBird
+ *
+ * @return outbound SMS pricing for the given SMPP username
+ *
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if pricing information could not be found for the given SMPP username
+ *
+ * @see Pricing API
+ */
+ public OutboundSmsPriceResponse getOutboundSmsPrices(final String smppUsername) throws GeneralException, UnauthorizedException, NotFoundException {
+ if (smppUsername == null) {
+ throw new IllegalArgumentException("SMPP username must be specified.");
+ }
+
+ final String url = String.format(OUTBOUND_SMS_PRICING_SMPP_PATH, smppUsername);
+ return messageBirdService.request(url, OutboundSmsPriceResponse.class);
+ }
+}
diff --git a/api/src/main/java/com/messagebird/MessageBirdService.java b/api/src/main/java/com/messagebird/MessageBirdService.java
index 525e6d50..43201fb7 100644
--- a/api/src/main/java/com/messagebird/MessageBirdService.java
+++ b/api/src/main/java/com/messagebird/MessageBirdService.java
@@ -3,13 +3,28 @@
import com.messagebird.exceptions.GeneralException;
import com.messagebird.exceptions.NotFoundException;
import com.messagebird.exceptions.UnauthorizedException;
-
+import com.messagebird.objects.PagedPaging;
+import java.util.List;
import java.util.Map;
/**
* Created by rvt on 1/7/15.
*/
public interface MessageBirdService {
+
+ /**
+ * Send GET request . It will retrieve a json object R back.
+ *
+ * @author ssk910
+ * @param request path to the request, for example "/messages/id/language"
+ * @param clazz Class type to return
+ * @return new class with returned dataset
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if id not found
+ */
+ R request(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException;
+
/**
* Execute a object by ID request. It will add the id to the request parameter and retreive a json object R back.
*
@@ -17,47 +32,146 @@ public interface MessageBirdService {
* @param id id of the object to request. id can be null in case request's that don't need a id, for example /balance
* @param clazz Class type to return
* @return new class with returned dataset
- * @throws com.messagebird.exceptions.UnauthorizedException
- * @throws com.messagebird.exceptions.GeneralException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
R requestByID(String request, String id, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException;
+ R requestByID(String request, String id, Map params, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException;
+
+ /**
+ * Execute a object by ID request. It will add the id to the request parameter and retrieve a list of an object E back.
+ *
+ * @author ssk910
+ * @param request path to the request, for example "/messages"
+ * @param id id of the object to request. id can be null in case request's that don't need a id, for example /balance
+ * @param elementClass Class type of List to return
+ * @return new list of elementClass
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException
+ */
+ List requestByIdAsList(String request, String id, Class elementClass) throws UnauthorizedException, GeneralException, NotFoundException;
+
/**
* Delete a object by ID.
*
* @param request path to the request, for example "/messages"
* @param id id of the object to delete
- * @throws UnauthorizedException
- * @throws GeneralException
- * @throws NotFoundException
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if id not found
*/
void deleteByID(String request, String id) throws UnauthorizedException, GeneralException, NotFoundException;
+ /**
+ * Send DELETE request. It will retrieve a json object R back.
+ *
+ * @author ssk910
+ * @param request path to the request, for example "/messages/id/language"
+ * @param clazz Class type to return
+ * @return class with returned dataset
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ * @throws NotFoundException if id not found
+ */
+ R delete(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException;
+
/**
* Request a List 'of' object.
- * Allow to request a listMessage or listViewMessges objects.
- * @see com.messagebird.objects.MessageListBase
+ * Allow to request a listMessage or listViewMessages objects.
+ * @see com.messagebird.objects.MessageList
*
- * @param request
+ * @param request request from client
* @param offset offset of data to return
* @param limit limit number of objects, incase you notice you pass in '1' a lot, please consider using requestByID if you know the ID of the message
* @param clazz object type to return
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
R requestList(String request, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException;
/**
- * Send a payload and receive a payload object
+ * Request a List 'of' object.
+ * Allow to request a listMessage or listViewMessages objects.
+ * @see com.messagebird.objects.MessageList
+ *
+ * @param request request from client
+ * @param params additional query params
+ * @param offset offset of data to return
+ * @param limit limit number of objects, incase you notice you pass in '1' a lot, please consider using requestByID if you know the ID of the message
+ * @param clazz object type to return
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ R requestList(String request, Map params, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException;
+
+ /**
+ * Request a List 'of' object.
+ * Allow to request a listMessage or listViewMessages objects.
+ * @see com.messagebird.objects.MessageList
+ *
+ * @param request request from client
+ * @param pagedPaging includes page and page size
+ * @param clazz object type to return
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ R requestList(String request, PagedPaging pagedPaging, Class clazz) throws UnauthorizedException, GeneralException;
+
+ /**
+ * Send a payload with the POST method and receive a payload object.
*
* @param request path to the request, for example "/messages"
* @param payload payload to send to the server
* @param clazz object type to return
- * @return
- * @throws UnauthorizedException
- * @throws GeneralException
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
*/
R sendPayLoad(String request, P payload, Class clazz) throws UnauthorizedException, GeneralException;
+
+ /**
+ * Send a payload with the provided method and receive a payload object.
+ *
+ * @param method HTTP method to use for the request
+ * @param request path to the request, for example "/messages"
+ * @param payload payload to send to the server
+ * @param clazz object type to return
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ R sendPayLoad(String method, String request, P payload, Class clazz) throws UnauthorizedException, GeneralException;
+
+ /**
+ * Send a payload with the provided method and headers and receive a payload object.
+ *
+ * @param method HTTP method to use for the request
+ * @param request path to the request, for example "/messages"
+ * @param headers additional headers to set on the request
+ * @param payload payload to send to the server
+ * @param clazz object type to return
+ * @return base class
+ * @throws UnauthorizedException if client is unauthorized
+ * @throws GeneralException general exception
+ */
+ R sendPayLoad(String method, String request, Map headers, P payload, Class 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 70ea1189..ef179b45 100644
--- a/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java
+++ b/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java
@@ -1,42 +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 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.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.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.net.*;
+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 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 = "https://rest.messagebird.com";
- private final String clienVersion = "1.1.4";
- private final String userAgentString = "MessageBird/Java ApiClient/" + clienVersion;
+ private final String serviceUrl;
+ private final String clientVersion = "6.3.1";
+ private final String userAgentString;
private Proxy proxy = null;
public MessageBirdServiceImpl(final String accessKey, final String serviceUrl) {
@@ -47,12 +84,39 @@ public MessageBirdServiceImpl(final String accessKey, final String serviceUrl) {
throw new IllegalArgumentException(SERVICE_URL_MUST_BE_SPECIFIED);
}
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 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 = "";
@@ -62,87 +126,453 @@ public R requestByID(String request, String id, Class clazz) throws Unaut
return getJsonData(request + path, null, "GET", clazz);
}
+ @Override
+ public R requestByID(String request, String id, Map params, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException {
+ String path = "";
+ if (id != null) {
+ path = "/" + id;
+ }
+ // Make rest of GET request
+ String queryParams = "";
+ if (!params.isEmpty()) {
+ queryParams = "?" + getPathVariables(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 + "?" + 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, "POST", clazz);
+ 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_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);
@@ -153,57 +583,99 @@ public
HttpURLConnection getConnection(final String serviceUrl, final P post
connection = (HttpURLConnection) restService.openConnection();
}
connection.setDoInput(true);
- connection.setRequestProperty("Accepts:", "application/json");
+ connection.setRequestProperty("Accept", "application/json");
connection.setUseCaches(false);
connection.setRequestProperty("charset", "utf-8");
connection.setRequestProperty("Connection", "close");
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 = getDateFormat();
+ mapper.setDateFormat(df);
+
+ setAdditionalHeaders(connection, headers);
- final String json = mapper.writeValueAsString(postData);
- connection.getOutputStream().write(json.getBytes("UTF-8"));
+ 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:", "application/text");
+ 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;
@@ -211,7 +683,8 @@ public String getAccessKey() {
/**
* get the used service URL
- * @return
+ *
+ * @return String
*/
public String getServiceUrl() {
return serviceUrl;
@@ -219,15 +692,17 @@ public String getServiceUrl() {
/**
* Get the client version
- * @return
+ *
+ * @return String
*/
- public String getClienVersion() {
- return clienVersion;
+ public String getClientVersion() {
+ return clientVersion;
}
/**
* Get the user agent string
- * @return
+ *
+ * @return String
*/
public String getUserAgentString() {
return userAgentString;
@@ -238,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;
@@ -246,7 +722,8 @@ public void setProxy(Proxy proxy) {
/**
* Get the proxy object if set
- * @return
+ *
+ * @return Proxy
*/
public Proxy getProxy() {
return proxy;
@@ -254,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();
@@ -277,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/Gender.java b/api/src/main/java/com/messagebird/objects/Gender.java
new file mode 100644
index 00000000..21df1830
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Gender.java
@@ -0,0 +1,21 @@
+package com.messagebird.objects;
+
+/**
+ * Created by faizan on 09/12/15.
+ */
+public enum Gender {
+
+ MALE("male"),
+ FEMALE("female");
+
+ private String code;
+
+ Gender(String code) {
+ this.code = code;
+ }
+
+ public String toString() {
+ return this.code;
+ }
+}
+
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/Hlr.java b/api/src/main/java/com/messagebird/objects/Hlr.java
index 589c867d..d6244e57 100644
--- a/api/src/main/java/com/messagebird/objects/Hlr.java
+++ b/api/src/main/java/com/messagebird/objects/Hlr.java
@@ -14,12 +14,13 @@
public class Hlr {
private String id;
private String href;
- private BigInteger msisdn;
+ protected BigInteger msisdn;
private String network;
- private String reference;
+ protected String reference;
private String status;
private Date createdDatetime;
private Date statusDatetime;
+ private HlrDetails details;
public Hlr() {
}
@@ -32,6 +33,7 @@ public String toString() {
", msisdn=" + msisdn +
", network='" + network + '\'' +
", reference='" + reference + '\'' +
+ ", details='" + details + '\'' +
", status='" + status + '\'' +
", createdDatetime=" + createdDatetime +
", statusDatetime=" + statusDatetime +
@@ -101,4 +103,9 @@ public Date getCreatedDatetime() {
public Date getStatusDatetime() {
return statusDatetime;
}
+
+ public HlrDetails getDetails() {
+ return details;
+ }
}
+
diff --git a/api/src/main/java/com/messagebird/objects/HlrDetails.java b/api/src/main/java/com/messagebird/objects/HlrDetails.java
new file mode 100644
index 00000000..f100a1ac
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/HlrDetails.java
@@ -0,0 +1,62 @@
+package com.messagebird.objects;
+
+/**
+ * Created by faizan on 01/02/16.
+ */
+public class HlrDetails {
+ String status_desc;
+ String imsi;
+ String country_iso;
+ String country_name;
+ String location_msc;
+ String location_iso;
+ int ported;
+ int roaming;
+
+
+ @Override
+ public String toString() {
+ return "HlrDetails{" +
+ "status_desc='" + status_desc + '\'' +
+ ", imsi='" + imsi + '\'' +
+ ", country_iso=" + country_iso +
+ ", country_name='" + country_name + '\'' +
+ ", location_msc='" + location_msc + '\'' +
+ ", location_iso='" + location_iso + '\'' +
+ ", ported=" + Integer.toString(ported) +
+ ", roaming=" + Integer.toString(roaming) +
+ '}';
+ }
+
+ public String getStatus_desc() {
+ return status_desc;
+ }
+
+ public String getImsi() {
+ return imsi;
+ }
+
+ public String getCountry_iso() {
+ return country_iso;
+ }
+
+ public String getCountry_name() {
+ return country_name;
+ }
+
+ public String getLocation_msc() {
+ return location_msc;
+ }
+
+ public String getLocation_iso() {
+ return location_iso;
+ }
+
+ public int getPorted() {
+ return ported;
+ }
+
+ public int getRoaming() {
+ return roaming;
+ }
+}
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
new file mode 100644
index 00000000..ccb63351
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Language.java
@@ -0,0 +1,47 @@
+package com.messagebird.objects;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Created by faizan on 09/12/15.
+ */
+public enum Language {
+
+ NL_NL("nl-nl"),
+ DE_DE("de-de"),
+ EN_GB("en-gb"),
+ EN_US("en-us"),
+ ES_ES("es-es"),
+ FR_FR("fr-fr"),
+ RU_RU("ru-ru"),
+ ZH_CN("zh-cn"),
+ EN_AU("en-au"),
+ ES_MX("es-mx"),
+ ES_US("es-us"),
+ FR_CA("fr-ca"),
+ IS_IS("is-is"),
+ IT_IT("it-it"),
+ JA_JP("ja-jp"),
+ KO_KR("ko-kr"),
+ PL_PL("pl-pl"),
+ PT_BR("pt-br"),
+ RO_RO("ro-ro");
+
+ final String code;
+
+ Language(String code) {
+ this.code = code;
+ }
+
+ @JsonValue
+ public String getCode() {
+ return code;
+ }
+
+ @Override
+ public String toString() {
+ 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/Lookup.java b/api/src/main/java/com/messagebird/objects/Lookup.java
new file mode 100644
index 00000000..2a7780eb
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Lookup.java
@@ -0,0 +1,108 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+
+public class Lookup implements Serializable {
+
+ private static final long serialVersionUID = 8927014359452296030L;
+
+ private String href;
+ private String countryCode;
+ private Integer countryPrefix;
+ private BigInteger phoneNumber;
+ private String type;
+ private Formats formats;
+ private LookupHlr hlr;
+
+ @Override
+ public String toString() {
+ return "Lookup{" +
+ "href=" + href +
+ ", countryCode=" + countryCode +
+ ", countryPrefix=" + countryPrefix +
+ ", phoneNumber=" + phoneNumber +
+ ", type=" + type +
+ ", formats=" + formats +
+ ", hlr=" + hlr +
+ "}";
+ }
+
+ public Lookup() {
+ }
+
+ public Lookup(BigInteger phoneNumber) {
+ this.phoneNumber = phoneNumber;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ public Integer getCountryPrefix() {
+ return countryPrefix;
+ }
+
+ public BigInteger getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Formats getFormats() {
+ return formats;
+ }
+
+ public Hlr getHlr() {
+ return hlr;
+ }
+
+ static public class Formats implements Serializable {
+
+ private static final long serialVersionUID = 2165916336570704972L;
+
+ private String e164;
+ private String international;
+ private String national;
+ private String rfc3966;
+
+ public Formats() {
+ }
+
+ @Override
+ public String toString() {
+ return "Formats{" +
+ "e164=" + e164 +
+ ", international=" + international +
+ ", national=" + national +
+ ", rfc3966=" + rfc3966 +
+ '}';
+ }
+
+ public String getE164() {
+ return e164;
+ }
+
+ public String getInternational() {
+ return international;
+ }
+
+ public String getNational() {
+ return national;
+ }
+
+ public String getRfc3966() {
+ return rfc3966;
+ }
+ }
+}
diff --git a/api/src/main/java/com/messagebird/objects/LookupHlr.java b/api/src/main/java/com/messagebird/objects/LookupHlr.java
new file mode 100644
index 00000000..98e568ae
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/LookupHlr.java
@@ -0,0 +1,27 @@
+package com.messagebird.objects;
+
+import java.math.BigInteger;
+
+public class LookupHlr extends Hlr {
+ private String countryCode;
+
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ public BigInteger getPhoneNumber() {
+ return msisdn;
+ }
+
+ public void setPhoneNumber(BigInteger phoneNumber) {
+ this.msisdn = phoneNumber;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+}
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/Message.java b/api/src/main/java/com/messagebird/objects/Message.java
index 3a4ae569..1776cecf 100644
--- a/api/src/main/java/com/messagebird/objects/Message.java
+++ b/api/src/main/java/com/messagebird/objects/Message.java
@@ -2,6 +2,7 @@
import java.io.Serializable;
import java.math.BigInteger;
+import java.net.URL;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
@@ -27,9 +28,14 @@ public class Message implements MessageBase, Serializable {
private Integer validity;
private Integer gateway;
private Map typeDetails;
- private DataCodingType datacoding = DataCodingType.unicode;
+ private DataCodingType datacoding = DataCodingType.plain;
private MClassType mclass;
private Date scheduledDatetime;
+
+ /**
+ * Optional URL for the status report. If not set uses URL from account settings.
+ */
+ private URL reportUrl;
public Message(String originator, String body, String recipients) {
if (recipients == null || recipients.trim().length() == 0) {
@@ -43,7 +49,7 @@ public Message(String originator, String body, String recipients) {
}
this.recipients = recipients.trim();
this.originator = originator.trim();
- this.originator = this.originator.substring(0, Math.min(11, this.originator.length()));
+ this.originator = this.originator.substring(0, Math.min(17, this.originator.length()));
this.body = body.trim();
}
@@ -115,6 +121,7 @@ public String toString() {
", reference='" + reference + '\'' +
", validity=" + validity +
", gateway=" + gateway +
+ ", reportUrl=" + reportUrl +
", typeDetails='" + typeDetails + '\'' +
", datacoding=" + datacoding +
", mclass=" + mclass +
@@ -298,5 +305,12 @@ public void setPremiumSMS(Object shortcode, Object keyword, Object tariff, Objec
this.type = MsgType.premium;
}
+ public URL getReportUrl() {
+ return reportUrl;
+ }
+
+ public void setReportUrl(URL reportUrl) {
+ this.reportUrl = reportUrl;
+ }
}
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/Messages.java b/api/src/main/java/com/messagebird/objects/Messages.java
new file mode 100644
index 00000000..3319d81d
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Messages.java
@@ -0,0 +1,24 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+/**
+ * Created by faizan on 09/12/15.
+ */
+public class Messages implements Serializable {
+ private String href;
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public String toString() {
+ return "Messages{" +
+ "href=" + this.href +
+ "}";
+ }
+}
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/Verify.java b/api/src/main/java/com/messagebird/objects/Verify.java
new file mode 100644
index 00000000..346847c2
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/Verify.java
@@ -0,0 +1,86 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Created by faizan on 09/12/15.
+ */
+public class Verify implements Serializable {
+
+ private String id;
+ private String href;
+ private String recipient;
+ private Messages messages;
+ private String status;
+ private Date createdDatetime;
+ private Date validUntilDatetime;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public String getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ public Messages getMessages() {
+ return messages;
+ }
+
+ public void setMessages(Messages messages) {
+ this.messages = messages;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Date getCreatedDatetime() {
+ return createdDatetime;
+ }
+
+ public void setCreatedDatetime(Date createdDatetime) {
+ this.createdDatetime = createdDatetime;
+ }
+
+ public Date getValidUntilDatetime() {
+ return validUntilDatetime;
+ }
+
+ public void setValidUntilDatetime(Date validUntilDatetime) {
+ this.validUntilDatetime = validUntilDatetime;
+ }
+
+ public String toString() {
+ return "Verify {" + " " +
+ "id=" + this.id + " " +
+ "href=" + this.href + " " +
+ "recipient=" + this.recipient + " " +
+ "messages=" + this.messages + " " +
+ "status=" + this.status + " " +
+ "createdDatetime=" + this.createdDatetime + " " +
+ "validUntilDatetime=" + this.validUntilDatetime+ " " +
+ "}";
+ }
+}
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
new file mode 100644
index 00000000..bee684e9
--- /dev/null
+++ b/api/src/main/java/com/messagebird/objects/VerifyRequest.java
@@ -0,0 +1,136 @@
+package com.messagebird.objects;
+
+import java.io.Serializable;
+
+/**
+ * Created by faizan on 09/12/15.
+ */
+public class VerifyRequest implements Serializable {
+
+ private String recipient;
+ private String originator;
+ private String reference;
+ 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;
+ }
+
+ public String getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ public String getOriginator() {
+ return originator;
+ }
+
+ public void setOriginator(String originator) {
+ this.originator = originator;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+
+ public VerifyType getType() {
+ return 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.
+ *
+ * @return returns plain or unicode
+ */
+ public DataCodingType getDatacoding() {
+ return datacoding;
+ }
+
+ /**
+ * The datacoding used by the template.
+ *
+ * @param datacoding
+ */
+ public void setDatacoding(DataCodingType datacoding) {
+ this.datacoding = datacoding;
+ }
+
+ public String getTemplate() {
+ return template;
+ }
+
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Integer getTokenLength() {
+ return tokenLength;
+ }
+
+ public void setTokenLength(Integer tokenLength) {
+ this.tokenLength = tokenLength;
+ }
+
+ public Gender getVoice() {
+ return voice;
+ }
+
+ public void setVoice(Gender voice) {
+ this.voice = voice;
+ }
+
+ public Language getLanguage() {
+ return language;
+ }
+
+ 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 db9abcbc..0674851e 100644
--- a/api/src/main/java/com/messagebird/objects/VoiceMessage.java
+++ b/api/src/main/java/com/messagebird/objects/VoiceMessage.java
@@ -14,6 +14,7 @@ public class VoiceMessage implements MessageBase, Serializable {
private static final long serialVersionUID = 1553564355131214875L;
+ private String originator;
private String body;
private String recipients;
private String reference;
@@ -21,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) {
@@ -48,6 +50,24 @@ public String getBody() {
return body;
}
+ /**
+ * The originator (sender) of the message
+ *
+ * @return String
+ */
+ public String getOriginator() {
+ return originator;
+ }
+
+ /**
+ * Set the originator (sender) of the message
+ *
+ * @return void
+ */
+ public void setOriginator(String originator) {
+ this.originator = originator;
+ }
+
/**
* The list of recipients msisdn's.
*
@@ -152,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 056a6210..6563919e 100644
--- a/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java
+++ b/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java
@@ -14,12 +14,14 @@ public class VoiceMessageResponse implements MessageResponseBase, Serializable {
private String id;
private String href;
+ private String originator;
private String body;
private String reference;
private String language;
private VoiceType voice;
private Integer repeat;
private IfMachineType ifMachine;
+ private int machineTimeout;
private Date scheduledDatetime;
private Date createdDatetime;
private MessageResponse.Recipients recipients;
@@ -32,12 +34,14 @@ public String toString() {
return "VoiceMessageResponse{" +
"id='" + id + '\'' +
", href='" + href + '\'' +
+ ", originator='" + originator +'\'' +
", body='" + body + '\'' +
", reference='" + reference + '\'' +
", language='" + language + '\'' +
", voice=" + voice +
", repeat=" + repeat +
", ifMachine=" + ifMachine +
+ ", machineTimeout=" + machineTimeout +
", scheduledDatetime=" + scheduledDatetime +
", createdDatetime=" + createdDatetime +
", recipients=" + recipients +
@@ -54,6 +58,15 @@ public String getHref() {
return href;
}
+ /**
+ * The originator (sender) of the message
+ *
+ * @return String
+ */
+ public String getOriginator() {
+ return originator;
+ }
+
@Override
public String getBody() {
return body;
@@ -103,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