diff --git a/pom.xml b/pom.xml index 6701b1a..8282072 100644 --- a/pom.xml +++ b/pom.xml @@ -370,7 +370,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4.2 + 2.15.2 com.fasterxml.jackson.datatype @@ -396,7 +396,12 @@ com.google.googlejavaformat google-java-format - 1.10.0 + 1.17.0 + + + com.google.guava + guava + 32.0.1-jre diff --git a/src/main/java/com/sinch/xms/ApiConnection.java b/src/main/java/com/sinch/xms/ApiConnection.java index 5a234c0..44b268e 100644 --- a/src/main/java/com/sinch/xms/ApiConnection.java +++ b/src/main/java/com/sinch/xms/ApiConnection.java @@ -33,8 +33,11 @@ import com.sinch.xms.api.MtBatchBinarySmsResult; import com.sinch.xms.api.MtBatchBinarySmsUpdate; import com.sinch.xms.api.MtBatchDryRunResult; +import com.sinch.xms.api.MtBatchMmsCreate; +import com.sinch.xms.api.MtBatchMmsResult; +import com.sinch.xms.api.MtBatchMmsUpdate; +import com.sinch.xms.api.MtBatchResult; import com.sinch.xms.api.MtBatchSmsCreate; -import com.sinch.xms.api.MtBatchSmsResult; import com.sinch.xms.api.MtBatchTextSmsCreate; import com.sinch.xms.api.MtBatchTextSmsResult; import com.sinch.xms.api.MtBatchTextSmsUpdate; @@ -327,7 +330,7 @@ private URI endpoint(@Nonnull String subPath, @Nonnull List param } /** - * Like {@link #endpoint(String, String)} but with no query parameters. + * Like {@link #endpoint(String, List)} but with no query parameters. * * @param subPath path fragment to place after the base path * @return a non-null endpoint URL @@ -604,6 +607,49 @@ public Future createBatchAsync( .execute(requestProducer, responseConsumer, callbackWrapper().wrap(callback)); } + /** + * Creates the given batch and schedules it for submission. If {@link MtBatchMmsCreate#sendAt()} + * returns null then the batch submission will begin immediately. + * + *

This method blocks until the request completes and its use is discouraged. Please consider + * using the asynchronous method {@link #createBatchAsync(MtBatchMmsCreate, FutureCallback)} + * instead. + * + * @param mms the batch to create + * @return a batch creation result + * @throws InterruptedException if the current thread was interrupted while waiting + * @throws ApiException if an error occurred while communicating with XMS + */ + public MtBatchMmsResult createBatch(MtBatchMmsCreate mms) + throws InterruptedException, ApiException { + try { + return createBatchAsync(mms, null).get(); + } catch (ExecutionException e) { + throw Utils.unwrapExecutionException(e); + } + } + + /** + * Asynchronously creates the given mms batch and schedules it for submission. If {@link + * MtBatchMmsCreate#sendAt()} returns null then the batch submission will begin + * immediately. + * + * @param mms the batch to create + * @param callback a callback that is invoked when batch is created + * @return a future whose result is the creation response + */ + public Future createBatchAsync( + MtBatchMmsCreate mms, FutureCallback callback) { + HttpPost req = post(batchesEndpoint(), mms); + + HttpAsyncRequestProducer requestProducer = new BasicAsyncRequestProducer(endpointHost(), req); + HttpAsyncResponseConsumer responseConsumer = + jsonAsyncConsumer(MtBatchMmsResult.class); + + return httpClient() + .execute(requestProducer, responseConsumer, callbackWrapper().wrap(callback)); + } + /** * Replaces the batch with the given identifier. After this method completes the batch will match * the provided batch description. @@ -778,6 +824,48 @@ public Future updateBatchAsync( return httpClient().execute(producer, consumer, callbackWrapper().wrap(callback)); } + /** + * Updates the given mms batch. + * + *

This method blocks until the request completes and its use is discouraged. Please consider + * using the asynchronous method {@link #updateBatchAsync(BatchId, MtBatchMmsUpdate, + * FutureCallback)} instead. + * + * @param id identifier of the batch to update + * @param mms a description of the desired updated + * @return the batch with the updates applied + * @throws InterruptedException if the current thread was interrupted while waiting + * @throws ApiException if an error occurred while communicating with XMS + */ + public MtBatchMmsResult updateBatch(BatchId id, MtBatchMmsUpdate mms) + throws InterruptedException, ApiException { + try { + return updateBatchAsync(id, mms, null).get(); + } catch (ExecutionException e) { + throw Utils.unwrapExecutionException(e); + } + } + + /** + * Asynchronously updates the mms batch with the given batch ID. The batch is updated to match the + * given update object. + * + * @param batchId the batch that should be updated + * @param mms description of the desired update + * @param callback called at call success, failure, or cancellation + * @return a future containing the updated batch + */ + public Future updateBatchAsync( + BatchId batchId, MtBatchMmsUpdate mms, FutureCallback callback) { + HttpPost req = post(batchEndpoint(batchId), mms); + + HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(endpointHost(), req); + HttpAsyncResponseConsumer consumer = + jsonAsyncConsumer(MtBatchMmsResult.class); + + return httpClient().execute(producer, consumer, callbackWrapper().wrap(callback)); + } + /** * Fetches the given batch. * @@ -789,7 +877,7 @@ public Future updateBatchAsync( * @throws InterruptedException if the current thread was interrupted while waiting * @throws ApiException if an error occurred while communicating with XMS */ - public MtBatchSmsResult fetchBatch(BatchId id) throws InterruptedException, ApiException { + public MtBatchResult fetchBatch(BatchId id) throws InterruptedException, ApiException { try { return fetchBatchAsync(id, null).get(); } catch (ExecutionException e) { @@ -804,14 +892,13 @@ public MtBatchSmsResult fetchBatch(BatchId id) throws InterruptedException, ApiE * @param callback a callback that is activated at call completion * @return a future yielding the desired batch */ - public Future fetchBatchAsync( - BatchId batchId, FutureCallback callback) { + public Future fetchBatchAsync( + BatchId batchId, FutureCallback callback) { HttpGet req = get(batchEndpoint(batchId)); HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(endpointHost(), req); - HttpAsyncResponseConsumer consumer = - jsonAsyncConsumer(MtBatchSmsResult.class); + HttpAsyncResponseConsumer consumer = jsonAsyncConsumer(MtBatchResult.class); return httpClient().execute(producer, consumer, callbackWrapper().wrap(callback)); } @@ -823,12 +910,12 @@ public Future fetchBatchAsync( * @param filter the batch filter * @return a future page */ - public PagedFetcher fetchBatches(final BatchFilter filter) { - return new PagedFetcher() { + public PagedFetcher fetchBatches(final BatchFilter filter) { + return new PagedFetcher() { @Override - Future> fetchAsync( - int page, FutureCallback> callback) { + Future> fetchAsync( + int page, FutureCallback> callback) { return fetchBatches(page, filter, callbackWrapper().wrap(callback)); } }; @@ -842,8 +929,8 @@ Future> fetchAsync( * @param callback the callback to invoke when call is finished * @return a future page */ - private Future> fetchBatches( - int page, BatchFilter filter, FutureCallback> callback) { + private Future> fetchBatches( + int page, BatchFilter filter, FutureCallback> callback) { List params = filter.toQueryParams(page); URI url = endpoint("/batches", params); @@ -851,7 +938,7 @@ private Future> fetchBatches( HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(endpointHost(), req); - HttpAsyncResponseConsumer> consumer = + HttpAsyncResponseConsumer> consumer = jsonAsyncConsumer(PagedBatchResult.class); return httpClient().execute(producer, consumer, callbackWrapper().wrap(callback)); @@ -868,7 +955,7 @@ private Future> fetchBatches( * @throws InterruptedException if the current thread was interrupted while waiting * @throws ApiException if an error occurred while communicating with XMS */ - public MtBatchSmsResult cancelBatch(BatchId batchId) throws InterruptedException, ApiException { + public MtBatchResult cancelBatch(BatchId batchId) throws InterruptedException, ApiException { try { return cancelBatchAsync(batchId, null).get(); } catch (ExecutionException e) { @@ -883,14 +970,13 @@ public MtBatchSmsResult cancelBatch(BatchId batchId) throws InterruptedException * @param callback the callback invoked when request completes * @return a future containing the batch that was cancelled */ - public Future cancelBatchAsync( - BatchId batchId, FutureCallback callback) { + public Future cancelBatchAsync( + BatchId batchId, FutureCallback callback) { HttpDelete req = delete(batchEndpoint(batchId)); HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(endpointHost(), req); - HttpAsyncResponseConsumer consumer = - jsonAsyncConsumer(MtBatchSmsResult.class); + HttpAsyncResponseConsumer consumer = jsonAsyncConsumer(MtBatchResult.class); return httpClient().execute(producer, consumer, callbackWrapper().wrap(callback)); } @@ -1080,7 +1166,8 @@ private Future> fetchDeliveryReports( } /** - * Create a delivery feedback for the batch with the given batch ID. + * Create a delivery feedback for the batch with the given batch ID. Feedback can only be provided + * if feedback_enabled was set when batch was submitted. * *

This method blocks until the request completes and its use is discouraged. Please consider * using the asynchronous method {@link #createDeliveryFeedbackAsync(BatchId, @@ -1101,7 +1188,8 @@ public Void createDeliveryFeedback(BatchId id, FeedbackDeliveryCreate feedbackDe } /** - * Create a delivery feedback for the batch with the given batch ID and recipients + * Create a delivery feedback for the batch with the given batch ID. Feedback can only be provided + * if feedback_enabled was set when batch was submitted. * * @param id identifier of the batch * @param feedbackDeliveryCreate create delivery feedback input with recipients diff --git a/src/main/java/com/sinch/xms/ErrorResponseException.java b/src/main/java/com/sinch/xms/ErrorResponseException.java index c8b89f4..975097b 100644 --- a/src/main/java/com/sinch/xms/ErrorResponseException.java +++ b/src/main/java/com/sinch/xms/ErrorResponseException.java @@ -26,8 +26,7 @@ * the XMS API has been broken, the error code and text indicates the specifics. * *

For information about specific errors please refer to the XMS API - * documentation. + * "https://developers.sinch.com/docs/sms/api-reference/status-codes/">XMS API documentation. */ public class ErrorResponseException extends ApiException { diff --git a/src/main/java/com/sinch/xms/SinchSMSApi.java b/src/main/java/com/sinch/xms/SinchSMSApi.java index 81f091a..7e4b4e0 100644 --- a/src/main/java/com/sinch/xms/SinchSMSApi.java +++ b/src/main/java/com/sinch/xms/SinchSMSApi.java @@ -23,8 +23,11 @@ import com.sinch.xms.api.FeedbackDeliveryCreate; import com.sinch.xms.api.GroupCreate; import com.sinch.xms.api.GroupUpdate; +import com.sinch.xms.api.MediaBody; import com.sinch.xms.api.MtBatchBinarySmsCreate; import com.sinch.xms.api.MtBatchBinarySmsUpdate; +import com.sinch.xms.api.MtBatchMmsCreate; +import com.sinch.xms.api.MtBatchMmsUpdate; import com.sinch.xms.api.MtBatchTextSmsCreate; import com.sinch.xms.api.MtBatchTextSmsUpdate; import com.sinch.xms.api.ParameterValues; @@ -77,6 +80,26 @@ public static MtBatchBinarySmsUpdate.Builder batchBinarySmsUpdate() { return MtBatchBinarySmsUpdate.builder(); } + /** + * Returns a freshly created batch MMS message builder. + * + * @return a builder of MMS batch messages + */ + @Nonnull + public static MtBatchMmsCreate.Builder batchMms() { + return MtBatchMmsCreate.builder(); + } + + /** + * Returns a freshly created builder for MMS message updates. + * + * @return a builder of MMS message updates + */ + @Nonnull + public static MtBatchMmsUpdate.Builder batchMmsUpdate() { + return MtBatchMmsUpdate.builder(); + } + /** * Returns a freshly created builder for delivery feedback. * @@ -186,4 +209,14 @@ public static BatchDeliveryReportParams.Builder batchDeliveryReportParams() { public static DeliveryReportFilter.Builder deliveryReportFilter() { return DeliveryReportFilter.builder(); } + + /** + * Returns a freshly created builder of delivery report filters. + * + * @return a builder of delivery report filters + */ + @Nonnull + public static MediaBody.Builder mediaBody() { + return MediaBody.builder(); + } } diff --git a/src/main/java/com/sinch/xms/api/BatchDeliveryReport.java b/src/main/java/com/sinch/xms/api/BatchDeliveryReport.java index 47d319d..06c1744 100644 --- a/src/main/java/com/sinch/xms/api/BatchDeliveryReport.java +++ b/src/main/java/com/sinch/xms/api/BatchDeliveryReport.java @@ -22,93 +22,21 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.immutables.value.Value; /** A batch delivery report. */ @Value.Enclosing -@Value.Immutable -@ValueStylePackage -@JsonDeserialize(builder = BatchDeliveryReport.Builder.class) @JsonInclude(Include.NON_EMPTY) @JsonTypeInfo(use = Id.NAME, property = "type") -@JsonTypeName("delivery_report_sms") +@JsonSubTypes({@Type(BatchDeliveryReportSms.class), @Type(BatchDeliveryReportMms.class)}) public abstract class BatchDeliveryReport { - /** A description of the messages having a given delivery state. */ - @Value.Immutable - @JsonDeserialize(builder = BatchDeliveryReport.Status.Builder.class) - @JsonInclude(Include.NON_EMPTY) - public abstract static class Status { - - /** A builder of batch delivery report statuses. */ - public static class Builder extends BatchDeliveryReportImpl.Status.Builder { - - Builder() {} - } - - /** - * Creates a builder of {@link Status} instances. - * - * @return a builder - */ - @Nonnull - public static final Status.Builder builder() { - return new Builder(); - } - - /** - * The delivery status code. - * - * @return a status code - */ - public abstract int code(); - - /** - * The delivery status for this bucket. - * - * @return a non-null delivery status - */ - public abstract DeliveryStatus status(); - - /** - * The number of individual messages in this status bucket. - * - * @return a positive integer - */ - public abstract int count(); - - /** - * The recipients having this status. Note, this is non-empty only if a full delivery - * report has been requested. - * - * @return a non-null list of recipients - */ - public abstract List recipients(); - } - - /** A builder of batch delivery reports. */ - public static class Builder extends BatchDeliveryReportImpl.Builder { - - Builder() {} - } - - /** - * Creates a builder of {@link BatchDeliveryReport} instances. - * - * @return a builder - */ - @Nonnull - public static final BatchDeliveryReport.Builder builder() { - return new Builder(); - } - /** * Identifier of the batch to which this delivery report refers. * diff --git a/src/main/java/com/sinch/xms/api/BatchDeliveryReportMms.java b/src/main/java/com/sinch/xms/api/BatchDeliveryReportMms.java new file mode 100644 index 0000000..07deb6f --- /dev/null +++ b/src/main/java/com/sinch/xms/api/BatchDeliveryReportMms.java @@ -0,0 +1,56 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.annotation.Nonnull; +import org.immutables.value.Value; + +/** A batch delivery report. */ +@Value.Enclosing +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = BatchDeliveryReportMms.Builder.class) +@JsonInclude(Include.NON_EMPTY) +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonTypeName("delivery_report_mms") +public abstract class BatchDeliveryReportMms extends BatchDeliveryReport { + + /** A builder of batch delivery reports. */ + public static class Builder extends BatchDeliveryReportMmsImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link BatchDeliveryReportMms} instances. + * + * @return a builder + */ + @Nonnull + public static final BatchDeliveryReportMms.Builder builder() { + return new Builder(); + } +} diff --git a/src/main/java/com/sinch/xms/api/BatchDeliveryReportSms.java b/src/main/java/com/sinch/xms/api/BatchDeliveryReportSms.java new file mode 100644 index 0000000..3eebe5e --- /dev/null +++ b/src/main/java/com/sinch/xms/api/BatchDeliveryReportSms.java @@ -0,0 +1,56 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.annotation.Nonnull; +import org.immutables.value.Value; + +/** A batch delivery report. */ +@Value.Enclosing +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = BatchDeliveryReportSms.Builder.class) +@JsonInclude(Include.NON_EMPTY) +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonTypeName("delivery_report_sms") +public abstract class BatchDeliveryReportSms extends BatchDeliveryReport { + + /** A builder of batch delivery reports. */ + public static class Builder extends BatchDeliveryReportSmsImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link BatchDeliveryReportSms} instances. + * + * @return a builder + */ + @Nonnull + public static final BatchDeliveryReportSms.Builder builder() { + return new Builder(); + } +} diff --git a/src/main/java/com/sinch/xms/api/FeedbackDeliveryCreate.java b/src/main/java/com/sinch/xms/api/FeedbackDeliveryCreate.java index 617a263..ca49186 100644 --- a/src/main/java/com/sinch/xms/api/FeedbackDeliveryCreate.java +++ b/src/main/java/com/sinch/xms/api/FeedbackDeliveryCreate.java @@ -27,7 +27,8 @@ public static final FeedbackDeliveryCreate.Builder builder() { } /** - * The list of recipients. + * The list of recipients. Can be set as an empty list to indicate that all recipients received + * the message. If the feedback was enabled for a group, at least one phone number is required. * * @return a list of strings containing recipients */ diff --git a/src/main/java/com/sinch/xms/api/MediaBody.java b/src/main/java/com/sinch/xms/api/MediaBody.java new file mode 100644 index 0000000..ebb6ec0 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MediaBody.java @@ -0,0 +1,79 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** + * Representation of a media body. The text part is optional but the media url is mandatory. + * + *

Supported media types are: + * + *

    + *
  • image: .jpg, .png (please observe that .jpg files have wider support on mobile devices than + * .png files) + *
  • video: .mp4, .gif, .mov + *
  • vCard (Virtual Contact File): .vcf + *
  • PDF files: .pdf + *
+ */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = MediaBody.Builder.class) +@JsonInclude(Include.NON_EMPTY) +public abstract class MediaBody { + + /** A builder of media body creation descriptions. */ + public static final class Builder extends MediaBodyImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link MediaBody} instances. + * + * @return a builder + */ + @Nonnull + public static final MediaBody.Builder builder() { + return new MediaBody.Builder(); + } + + /** + * The text message. + * + * @return a message + */ + @Nullable + public abstract String message(); + + /** + * The url to the media content. + * + * @return a non-null url to the media content + */ + @Nonnull + public abstract String url(); +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchCreate.java b/src/main/java/com/sinch/xms/api/MtBatchCreate.java new file mode 100644 index 0000000..cfa74b1 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MtBatchCreate.java @@ -0,0 +1,130 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.OverridingMethodsMustInvokeSuper; +import org.immutables.value.Value; + +/** + * Base class for mobile terminated batch messages. A mobile terminated message can have either a + * {@link MtBatchSmsCreate SMS} or a {@link MtBatchMmsCreate MMS} message payload. + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +public abstract class MtBatchCreate { + + /** + * The list of message recipients. May not be empty. + * + * @return a non-empty list of recipients + */ + @JsonProperty("to") + public abstract List recipients(); + + /** + * The message originator. May be an MSISDN or short code. + * + * @return an originator address + */ + @Nullable + @JsonProperty("from") + public abstract String sender(); + + /** + * The type of delivery report to request for this batch. + * + * @return a type of report or null to use the default type + */ + @Nullable + @JsonProperty("delivery_report") + public abstract ReportType deliveryReport(); + + /** + * The time this batch should be sent. If null or set to a past time then the batch + * will be sent immediately. + * + * @return the time when this batch should be sent + */ + @Nullable + @JsonProperty("send_at") + public abstract OffsetDateTime sendAt(); + + /** + * The time at which this batch will expire. Any message not delivered by this time will be placed + * into an expired state and no further delivery will be attempted. + * + * @return the time when this batch expires + */ + @Nullable + @JsonProperty("expire_at") + public abstract OffsetDateTime expireAt(); + + /** + * The URL to which batch callbacks should be sent. If null then callbacks will be + * sent to the default URL. + * + * @return an URL having a callback listener or null to use the default callback URL + */ + @Nullable + @JsonProperty("callback_url") + public abstract URI callbackUrl(); + + /** + * If set to true, then feedback is expected after successful delivery. + * + * @return boolean value + */ + @Nullable + @JsonProperty("feedback_enabled") + public abstract Boolean feedbackEnabled(); + + /** + * The client identifier to attach to this message. If set, it will be added in the delivery + * report/callback of this batch. + * + * @return a client reference id + */ + @Nullable + @JsonProperty("client_reference") + public abstract String clientReference(); + + @OverridingMethodsMustInvokeSuper + @Value.Check + protected void check() { + if (recipients().isEmpty()) { + throw new IllegalStateException("no destination"); + } + + for (String to : recipients()) { + if (to.isEmpty()) { + throw new IllegalStateException("contains empty destination"); + } + } + + if (sender() != null && sender().isEmpty()) { + throw new IllegalStateException("empty from address"); + } + } +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchMmsCreate.java b/src/main/java/com/sinch/xms/api/MtBatchMmsCreate.java new file mode 100644 index 0000000..2ec6c87 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MtBatchMmsCreate.java @@ -0,0 +1,122 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** + * Container of all necessary parameters to create a media batch message. + * + *

A minimal definition has defined values for + * + *

    + *
  • {@link #recipients()}, + *
  • {@link #sender()}, + *
  • {@link #body()}. + *
+ */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = MtBatchMmsCreate.Builder.class) +@JsonTypeName("mt_media") +public abstract class MtBatchMmsCreate extends MtBatchCreate { + + /** A builder of textual batch messages. */ + public static class Builder extends MtBatchMmsCreateImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link MtBatchMmsCreate} instances. + * + * @return a builder + */ + @Nonnull + public static final MtBatchMmsCreate.Builder builder() { + return new Builder(); + } + + /** + * The message text or template. If this describes a template then {@link #parameters()} must + * describe the parameter substitutions. + * + *

A parameterized message is a regular text message but having one or more named parameters + * embedded using the syntax ${parameter_key} where parameter_key is + * your chosen parameter name. When the messaging system is sending the message the template will + * be expanded and any occurrence of ${parameter_key} in the message body is replaced + * by the parameter value for the message recipient. + * + *

The typical way to use templates is + * + *

+   * SinchSMSApi.batchMms()
+   *     .sender("12345")
+   *     .addRecipient("987654321")
+   *     // Other initialization
+   *     .body(
+   *         SinchSMSApi.mediaBody()
+   *             .message("Hello, ${name}")
+   *             .url("http://media.url.com/image.jpg"))
+   *             .build()
+   *     .putParameter("name",
+   *         SinchSMSApi.parameterValues()
+   *             .putSubstitution("987654321", "Jane")
+   *             .default("valued customer")
+   *             .build())
+   *     .build();
+   * 
+ * + * and here the recipient with MSISDN 987654321 will receive the message "Hello, Jane" while all + * other recipients would receive "Hello, valued customer". + * + * @return the message to send + */ + public abstract MediaBody body(); + + /** + * Whether the media included in the message to be checked against Sinch MMS channel best + * practices. If set to true, the message will be rejected if it doesn't conform to the listed + * recommendations, otherwise no validation will be performed. Defaults to false. + * + * @return boolean indicating if strict validation is meant to be performed + */ + @Nullable + @JsonProperty("strict_validation") + public abstract Boolean strictValidation(); + + /** + * The message template parameter substitutions. If {@link #body()} describes a template then this + * must return the necessary substitutions for all template parameters. + * + * @return a map from template variable to parameter values + * @see #body() + */ + @JsonInclude(Include.NON_EMPTY) + public abstract Map parameters(); +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchMmsResult.java b/src/main/java/com/sinch/xms/api/MtBatchMmsResult.java new file mode 100644 index 0000000..cf1fc63 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MtBatchMmsResult.java @@ -0,0 +1,88 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** + * Objects of this class contain information about a textual MMS batch. The information includes the + * message body, the batch identifier, the creation time, and so on. + */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = MtBatchMmsResult.Builder.class) +@JsonTypeName("mt_media") +public abstract class MtBatchMmsResult extends MtBatchResult { + + /** Builder of MMS batch results. */ + public static class Builder extends MtBatchMmsResultImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link MtBatchMmsResult} instances. + * + * @return a builder + */ + @Nonnull + public static final MtBatchMmsResult.Builder builder() { + return new Builder(); + } + + /** + * The message body including message text or template (optional) and the media content url. If + * this describes a template then {@link #parameters()} describes the parameter substitutions. + * + *

See {@link MtBatchMmsCreate#body()} for a more thorough description of this field. + * + * @return the message to send + */ + public abstract MediaBody body(); + + /** + * Whether the media included in the message to be checked against Sinch MMS channel best + * practices. If set to true, the message will be rejected if it doesn't conform to the listed + * recommendations, otherwise no validation will be performed. Defaults to false. + * + * @return boolean indicating if strict validation is meant to be performed + */ + @Nullable + @JsonProperty("strict_validation") + public abstract Boolean strictValidation(); + + /** + * The message template parameter substitutions. If {@link #body()} describes a template then this + * returns the substitutions for the template parameters. + * + * @return a map from template variable to parameter values + * @see #body() + */ + @JsonInclude(Include.NON_EMPTY) + public abstract Map parameters(); +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchMmsUpdate.java b/src/main/java/com/sinch/xms/api/MtBatchMmsUpdate.java new file mode 100644 index 0000000..3b1e6d8 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MtBatchMmsUpdate.java @@ -0,0 +1,206 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.sinch.xms.UpdateValue; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** A description of updates that can be applied to a media batch message. */ +@Value.Immutable +@ValueStylePackage +@JsonSerialize(as = MtBatchMmsUpdateImpl.class) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonTypeName("mt_media") +public abstract class MtBatchMmsUpdate extends MtBatchUpdate { + + /** A builder of text batch message updates. */ + public static class Builder extends MtBatchMmsUpdateImpl.Builder { + + Builder() {} + + /** + * Resets the delivery report type to the messaging system default value. + * + * @return this builder for use in a chained invocation + */ + public Builder unsetDeliveryReport() { + return this.deliveryReport(UpdateValue.unset()); + } + + /** + * Updates the delivery report type of this batch. If given a null reference then + * this is equivalent to calling {@link #unsetDeliveryReport()}. + * + * @param deliveryReport the new delivery report type or null to unset + * @return this builder for use in a chained invocation + */ + public Builder deliveryReport(ReportType deliveryReport) { + if (deliveryReport == null) { + return this.unsetDeliveryReport(); + } else { + return this.deliveryReport(UpdateValue.set(deliveryReport)); + } + } + + /** + * Unsets the scheduled send time. This has the effect of immediately starting to send the + * batch. + * + * @return this builder for use in a chained invocation + */ + public Builder unsetSendAt() { + return this.sendAt(UpdateValue.unset()); + } + + /** + * Updates the scheduled send time. If given a null reference then this is + * equivalent to calling {@link #unsetSendAt()}. + * + * @param time the new scheduled send time + * @return this builder for use in a chained invocation + */ + public Builder sendAt(OffsetDateTime time) { + if (time == null) { + return this.unsetSendAt(); + } else { + return this.sendAt(UpdateValue.set(time)); + } + } + + /** + * Resets the batch expire time to the messaging system default value. + * + * @return this builder for use in a chained invocation + */ + public Builder unsetExpireAt() { + return this.expireAt(UpdateValue.unset()); + } + + /** + * Updates the batch expire time. If given a null reference then this is equivalent + * to calling {@link #unsetExpireAt()}. + * + * @param time the new expire time + * @return this builder for use in a chained invocation + */ + public Builder expireAt(OffsetDateTime time) { + if (time == null) { + return this.unsetExpireAt(); + } else { + return this.expireAt(UpdateValue.set(time)); + } + } + + /** + * Resets the callback URL to the default callback URL. + * + * @return this builder for use in a chained invocation + */ + public Builder unsetCallbackUrl() { + return this.callbackUrl(UpdateValue.unset()); + } + + /** + * Updates the callback URL. If given a null reference then this is equivalent to + * calling {@link #unsetCallbackUrl()}. + * + * @param url the new callback URL + * @return this builder for use in a chained invocation + */ + public Builder callbackUrl(URI url) { + if (url == null) { + return this.unsetCallbackUrl(); + } else { + return this.callbackUrl(UpdateValue.set(url)); + } + } + + /** + * Unsets the batch parameters. + * + * @return this builder for use in a chained invocation + */ + public Builder unsetParameters() { + return this.parameters(UpdateValue.>unset()); + } + + /** + * Replaces the batch parameters to the ones in the given map. If given a null + * reference then this is equivalent to calling {@link #unsetParameters()}. + * + * @param params the new parameter mapping + * @return this builder for use in a chained invocation + */ + public Builder parameters(Map params) { + if (params == null) { + return this.unsetParameters(); + } else { + return this.parameters(UpdateValue.set(params)); + } + } + } + + /** + * Creates a builder of {@link MtBatchMmsUpdate} instances. + * + * @return a builder + */ + @Nonnull + public static final MtBatchMmsUpdate.Builder builder() { + return new Builder(); + } + + /** + * The updated batch message body. + * + *

See {@link MtBatchMmsCreate#body()} for a more thorough description of this field. + * + * @return the batch message body; null if left unchanged + */ + @Nullable + public abstract MediaBody body(); + + /** + * Updates the strict validation flag. + * + * @return boolean indicating if strict validation is meant to be performed + */ + @Nullable + @JsonProperty("strict_validation") + public abstract Boolean strictValidation(); + + /** + * Description of how to update the batch parameters. + * + * @return an update description + * @see MtBatchMmsCreate#parameters() + */ + @Nullable + public abstract UpdateValue> parameters(); +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchResult.java b/src/main/java/com/sinch/xms/api/MtBatchResult.java new file mode 100644 index 0000000..e156f1f --- /dev/null +++ b/src/main/java/com/sinch/xms/api/MtBatchResult.java @@ -0,0 +1,150 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.List; +import javax.annotation.Nullable; + +/** Base class for batch description classes. */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @Type(MtBatchTextSmsResult.class), + @Type(MtBatchBinarySmsResult.class), + @Type(MtBatchMmsResult.class) +}) +public abstract class MtBatchResult { + + MtBatchResult() { + // Intentionally left empty. + } + + /** + * The unique batch identifier. This identifier can be used to, for example fetch a delivery + * reports and update or cancel the batch. + * + * @return a batch identifier + */ + public abstract BatchId id(); + + /** + * The list of message recipients. May not be empty. + * + * @return a non-empty list of recipients + */ + @JsonProperty("to") + public abstract List recipients(); + + /** + * The message originator. May be an MSISDN or short code. + * + * @return an originator address + */ + @JsonProperty("from") + @Nullable + public abstract String sender(); + + /** + * The type of delivery report used for this batch. + * + * @return a type of report, ReportType.NONE if not provided + */ + @JsonProperty("delivery_report") + public abstract ReportType deliveryReport(); + + /** + * The URL to which batch callbacks are sent. If null then callbacks will be sent to + * the default URL. + * + * @return an URL or null if the default callback URL is used + */ + @Nullable + public abstract URI callbackUrl(); + + /** + * The scheduled time this batch will be sent. If null or set to a past time then the + * batch is sent immediately. + * + * @return the time when this batch will be sent + */ + @Nullable + @JsonProperty("send_at") + public abstract OffsetDateTime sendAt(); + + /** + * The time when this batch will expire. Any message not delivered by this time will be placed + * into an expired state and no further delivery will be attempted. + * + * @return the time when this batch expires + */ + @Nullable + @JsonProperty("expire_at") + public abstract OffsetDateTime expireAt(); + + /** + * The time when this batch was created. + * + * @return the time when this batch was created + */ + @Nullable + @JsonProperty("created_at") + public abstract OffsetDateTime createdAt(); + + /** + * The time when this batch was last modified. + * + * @return the time when this batch was last modified + */ + @Nullable + @JsonProperty("modified_at") + public abstract OffsetDateTime modifiedAt(); + + /** + * Whether this batch has been canceled. + * + * @return true if the batch is canceled; false otherwise + */ + public abstract boolean canceled(); + + /** + * The client identifier to attach to this message. If set, it will be added in the delivery + * report/callback of this batch. + * + * @return a client reference id + */ + @Nullable + @JsonProperty("client_reference") + public abstract String clientReference(); + + /** + * Send feedback if your system can confirm successful message delivery. Feedback can only be + * provided if feedback_enabled was set when batch was submitted. + * + * @return boolean indicating if feedback is enabled + */ + @Nullable + @JsonProperty("feedback_enabled") + public abstract Boolean feedbackEnabled(); +} diff --git a/src/main/java/com/sinch/xms/api/MtBatchSmsCreate.java b/src/main/java/com/sinch/xms/api/MtBatchSmsCreate.java index 7fd383e..4560c36 100644 --- a/src/main/java/com/sinch/xms/api/MtBatchSmsCreate.java +++ b/src/main/java/com/sinch/xms/api/MtBatchSmsCreate.java @@ -19,113 +19,20 @@ */ package com.sinch.xms.api; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Set; import javax.annotation.Nullable; import javax.annotation.OverridingMethodsMustInvokeSuper; import org.immutables.value.Value; /** - * Base class for mobile terminated batch messages. A mobile terminated message can have either a - * {@link MtBatchTextSmsCreate textual} or a {@link MtBatchBinarySmsCreate binary} message payload. + * Base class for mobile terminated SMS batch messages. A mobile terminated message can have either + * a {@link MtBatchTextSmsCreate textual} or a {@link MtBatchBinarySmsCreate binary} message + * payload. */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({@Type(MtBatchTextSmsCreate.class), @Type(MtBatchBinarySmsCreate.class)}) -public abstract class MtBatchSmsCreate { - - /** - * The list of message recipients. May not be empty. - * - * @return a non-empty list of recipients - */ - @JsonProperty("to") - public abstract List recipients(); - - /** - * The message originator. May be an MSISDN or short code. - * - * @return an originator address - */ - @Nullable - @JsonProperty("from") - public abstract String sender(); - - /** - * The type of delivery report to request for this batch. - * - * @return a type of report or null to use the default type - */ - @Nullable - @JsonProperty("delivery_report") - public abstract ReportType deliveryReport(); - - /** - * The time this batch should be sent. If null or set to a past time then the batch - * will be sent immediately. - * - * @return the time when this batch should be sent - */ - @Nullable - @JsonProperty("send_at") - public abstract OffsetDateTime sendAt(); - - /** - * The time at which this batch will expire. Any message not delivered by this time will be placed - * into an expired state and no further delivery will be attempted. - * - * @return the time when this batch expires - */ - @Nullable - @JsonProperty("expire_at") - public abstract OffsetDateTime expireAt(); - - /** - * The URL to which batch callbacks should be sent. If null then callbacks will be - * sent to the default URL. - * - * @return an URL having a callback listener or null to use the default callback URL - */ - @Nullable - @JsonProperty("callback_url") - public abstract URI callbackUrl(); - - /** - * Send feedback if your system can confirm successful message delivery. Feedback can only be - * provided if feedback_enabled was set when batch was submitted. - * - * @return boolean value - */ - @Nullable - @JsonProperty("feedback_enabled") - public abstract Boolean feedbackEnabled(); - - /** - * The tags that should be attached to this message. - * - * @return a non-null set of tags - * @deprecated client reference should be used instead - */ - @Deprecated - @JsonInclude(Include.NON_EMPTY) - public abstract Set tags(); - - /** - * The client identifier to attach to this message. If set, it will be added in the delivery - * report/callback of this batch. - * - * @return a client reference id - */ - @Nullable - @JsonProperty("client_reference") - public abstract String clientReference(); +public abstract class MtBatchSmsCreate extends MtBatchCreate { /** * Shows message on screen without user interaction while not saving the message to the inbox. diff --git a/src/main/java/com/sinch/xms/api/MtBatchSmsResult.java b/src/main/java/com/sinch/xms/api/MtBatchSmsResult.java index 15656f1..7b4438a 100644 --- a/src/main/java/com/sinch/xms/api/MtBatchSmsResult.java +++ b/src/main/java/com/sinch/xms/api/MtBatchSmsResult.java @@ -20,123 +20,18 @@ package com.sinch.xms.api; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.List; import javax.annotation.Nullable; /** * Base class for batch description classes. This contains the fields common for both {@link * MtBatchTextSmsResult textual} and {@link MtBatchBinarySmsResult binary} batches. */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") -@JsonSubTypes({@Type(MtBatchTextSmsResult.class), @Type(MtBatchBinarySmsResult.class)}) -public abstract class MtBatchSmsResult { +public abstract class MtBatchSmsResult extends MtBatchResult { MtBatchSmsResult() { // Intentionally left empty. } - /** - * The unique batch identifier. This identifier can be used to, for example fetch a delivery - * reports and update or cancel the batch. - * - * @return a batch identifier - */ - public abstract BatchId id(); - - /** - * The list of message recipients. May not be empty. - * - * @return a non-empty list of recipients - */ - @JsonProperty("to") - public abstract List recipients(); - - /** - * The message originator. May be an MSISDN or short code. - * - * @return an originator address - */ - @JsonProperty("from") - @Nullable - public abstract String sender(); - - /** - * The type of delivery report used for this batch. - * - * @return a type of report, ReportType.NONE if not provided - */ - @JsonProperty("delivery_report") - public abstract ReportType deliveryReport(); - - /** - * The URL to which batch callbacks are sent. If null then callbacks will be sent to - * the default URL. - * - * @return an URL or null if the default callback URL is used - */ - @Nullable - public abstract URI callbackUrl(); - - /** - * The scheduled time this batch will be sent. If null or set to a past time then the - * batch is sent immediately. - * - * @return the time when this batch will be sent - */ - @Nullable - @JsonProperty("send_at") - public abstract OffsetDateTime sendAt(); - - /** - * The time when this batch will expire. Any message not delivered by this time will be placed - * into an expired state and no further delivery will be attempted. - * - * @return the time when this batch expires - */ - @Nullable - @JsonProperty("expire_at") - public abstract OffsetDateTime expireAt(); - - /** - * The time when this batch was created. - * - * @return the time when this batch was created - */ - @Nullable - @JsonProperty("created_at") - public abstract OffsetDateTime createdAt(); - - /** - * The time when this batch was last modified. - * - * @return the time when this batch was last modified - */ - @Nullable - @JsonProperty("modified_at") - public abstract OffsetDateTime modifiedAt(); - - /** - * Whether this batch has been canceled. - * - * @return true if the batch is canceled; false otherwise - */ - public abstract boolean canceled(); - - /** - * The client identifier to attach to this message. If set, it will be added in the delivery - * report/callback of this batch. - * - * @return a client reference id - */ - @Nullable - @JsonProperty("client_reference") - public abstract String clientReference(); - /** * Shows message on screen without user interaction while not saving the message to the inbox. * @@ -145,16 +40,6 @@ public abstract class MtBatchSmsResult { @JsonProperty("flash_message") public abstract boolean flashMessage(); - /** - * Send feedback if your system can confirm successful message delivery. Feedback can only be - * provided if feedback_enabled was set when batch was submitted. - * - * @return boolean indicating if feedback is enabled - */ - @Nullable - @JsonProperty("feedback_enabled") - public abstract Boolean feedbackEnabled(); - /** * Message will be dispatched only if it is not split to more parts than Max Number of Message * Parts. diff --git a/src/main/java/com/sinch/xms/api/MtBatchSmsUpdate.java b/src/main/java/com/sinch/xms/api/MtBatchSmsUpdate.java index 944938e..537c9a5 100644 --- a/src/main/java/com/sinch/xms/api/MtBatchSmsUpdate.java +++ b/src/main/java/com/sinch/xms/api/MtBatchSmsUpdate.java @@ -23,92 +23,70 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.sinch.xms.UpdateValue; -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.List; import javax.annotation.Nullable; -import javax.annotation.OverridingMethodsMustInvokeSuper; -import org.immutables.value.Value; -/** Objects of this type can be used to update previously submitted MT batches. */ +/** Objects of this type can be used to update previously submitted MT SMS batches. */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({@Type(MtBatchTextSmsUpdate.class), @Type(MtBatchBinarySmsUpdate.class)}) -public abstract class MtBatchSmsUpdate { +public abstract class MtBatchSmsUpdate extends MtBatchUpdate { /** - * The message destinations to add to the batch. + * Shows message on screen without user interaction while not saving the message to the inbox. + * Defaults to false. * - * @return a list of MSISDNs or group IDs + * @return boolean indicating if it's a flash message */ @Nullable - @JsonProperty("to_add") - public abstract List recipientInsertions(); + @JsonProperty("flash_message") + public abstract Boolean flashMessage(); /** - * The message destinations to remove from the batch. + * Message will be dispatched only if it is not split to more parts than Max Number of Message + * Parts. * - * @return a list of MSISDNs or group IDs + * @return the maximum allowed number of message parts */ @Nullable - @JsonProperty("to_remove") - public abstract List recipientRemovals(); + @JsonProperty("max_number_of_message_parts") + public abstract Integer maxNumberOfMessageParts(); /** - * The message originator. + * The DLT principal entity identifier to attach to this message. * - * @return an MSISDN or short code + * @return a principal entity id */ @Nullable - @JsonProperty("from") - public abstract String sender(); + @JsonProperty("dlt_principal_entity_id") + public abstract String dltPrincipalEntity(); /** - * Description of how to update the batch delivery report value. + * The DLT template identifier to attach to this message. * - * @return an update description - * @see MtBatchSmsCreate#deliveryReport() + * @return a template id */ @Nullable - @JsonProperty("delivery_report") - public abstract UpdateValue deliveryReport(); + @JsonProperty("dlt_template_id") + public abstract String dltTemplateId(); /** - * Description of how to update the batch send at value. + * The type of number of the message originator. Valid values are 0 to 6. This is optional and + * used for overriding the automatic detection of type of number. If provided then from_npi must + * also be set. * - * @return an update description - * @see MtBatchSmsCreate#sendAt() + * @return the type of number for the originator address */ + @JsonProperty("from_ton") @Nullable - @JsonProperty("send_at") - public abstract UpdateValue sendAt(); + public abstract Integer senderTon(); /** - * Description of how to update the batch expire at value. + * The numbering plan identification of the message originator. Valid values are 0 to 18. This is + * optional and used for overriding the automatic detection. If provided then from_ton must also + * be set. * - * @return an update description - * @see MtBatchSmsCreate#expireAt() + * @return the numbering plan identification for the originator address */ + @JsonProperty("from_npi") @Nullable - @JsonProperty("expire_at") - public abstract UpdateValue expireAt(); - - /** - * Description of how to update the batch callback URL. - * - * @return an update description - * @see MtBatchSmsCreate#callbackUrl() - */ - @Nullable - @JsonProperty("callback_url") - public abstract UpdateValue callbackUrl(); - - /** Validates that this object is in a correct state. */ - @OverridingMethodsMustInvokeSuper - @Value.Check - protected void check() { - if (sender() != null && sender().isEmpty()) { - throw new IllegalStateException("empty from address"); - } - } + public abstract Integer senderNpi(); } diff --git a/src/main/java/com/sinch/xms/api/MtBatchTextSmsCreate.java b/src/main/java/com/sinch/xms/api/MtBatchTextSmsCreate.java index 930352d..3ae81fb 100644 --- a/src/main/java/com/sinch/xms/api/MtBatchTextSmsCreate.java +++ b/src/main/java/com/sinch/xms/api/MtBatchTextSmsCreate.java @@ -73,13 +73,13 @@ public static final MtBatchTextSmsCreate.Builder builder() { *

The typical way to use templates is * *

-   * ClxApi.batchTextSms()
+   * SinchSMSApi.batchTextSms()
    *     .sender("12345")
    *     .addRecipient("987654321")
    *     // Other initialization
    *     .body("Hello, ${name}")
    *     .putParameter("name",
-   *         ClxApi.parameterValues()
+   *         SinchSMSApi.parameterValues()
    *             .putSubstitution("987654321", "Jane")
    *             .default("valued customer")
    *             .build())
diff --git a/src/main/java/com/sinch/xms/api/MtBatchUpdate.java b/src/main/java/com/sinch/xms/api/MtBatchUpdate.java
new file mode 100644
index 0000000..b3fa9d4
--- /dev/null
+++ b/src/main/java/com/sinch/xms/api/MtBatchUpdate.java
@@ -0,0 +1,130 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.sinch.xms.UpdateValue;
+import java.net.URI;
+import java.time.OffsetDateTime;
+import java.util.List;
+import javax.annotation.Nullable;
+import javax.annotation.OverridingMethodsMustInvokeSuper;
+import org.immutables.value.Value;
+
+/** Base class for updating previously submitted MT batches. */
+public abstract class MtBatchUpdate {
+
+  /**
+   * The message destinations to add to the batch.
+   *
+   * @return a list of MSISDNs or group IDs
+   */
+  @Nullable
+  @JsonProperty("to_add")
+  public abstract List recipientInsertions();
+
+  /**
+   * The message destinations to remove from the batch.
+   *
+   * @return a list of MSISDNs or group IDs
+   */
+  @Nullable
+  @JsonProperty("to_remove")
+  public abstract List recipientRemovals();
+
+  /**
+   * The message originator.
+   *
+   * @return an MSISDN or short code
+   */
+  @Nullable
+  @JsonProperty("from")
+  public abstract String sender();
+
+  /**
+   * Description of how to update the batch delivery report value.
+   *
+   * @return an update description
+   * @see MtBatchCreate#deliveryReport()
+   */
+  @Nullable
+  @JsonProperty("delivery_report")
+  public abstract UpdateValue deliveryReport();
+
+  /**
+   * Description of how to update the batch send at value.
+   *
+   * @return an update description
+   * @see MtBatchCreate#sendAt()
+   */
+  @Nullable
+  @JsonProperty("send_at")
+  public abstract UpdateValue sendAt();
+
+  /**
+   * Description of how to update the batch expire at value.
+   *
+   * @return an update description
+   * @see MtBatchCreate#expireAt()
+   */
+  @Nullable
+  @JsonProperty("expire_at")
+  public abstract UpdateValue expireAt();
+
+  /**
+   * Description of how to update the batch callback URL.
+   *
+   * @return an update description
+   * @see MtBatchCreate#callbackUrl()
+   */
+  @Nullable
+  @JsonProperty("callback_url")
+  public abstract UpdateValue callbackUrl();
+
+  /**
+   * If set to true, then feedback is expected after successful delivery.
+   *
+   * 

Defaults to false. + * + * @return boolean value + */ + @Nullable + @JsonProperty("feedback_enabled") + public abstract Boolean feedbackEnabled(); + + /** + * The client identifier to attach to this message. If set, it will be added in the delivery + * report/callback of this batch. + * + * @return a client reference id + */ + @Nullable + @JsonProperty("client_reference") + public abstract String clientReference(); + + /** Validates that this object is in a correct state. */ + @OverridingMethodsMustInvokeSuper + @Value.Check + protected void check() { + if (sender() != null && sender().isEmpty()) { + throw new IllegalStateException("empty from address"); + } + } +} diff --git a/src/main/java/com/sinch/xms/api/PagedBatchResult.java b/src/main/java/com/sinch/xms/api/PagedBatchResult.java index ef0c50d..f7fb83b 100644 --- a/src/main/java/com/sinch/xms/api/PagedBatchResult.java +++ b/src/main/java/com/sinch/xms/api/PagedBatchResult.java @@ -29,7 +29,7 @@ @Value.Immutable @ValueStylePackage @JsonDeserialize(builder = PagedBatchResult.Builder.class) -public abstract class PagedBatchResult extends Page { +public abstract class PagedBatchResult extends Page { /** A builder of batch result pages. */ public static class Builder extends PagedBatchResultImpl.Builder { @@ -49,5 +49,5 @@ public static final PagedBatchResult.Builder builder() { @JsonProperty("batches") @Override - public abstract List content(); + public abstract List content(); } diff --git a/src/main/java/com/sinch/xms/api/RecipientDeliveryReport.java b/src/main/java/com/sinch/xms/api/RecipientDeliveryReport.java index 7c0fe7b..31b63ad 100644 --- a/src/main/java/com/sinch/xms/api/RecipientDeliveryReport.java +++ b/src/main/java/com/sinch/xms/api/RecipientDeliveryReport.java @@ -20,39 +20,18 @@ package com.sinch.xms.api; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.time.OffsetDateTime; -import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.immutables.value.Value; /** Representation of a delivery report for a specific recipient. */ -@Value.Immutable -@ValueStylePackage -@JsonDeserialize(builder = RecipientDeliveryReport.Builder.class) @JsonTypeInfo(use = Id.NAME, property = "type") -@JsonTypeName("recipient_delivery_report_sms") +@JsonSubTypes({@Type(RecipientDeliveryReportSms.class), @Type(RecipientDeliveryReportMms.class)}) public abstract class RecipientDeliveryReport { - /** A builder of recipient delivery reports. */ - public static final class Builder extends RecipientDeliveryReportImpl.Builder { - - Builder() {} - } - - /** - * Creates a builder of {@link RecipientDeliveryReport} instances. - * - * @return a builder - */ - @Nonnull - public static final RecipientDeliveryReport.Builder builder() { - return new Builder(); - } - /** * The batch to which this delivery report belongs * @@ -127,8 +106,4 @@ public static final RecipientDeliveryReport.Builder builder() { @Nullable @JsonProperty("encoding") public abstract String encoding(); - - @Nullable - @JsonProperty("number_of_message_parts") - public abstract Integer numberOfMessageParts(); } diff --git a/src/main/java/com/sinch/xms/api/RecipientDeliveryReportMms.java b/src/main/java/com/sinch/xms/api/RecipientDeliveryReportMms.java new file mode 100644 index 0000000..3996c95 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/RecipientDeliveryReportMms.java @@ -0,0 +1,52 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.annotation.Nonnull; +import org.immutables.value.Value; + +/** Representation of an MMS delivery report for a specific recipient. */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = RecipientDeliveryReportMms.Builder.class) +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonTypeName("recipient_delivery_report_mms") +public abstract class RecipientDeliveryReportMms extends RecipientDeliveryReport { + + /** A builder of recipient delivery reports. */ + public static final class Builder extends RecipientDeliveryReportMmsImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link RecipientDeliveryReportMms} instances. + * + * @return a builder + */ + @Nonnull + public static final RecipientDeliveryReportMms.Builder builder() { + return new Builder(); + } +} diff --git a/src/main/java/com/sinch/xms/api/RecipientDeliveryReportSms.java b/src/main/java/com/sinch/xms/api/RecipientDeliveryReportSms.java new file mode 100644 index 0000000..4ccc5d2 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/RecipientDeliveryReportSms.java @@ -0,0 +1,58 @@ +/*- + * #%L + * SDK for Sinch SMS + * %% + * Copyright (C) 2016 Sinch + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.immutables.value.Value; + +/** Representation of an SMS delivery report for a specific recipient. */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = RecipientDeliveryReportSms.Builder.class) +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonTypeName("recipient_delivery_report_sms") +public abstract class RecipientDeliveryReportSms extends RecipientDeliveryReport { + + /** A builder of recipient delivery reports. */ + public static final class Builder extends RecipientDeliveryReportSmsImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link RecipientDeliveryReportSms} instances. + * + * @return a builder + */ + @Nonnull + public static final RecipientDeliveryReportSms.Builder builder() { + return new Builder(); + } + + @Nullable + @JsonProperty("number_of_message_parts") + public abstract Integer numberOfMessageParts(); +} diff --git a/src/main/java/com/sinch/xms/api/Status.java b/src/main/java/com/sinch/xms/api/Status.java new file mode 100644 index 0000000..76be470 --- /dev/null +++ b/src/main/java/com/sinch/xms/api/Status.java @@ -0,0 +1,61 @@ +package com.sinch.xms.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.List; +import javax.annotation.Nonnull; +import org.immutables.value.Value; + +/** A description of the messages having a given delivery state. */ +@Value.Immutable +@ValueStylePackage +@JsonDeserialize(builder = Status.Builder.class) +@JsonInclude(Include.NON_EMPTY) +public abstract class Status { + + /** A builder of batch delivery report statuses. */ + public static class Builder extends StatusImpl.Builder { + + Builder() {} + } + + /** + * Creates a builder of {@link Status} instances. + * + * @return a builder + */ + @Nonnull + public static final Status.Builder builder() { + return new Builder(); + } + + /** + * The delivery status code. + * + * @return a status code + */ + public abstract int code(); + + /** + * The delivery status for this bucket. + * + * @return a non-null delivery status + */ + public abstract DeliveryStatus status(); + + /** + * The number of individual messages in this status bucket. + * + * @return a positive integer + */ + public abstract int count(); + + /** + * The recipients having this status. Note, this is non-empty only if a full delivery + * report has been requested. + * + * @return a non-null list of recipients + */ + public abstract List recipients(); +} diff --git a/src/main/java/com/sinch/xms/package-info.java b/src/main/java/com/sinch/xms/package-info.java index 2a4ac4a..135b49b 100644 --- a/src/main/java/com/sinch/xms/package-info.java +++ b/src/main/java/com/sinch/xms/package-info.java @@ -20,7 +20,7 @@ /** * This package contains a Java interface for the Sinch XMS API. This API is intended for managing - * and sending batches of SMS messages as well as receiving mobile originated messages. + * and sending batches of SMS and MMS messages as well as receiving mobile originated messages. * *

The typical use case of this library is to create a long lived {@link * com.sinch.xms.ApiConnection} object which is then used to submit batches and perform other @@ -30,9 +30,9 @@ * exits. * *

- * import com.clxcommunications.xms.ApiConnection;
- * import com.clxcommunications.xms.ClxApi;
- * import com.clxcommunications.xms.api.MtBatchTextSmsResult;
+ * import com.sinch.xms.ApiConnection;
+ * import com.sinch.xms.SinchSMSApi;
+ * import com.sinch.xms.api.MtBatchTextSmsResult;
  *
  * public class Example {
  *
@@ -44,7 +44,7 @@
  *
  *         try {
  *             MtBatchTextSmsResult result =
- *                     conn.createBatch(ClxApi.batchTextSms()
+ *                     conn.createBatch(SinchSMSApi.batchTextSms()
  *                             .sender("my short code")
  *                             .addRecipient("my destination")
  *                             .body("my message")
diff --git a/src/site/markdown/tutorial.md b/src/site/markdown/tutorial.md
index e18d609..ba0750f 100644
--- a/src/site/markdown/tutorial.md
+++ b/src/site/markdown/tutorial.md
@@ -4,7 +4,7 @@ The purpose of this document is to present the basic concepts of the Sinch HTTP
 
 ## HTTP REST Messaging API basics
 
-HTTP REST Messaging API is a REST API that is provided by Sinch for sending and receiving SMS messages. It also provides various other services supporting this task such as managing groups of recipients, tagging, and so forth.
+HTTP REST Messaging API is a REST API that is provided by Sinch for sending and receiving SMS and MMS messages. It also provides various other services supporting this task such as managing groups of recipients, tagging, and so forth.
 
 Note, for brevity we will in this document refer to HTTP REST Messaging API as _XMS API_ and the HTTP REST Messaging API service or HTTP endpoint as _XMS_.
 
@@ -91,7 +91,7 @@ MtBatchTextSmsResult result =
 
 You will notice a few things with this code. We are using a `conn` variable that corresponds to an API connection that we assume has been previously created. We are calling the `createBatch` method on the connection with a single argument that describes the batch we wish to create.
 
-Describing the batch is done using an object satisfying the `MtBatchTextSmsCreate` interface. Such objects can most easily be created using the builder returned by `ClxApi.batchTextSms()`. For a batch with a binary body you would similarly describe it using a object satisfying the `MtBatchBinarySmsCreate` interface and typically use `ClxApi.batchBinarySms()` to build such objects.
+Describing the batch is done using an object satisfying the `MtBatchTextSmsCreate` interface. Such objects can most easily be created using the builder returned by `SinchSMSApi.batchTextSms()`. For a batch with a binary body you would similarly describe it using a object satisfying the `MtBatchBinarySmsCreate` interface and typically use `SinchSMSApi.batchBinarySms()` to build such objects.
 
 The return value in this case is a `MtBatchTextSmsResult` object that contains not only the submitted batch information but also information included by XMS, such that the unique batch identifier, the creation time, etc. For example, to simply print the batch identifier we could add the code
 
@@ -140,7 +140,7 @@ MtBatchTextSmsResult result =
         .addRecipient("987654321", "123456789", "555555555")
         .body("Hello, ${name}!")
         .putParameter("name",
-            ClxApi.parameterValues()
+            SinchSMSApi.parameterValues()
                 .putSubstitution("987654321", "Mary")
                 .putSubstitution("123456789", "Joe")
                 .defaultValue("valued customer")
@@ -265,7 +265,7 @@ try {
 
 For most typical use cases the users of the XMS SDK do not have to worry about its internals. However, for some specialized cases the SDK does allow deep access. In particular, if you have special needs concerning the way the SDK does HTTP traffic you can tell the SDK in the API connection to use a Apache HTTP Async Client of your choice. Do note, however, that in such cases the SDK assumes the client is started up and torn down externally to the API connection.
 
-For example, consider a use case where you have two XMS service plans and you wish them to simultaneously interact with XMS from the same application. It may in this case be beneficial to maintain a single connection pool towards XMS for both plans. This requires creating a suitable instance of the [`HttpAsyncClient`](https://hc.apache.org/httpcomponents-asyncclient-dev/httpasyncclient/apidocs/org/apache/http/nio/client/HttpAsyncClient.html) class and using it when initializing the API connection. Note, the XMS SDK provides a concrete client class with a suitable configuration for XMS called [`ApiHttpAsyncClient`](apidocs/index.html?com/clxcommunications/xms/ApiHttpAsyncClient.html).
+For example, consider a use case where you have two XMS service plans and you wish them to simultaneously interact with XMS from the same application. It may in this case be beneficial to maintain a single connection pool towards XMS for both plans. This requires creating a suitable instance of the [`HttpAsyncClient`](https://hc.apache.org/httpcomponents-asyncclient-dev/httpasyncclient/apidocs/org/apache/http/nio/client/HttpAsyncClient.html) class and using it when initializing the API connection. Note, the XMS SDK provides a concrete client class with a suitable configuration for XMS called [`ApiHttpAsyncClient`](apidocs/index.html?com/sinch/xms/ApiHttpAsyncClient.html).
 
 Thus, sharing the default HTTP client between two connections may in practice be accomplished with code such at the following.
 
diff --git a/src/test/java/com/sinch/xms/ApiConnectionIT.java b/src/test/java/com/sinch/xms/ApiConnectionIT.java
index d34d2a7..3fd2925 100644
--- a/src/test/java/com/sinch/xms/ApiConnectionIT.java
+++ b/src/test/java/com/sinch/xms/ApiConnectionIT.java
@@ -47,6 +47,7 @@
 import com.sinch.testsupport.TestUtils;
 import com.sinch.xms.api.ApiError;
 import com.sinch.xms.api.BatchDeliveryReport;
+import com.sinch.xms.api.BatchDeliveryReportSms;
 import com.sinch.xms.api.BatchId;
 import com.sinch.xms.api.DeliveryStatus;
 import com.sinch.xms.api.FeedbackDeliveryCreate;
@@ -61,6 +62,7 @@
 import com.sinch.xms.api.MtBatchBinarySmsResult;
 import com.sinch.xms.api.MtBatchBinarySmsUpdate;
 import com.sinch.xms.api.MtBatchDryRunResult;
+import com.sinch.xms.api.MtBatchResult;
 import com.sinch.xms.api.MtBatchSmsCreate;
 import com.sinch.xms.api.MtBatchSmsResult;
 import com.sinch.xms.api.MtBatchTextSmsCreate;
@@ -72,7 +74,9 @@
 import com.sinch.xms.api.PagedGroupResult;
 import com.sinch.xms.api.PagedInboundsResult;
 import com.sinch.xms.api.RecipientDeliveryReport;
+import com.sinch.xms.api.RecipientDeliveryReportSms;
 import com.sinch.xms.api.ReportType;
+import com.sinch.xms.api.Status;
 import com.sinch.xms.api.Tags;
 import com.sinch.xms.api.TagsUpdate;
 import java.io.IOException;
@@ -757,7 +761,7 @@ public void canFetchTextBatch() throws Exception {
             .start();
 
     try {
-      MtBatchSmsResult actual = conn.fetchBatch(batchId);
+      MtBatchResult actual = conn.fetchBatch(batchId);
       assertThat(actual, is(expected));
     } finally {
       conn.close();
@@ -798,16 +802,16 @@ public void canFetchTextBatchAsync() throws Exception {
             .start();
 
     try {
-      FutureCallback testCallback =
-          new TestCallback() {
+      FutureCallback testCallback =
+          new TestCallback() {
 
             @Override
-            public void completed(MtBatchSmsResult result) {
+            public void completed(MtBatchResult result) {
               assertThat(result, is(expected));
             }
           };
 
-      MtBatchSmsResult actual = conn.fetchBatchAsync(batchId, testCallback).get();
+      MtBatchResult actual = conn.fetchBatchAsync(batchId, testCallback).get();
       assertThat(actual, is(expected));
     } finally {
       conn.close();
@@ -849,16 +853,16 @@ public void canFetchBinaryBatch() throws Exception {
             .start();
 
     try {
-      FutureCallback testCallback =
-          new TestCallback() {
+      FutureCallback testCallback =
+          new TestCallback() {
 
             @Override
-            public void completed(MtBatchSmsResult result) {
+            public void completed(MtBatchResult result) {
               assertThat(result, is(expected));
             }
           };
 
-      MtBatchSmsResult actual = conn.fetchBatchAsync(batchId, testCallback).get();
+      MtBatchResult actual = conn.fetchBatchAsync(batchId, testCallback).get();
       assertThat(actual, is(expected));
     } finally {
       conn.close();
@@ -921,8 +925,8 @@ public void canHandle404WhenFetchingBatchAsync() throws Exception {
             .endpoint("http://localhost:" + wm.port())
             .start();
 
-    FutureCallback callback =
-        new TestCallback() {
+    FutureCallback callback =
+        new TestCallback() {
 
           @Override
           public void failed(Exception e) {
@@ -977,8 +981,8 @@ public void canHandle500WhenFetchingBatch() throws Exception {
        */
       final CountDownLatch latch = new CountDownLatch(1);
 
-      FutureCallback testCallback =
-          new TestCallback() {
+      FutureCallback testCallback =
+          new TestCallback() {
 
             @Override
             public void failed(Exception exception) {
@@ -990,7 +994,7 @@ public void failed(Exception exception) {
             }
           };
 
-      Future future = conn.fetchBatchAsync(batchId, testCallback);
+      Future future = conn.fetchBatchAsync(batchId, testCallback);
 
       // Give plenty of time for the callback to be called.
       latch.await();
@@ -1072,7 +1076,7 @@ public void canCancelBatch() throws Exception {
             .start();
 
     try {
-      MtBatchSmsResult result = conn.cancelBatch(batchId);
+      MtBatchResult result = conn.cancelBatch(batchId);
       assertThat(result, is(expected));
     } finally {
       conn.close();
@@ -1145,14 +1149,14 @@ public void canCancelBatchConcurrently() throws Exception {
             .start();
 
     try {
-      final Queue results = new ConcurrentArrayQueue();
+      final Queue results = new ConcurrentArrayQueue();
       final CountDownLatch latch = new CountDownLatch(2);
 
-      FutureCallback callback =
-          new TestCallback() {
+      FutureCallback callback =
+          new TestCallback() {
 
             @Override
-            public void completed(MtBatchSmsResult result) {
+            public void completed(MtBatchResult result) {
               results.add(result);
               latch.countDown();
             }
@@ -1183,7 +1187,7 @@ public void canListBatchesWithEmpty() throws Exception {
     String path = "/v1/" + spid + "/batches?page=0";
     BatchFilter filter = SinchSMSApi.batchFilter().build();
 
-    final Page expected =
+    final Page expected =
         PagedBatchResult.builder().page(0).size(0).totalSize(0).build();
 
     stubGetResponse(expected, path);
@@ -1196,18 +1200,18 @@ public void canListBatchesWithEmpty() throws Exception {
             .start();
 
     try {
-      FutureCallback> testCallback =
-          new TestCallback>() {
+      FutureCallback> testCallback =
+          new TestCallback>() {
 
             @Override
-            public void completed(Page result) {
+            public void completed(Page result) {
               assertThat(result, is(expected));
             }
           };
 
-      PagedFetcher fetcher = conn.fetchBatches(filter);
+      PagedFetcher fetcher = conn.fetchBatches(filter);
 
-      Page actual = fetcher.fetchAsync(0, testCallback).get();
+      Page actual = fetcher.fetchAsync(0, testCallback).get();
       assertThat(actual, is(expected));
     } finally {
       conn.close();
@@ -1224,7 +1228,7 @@ public void canListBatchesWithTwoPages() throws Exception {
     // Prepare first page.
     String path1 = "/v1/" + spid + "/batches?page=0";
 
-    final Page expected1 =
+    final Page expected1 =
         PagedBatchResult.builder().page(0).size(0).totalSize(2).build();
 
     stubGetResponse(expected1, path1);
@@ -1232,7 +1236,7 @@ public void canListBatchesWithTwoPages() throws Exception {
     // Prepare second page.
     String path2 = "/v1/" + spid + "/batches?page=1";
 
-    final Page expected2 =
+    final Page expected2 =
         PagedBatchResult.builder().page(1).size(0).totalSize(2).build();
 
     stubGetResponse(expected2, path2);
@@ -1245,11 +1249,11 @@ public void canListBatchesWithTwoPages() throws Exception {
             .start();
 
     try {
-      FutureCallback> testCallback =
-          new TestCallback>() {
+      FutureCallback> testCallback =
+          new TestCallback>() {
 
             @Override
-            public void completed(Page result) {
+            public void completed(Page result) {
               switch (result.page()) {
                 case 0:
                   assertThat(result, is(expected1));
@@ -1263,12 +1267,12 @@ public void completed(Page result) {
             }
           };
 
-      PagedFetcher fetcher = conn.fetchBatches(filter);
+      PagedFetcher fetcher = conn.fetchBatches(filter);
 
-      Page actual1 = fetcher.fetchAsync(0, testCallback).get();
+      Page actual1 = fetcher.fetchAsync(0, testCallback).get();
       assertThat(actual1, is(expected1));
 
-      Page actual2 = fetcher.fetchAsync(1, testCallback).get();
+      Page actual2 = fetcher.fetchAsync(1, testCallback).get();
       assertThat(actual2, is(expected2));
     } finally {
       conn.close();
@@ -1286,7 +1290,7 @@ public void canIterateOverPages() throws Exception {
     // Prepare first page.
     String path1 = "/v1/" + spid + "/batches?page=0";
 
-    final Page expected1 =
+    final Page expected1 =
         PagedBatchResult.builder()
             .page(0)
             .size(1)
@@ -1307,7 +1311,7 @@ public void canIterateOverPages() throws Exception {
     // Prepare second page.
     String path2 = "/v1/" + spid + "/batches?page=1";
 
-    final Page expected2 =
+    final Page expected2 =
         PagedBatchResult.builder()
             .page(1)
             .size(2)
@@ -1343,15 +1347,15 @@ public void canIterateOverPages() throws Exception {
             .start();
 
     try {
-      PagedFetcher fetcher = conn.fetchBatches(filter);
+      PagedFetcher fetcher = conn.fetchBatches(filter);
 
-      List> actuals = new ArrayList>();
+      List> actuals = new ArrayList<>();
 
-      for (Page result : fetcher.pages()) {
+      for (Page result : fetcher.pages()) {
         actuals.add(result);
       }
 
-      List> expecteds = new ArrayList>();
+      List> expecteds = new ArrayList<>();
       expecteds.add(expected1);
       expecteds.add(expected2);
 
@@ -1372,7 +1376,7 @@ public void canIterateOverBatchesWithTwoPages() throws Exception {
     // Prepare first page.
     String path1 = "/v1/" + spid + "/batches?page=0";
 
-    final Page expected1 =
+    final Page expected1 =
         PagedBatchResult.builder()
             .page(0)
             .size(1)
@@ -1393,7 +1397,7 @@ public void canIterateOverBatchesWithTwoPages() throws Exception {
     // Prepare second page.
     String path2 = "/v1/" + spid + "/batches?page=1";
 
-    final Page expected2 =
+    final Page expected2 =
         PagedBatchResult.builder()
             .page(1)
             .size(2)
@@ -1429,15 +1433,15 @@ public void canIterateOverBatchesWithTwoPages() throws Exception {
             .start();
 
     try {
-      PagedFetcher fetcher = conn.fetchBatches(filter);
+      PagedFetcher fetcher = conn.fetchBatches(filter);
 
-      List actuals = new ArrayList();
+      List actuals = new ArrayList<>();
 
-      for (MtBatchSmsResult result : fetcher.elements()) {
+      for (MtBatchResult result : fetcher.elements()) {
         actuals.add(result);
       }
 
-      List expecteds = new ArrayList();
+      List expecteds = new ArrayList<>();
       expecteds.addAll(expected1.content());
       expecteds.addAll(expected2.content());
 
@@ -1463,19 +1467,19 @@ public void canFetchDeliveryReportSync() throws Exception {
             + "/delivery_report"
             + "?type=summary&status=Aborted%2CDelivered&code=200%2C300";
 
-    final BatchDeliveryReport expected =
-        BatchDeliveryReport.builder()
+    final BatchDeliveryReportSms expected =
+        BatchDeliveryReportSms.builder()
             .batchId(batchId)
             .totalMessageCount(1010)
             .addStatus(
-                BatchDeliveryReport.Status.builder()
+                Status.builder()
                     .code(200)
                     .status(DeliveryStatus.ABORTED)
                     .count(10)
                     .addRecipient("rec1", "rec2")
                     .build())
             .addStatus(
-                BatchDeliveryReport.Status.builder()
+                Status.builder()
                     .code(300)
                     .status(DeliveryStatus.DELIVERED)
                     .count(20)
@@ -1516,19 +1520,19 @@ public void canFetchDeliveryReportAsync() throws Exception {
 
     String path = "/v1/" + spid + "/batches/" + batchId + "/delivery_report?type=full";
 
-    final BatchDeliveryReport expected =
-        BatchDeliveryReport.builder()
+    final BatchDeliveryReportSms expected =
+        BatchDeliveryReportSms.builder()
             .batchId(batchId)
             .totalMessageCount(1010)
             .addStatus(
-                BatchDeliveryReport.Status.builder()
+                Status.builder()
                     .code(200)
                     .status(DeliveryStatus.ABORTED)
                     .count(10)
                     .addRecipient("rec1", "rec2")
                     .build())
             .addStatus(
-                BatchDeliveryReport.Status.builder()
+                Status.builder()
                     .code(300)
                     .status(DeliveryStatus.DELIVERED)
                     .count(20)
@@ -1575,8 +1579,8 @@ public void canFetchRecipientDeliveryReportSync() throws Exception {
 
     String path = "/v1/" + spid + "/batches/" + batchId + "/delivery_report/" + recipient;
 
-    final RecipientDeliveryReport expected =
-        RecipientDeliveryReport.builder()
+    final RecipientDeliveryReportSms expected =
+        RecipientDeliveryReportSms.builder()
             .batchId(batchId)
             .recipient(recipient)
             .code(200)
@@ -1615,8 +1619,8 @@ public void canFetchDeliveryReports() throws Exception {
 
     String path = "/v1/" + spid + "/delivery_reports?page=0";
 
-    final RecipientDeliveryReport expected1 =
-        RecipientDeliveryReport.builder()
+    final RecipientDeliveryReportSms expected1 =
+        RecipientDeliveryReportSms.builder()
             .batchId(batchId)
             .recipient(recipient)
             .code(200)
@@ -1664,7 +1668,7 @@ public void canFetchRecipientDeliveryReportAsync() throws Exception {
     String path = "/v1/" + spid + "/batches/" + batchId + "/delivery_report/" + recipient;
 
     final RecipientDeliveryReport expected =
-        RecipientDeliveryReport.builder()
+        RecipientDeliveryReportSms.builder()
             .batchId(batchId)
             .recipient(recipient)
             .code(200)
@@ -3450,7 +3454,6 @@ public void completed(MoSms result) {
    *
    * @param response the response to give, serialized to JSON
    * @param path the path on which to listen
-   * @param status the response HTTP status
    * @throws JsonProcessingException if the given response object could not be serialized
    */
   private void stubGetResponse(Object response, String path) throws JsonProcessingException {
diff --git a/src/test/java/com/sinch/xms/api/BatchDeliveryReportMmsTest.java b/src/test/java/com/sinch/xms/api/BatchDeliveryReportMmsTest.java
new file mode 100644
index 0000000..1fd4566
--- /dev/null
+++ b/src/test/java/com/sinch/xms/api/BatchDeliveryReportMmsTest.java
@@ -0,0 +1,146 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import com.pholser.junit.quickcheck.Property;
+import com.pholser.junit.quickcheck.runner.JUnitQuickcheck;
+import com.sinch.testsupport.TestUtils;
+import com.sinch.xms.ApiObjectMapper;
+import com.sinch.xms.Utils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(JUnitQuickcheck.class)
+public class BatchDeliveryReportMmsTest {
+
+  private final ApiObjectMapper json = new ApiObjectMapper();
+
+  @Test
+  public void canSerializeSummary() throws Exception {
+    BatchDeliveryReportMms input =
+        new BatchDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .totalMessageCount(50)
+            .addStatus(
+                new Status.Builder().code(10).status(DeliveryStatus.DELIVERED).count(20).build())
+            .addStatus(
+                new Status.Builder().code(20).status(DeliveryStatus.FAILED).count(30).build())
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'batch_id': 'batchid',",
+                "  'total_message_count': 50,",
+                "  'type': 'delivery_report_mms',",
+                "  'statuses': [",
+                "    {",
+                "      'code': 10,",
+                "      'status': 'Delivered',",
+                "      'count': 20",
+                "    },",
+                "    {",
+                "      'code': 20,",
+                "      'status': 'Failed',",
+                "      'count': 30",
+                "    }",
+                "  ]",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canSerializeFull() throws Exception {
+    BatchDeliveryReportMms input =
+        new BatchDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .totalMessageCount(50)
+            .addStatus(
+                new Status.Builder()
+                    .code(10)
+                    .status(DeliveryStatus.DELIVERED)
+                    .count(20)
+                    .addRecipient("to1", "to2")
+                    .build())
+            .addStatus(
+                new Status.Builder()
+                    .code(20)
+                    .status(DeliveryStatus.FAILED)
+                    .count(30)
+                    .addRecipient("to3", "to4", "to5")
+                    .build())
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'batch_id': 'batchid',",
+                "  'total_message_count': 50,",
+                "  'type': 'delivery_report_mms',",
+                "  'statuses': [",
+                "    {",
+                "      'code': 10,",
+                "      'status': 'Delivered',",
+                "      'count': 20,",
+                "      'recipients': [ 'to1', 'to2' ]",
+                "    },",
+                "    {",
+                "      'code': 20,",
+                "      'status': 'Failed',",
+                "      'count': 30,",
+                "      'recipients': [ 'to3', 'to4', 'to5' ]",
+                "    }",
+                "  ]",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Property
+  public void canDeserialize(BatchId batchId) throws Exception {
+    BatchDeliveryReportMms expected =
+        new BatchDeliveryReportMms.Builder()
+            .batchId(batchId)
+            .totalMessageCount(50)
+            .addStatus(
+                new Status.Builder().code(10).status(DeliveryStatus.DELIVERED).count(20).build())
+            .addStatus(
+                new Status.Builder().code(20).status(DeliveryStatus.FAILED).count(30).build())
+            .build();
+
+    String input = json.writeValueAsString(expected);
+
+    BatchDeliveryReportMms actual = json.readValue(input, BatchDeliveryReportMms.class);
+
+    assertThat(actual, is(expected));
+  }
+}
diff --git a/src/test/java/com/sinch/xms/api/BatchDeliveryReportTest.java b/src/test/java/com/sinch/xms/api/BatchDeliveryReportSmsTest.java
similarity index 75%
rename from src/test/java/com/sinch/xms/api/BatchDeliveryReportTest.java
rename to src/test/java/com/sinch/xms/api/BatchDeliveryReportSmsTest.java
index 6d58443..4cea1aa 100644
--- a/src/test/java/com/sinch/xms/api/BatchDeliveryReportTest.java
+++ b/src/test/java/com/sinch/xms/api/BatchDeliveryReportSmsTest.java
@@ -31,28 +31,20 @@
 import org.junit.runner.RunWith;
 
 @RunWith(JUnitQuickcheck.class)
-public class BatchDeliveryReportTest {
+public class BatchDeliveryReportSmsTest {
 
   private final ApiObjectMapper json = new ApiObjectMapper();
 
   @Test
   public void canSerializeSummary() throws Exception {
-    BatchDeliveryReport input =
-        new BatchDeliveryReport.Builder()
+    BatchDeliveryReportSms input =
+        new BatchDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .totalMessageCount(50)
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
-                    .code(10)
-                    .status(DeliveryStatus.DELIVERED)
-                    .count(20)
-                    .build())
+                new Status.Builder().code(10).status(DeliveryStatus.DELIVERED).count(20).build())
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
-                    .code(20)
-                    .status(DeliveryStatus.FAILED)
-                    .count(30)
-                    .build())
+                new Status.Builder().code(20).status(DeliveryStatus.FAILED).count(30).build())
             .build();
 
     String expected =
@@ -84,19 +76,19 @@ public void canSerializeSummary() throws Exception {
 
   @Test
   public void canSerializeFull() throws Exception {
-    BatchDeliveryReport input =
-        new BatchDeliveryReport.Builder()
+    BatchDeliveryReportSms input =
+        new BatchDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .totalMessageCount(50)
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
+                new Status.Builder()
                     .code(10)
                     .status(DeliveryStatus.DELIVERED)
                     .count(20)
                     .addRecipient("to1", "to2")
                     .build())
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
+                new Status.Builder()
                     .code(20)
                     .status(DeliveryStatus.FAILED)
                     .count(30)
@@ -135,27 +127,19 @@ public void canSerializeFull() throws Exception {
 
   @Property
   public void canDeserialize(BatchId batchId) throws Exception {
-    BatchDeliveryReport expected =
-        new BatchDeliveryReport.Builder()
+    BatchDeliveryReportSms expected =
+        new BatchDeliveryReportSms.Builder()
             .batchId(batchId)
             .totalMessageCount(50)
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
-                    .code(10)
-                    .status(DeliveryStatus.DELIVERED)
-                    .count(20)
-                    .build())
+                new Status.Builder().code(10).status(DeliveryStatus.DELIVERED).count(20).build())
             .addStatus(
-                new BatchDeliveryReport.Status.Builder()
-                    .code(20)
-                    .status(DeliveryStatus.FAILED)
-                    .count(30)
-                    .build())
+                new Status.Builder().code(20).status(DeliveryStatus.FAILED).count(30).build())
             .build();
 
     String input = json.writeValueAsString(expected);
 
-    BatchDeliveryReport actual = json.readValue(input, BatchDeliveryReport.class);
+    BatchDeliveryReportSms actual = json.readValue(input, BatchDeliveryReportSms.class);
 
     assertThat(actual, is(expected));
   }
diff --git a/src/test/java/com/sinch/xms/api/MtBatchMmsCreateTest.java b/src/test/java/com/sinch/xms/api/MtBatchMmsCreateTest.java
new file mode 100644
index 0000000..8d74436
--- /dev/null
+++ b/src/test/java/com/sinch/xms/api/MtBatchMmsCreateTest.java
@@ -0,0 +1,135 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import com.sinch.testsupport.TestUtils;
+import com.sinch.xms.ApiObjectMapper;
+import com.sinch.xms.SinchSMSApi;
+import com.sinch.xms.Utils;
+import org.junit.Test;
+
+public class MtBatchMmsCreateTest {
+
+  private final ApiObjectMapper json = new ApiObjectMapper();
+
+  @Test
+  public void canSerializeMinimal() throws Exception {
+    MtBatchMmsCreate input = minimalBatchBuilder().build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg'",
+                "  }",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canDeserializeMinimal() throws Exception {
+    MtBatchMmsCreate expected = minimalBatchBuilder().build();
+
+    String input = json.writeValueAsString(expected);
+
+    MtBatchMmsCreate actual = json.readValue(input, MtBatchMmsCreate.class);
+
+    assertThat(actual, is(expected));
+  }
+
+  @Test
+  public void canSerializeWithMessageContent() throws Exception {
+    MtBatchMmsCreate input =
+        SinchSMSApi.batchMms()
+            .sender("1234")
+            .addRecipient("987654321")
+            .body(
+                SinchSMSApi.mediaBody()
+                    .url("http://my.test.url/image.jpg")
+                    .message("the text")
+                    .build())
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg',",
+                "    'message':'the text'",
+                "  }",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canDeserializeWithMessageContent() throws Exception {
+    MtBatchMmsCreate expected =
+        SinchSMSApi.batchMms()
+            .sender("1234")
+            .addRecipient("987654321")
+            .body(
+                SinchSMSApi.mediaBody()
+                    .url("http://my.test.url/image.jpg")
+                    .message("the text")
+                    .build())
+            .build();
+
+    String input = json.writeValueAsString(expected);
+
+    MtBatchMmsCreate actual = json.readValue(input, MtBatchMmsCreate.class);
+
+    assertThat(actual, is(expected));
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void requiresUrl() {
+    SinchSMSApi.batchMms()
+        .sender("1234")
+        .addRecipient("987654321")
+        .body(SinchSMSApi.mediaBody().message("Hello, world!").build())
+        .build();
+  }
+
+  private static MtBatchMmsCreate.Builder minimalBatchBuilder() {
+    return SinchSMSApi.batchMms()
+        .sender("1234")
+        .addRecipient("987654321")
+        .body(SinchSMSApi.mediaBody().url("http://my.test.url/image.jpg").build());
+  }
+}
diff --git a/src/test/java/com/sinch/xms/api/MtBatchMmsResultTest.java b/src/test/java/com/sinch/xms/api/MtBatchMmsResultTest.java
new file mode 100644
index 0000000..b5566f4
--- /dev/null
+++ b/src/test/java/com/sinch/xms/api/MtBatchMmsResultTest.java
@@ -0,0 +1,147 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import com.sinch.testsupport.TestUtils;
+import com.sinch.xms.ApiObjectMapper;
+import com.sinch.xms.SinchSMSApi;
+import com.sinch.xms.Utils;
+import org.junit.Test;
+
+public class MtBatchMmsResultTest {
+
+  private final ApiObjectMapper json = new ApiObjectMapper();
+
+  @Test
+  public void canSerializeMinimal() throws Exception {
+    MtBatchMmsResult input = minimalBatchBuilder().build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg'",
+                "  },",
+                "  'canceled': false,",
+                "  'feedback_enabled': false,",
+                "  'delivery_report': 'none',",
+                "  'id': '" + input.id() + "'",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canDeserializeMinimal() throws Exception {
+    MtBatchMmsResult expected = minimalBatchBuilder().build();
+
+    String input = json.writeValueAsString(expected);
+
+    MtBatchMmsResult actual = json.readValue(input, MtBatchMmsResult.class);
+
+    assertThat(actual, is(expected));
+  }
+
+  @Test
+  public void canSerializeWithParameters() throws Exception {
+    MtBatchMmsResult input =
+        minimalBatchBuilder()
+            .body(
+                SinchSMSApi.mediaBody()
+                    .url("http://my.test.url/image.jpg")
+                    .message("the text")
+                    .build())
+            .putParameter(
+                "param1",
+                SinchSMSApi.parameterValues()
+                    .putSubstitution("123", "foo")
+                    .putSubstitution("234", "bar")
+                    .build())
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg',",
+                "    'message':'the text'",
+                "  },",
+                "  'canceled': false,",
+                "  'feedback_enabled': false,",
+                "  'delivery_report': 'none',",
+                "  'id': '" + input.id() + "',",
+                "  'parameters': {",
+                "    'param1': {",
+                "      '123': 'foo',",
+                "      '234': 'bar'",
+                "    }",
+                "  }",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canDeserializeWithParameters() throws Exception {
+    MtBatchMmsResult expected =
+        minimalBatchBuilder()
+            .putParameter(
+                "param1",
+                SinchSMSApi.parameterValues()
+                    .putSubstitution("123", "foo")
+                    .putSubstitution("234", "bar")
+                    .build())
+            .build();
+
+    String input = json.writeValueAsString(expected);
+
+    MtBatchMmsResult actual = json.readValue(input, MtBatchMmsResult.class);
+
+    assertThat(actual, is(expected));
+  }
+
+  private static MtBatchMmsResult.Builder minimalBatchBuilder() {
+    return new MtBatchMmsResult.Builder()
+        .sender("1234")
+        .addRecipient("987654321")
+        .feedbackEnabled(false)
+        .deliveryReport(ReportType.NONE)
+        .body(SinchSMSApi.mediaBody().url("http://my.test.url/image.jpg").build())
+        .canceled(false)
+        .id(TestUtils.freshBatchId());
+  }
+}
diff --git a/src/test/java/com/sinch/xms/api/MtBatchMmsUpdateTest.java b/src/test/java/com/sinch/xms/api/MtBatchMmsUpdateTest.java
new file mode 100644
index 0000000..6a8966f
--- /dev/null
+++ b/src/test/java/com/sinch/xms/api/MtBatchMmsUpdateTest.java
@@ -0,0 +1,115 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import com.sinch.testsupport.TestUtils;
+import com.sinch.xms.ApiObjectMapper;
+import com.sinch.xms.SinchSMSApi;
+import com.sinch.xms.UpdateValue;
+import com.sinch.xms.Utils;
+import java.util.Map;
+import java.util.TreeMap;
+import org.junit.Test;
+
+public class MtBatchMmsUpdateTest {
+
+  private final ApiObjectMapper json = new ApiObjectMapper();
+
+  @Test
+  public void canSerializeMinimal() throws Exception {
+    MtBatchMmsUpdate input = SinchSMSApi.batchMmsUpdate().build();
+
+    String expected = Utils.join("\n", "{", "  'type': 'mt_media'", "}").replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canSerializeWithUpdatedParameters() throws Exception {
+    Map params = new TreeMap();
+
+    params.put("newparam", SinchSMSApi.parameterValues().putSubstitution("key1", "value1").build());
+
+    MtBatchMmsUpdate input =
+        SinchSMSApi.batchMmsUpdate()
+            .sender("1234")
+            .addRecipientInsertion("987654321")
+            .body(
+                SinchSMSApi.mediaBody()
+                    .url("http://my.test.url/image.jpg")
+                    .message("the text")
+                    .build())
+            .parameters(UpdateValue.set(params))
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to_add': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg',",
+                "    'message':'the text'",
+                "  },",
+                "  'parameters': { 'newparam': { 'key1': 'value1' } }",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canSerializeWithUnsetParameters() throws Exception {
+    MtBatchMmsUpdate input =
+        SinchSMSApi.batchMmsUpdate()
+            .sender("1234")
+            .addRecipientInsertion("987654321")
+            .body(SinchSMSApi.mediaBody().url("http://my.test.url/image.jpg").build())
+            .unsetParameters()
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'type': 'mt_media',",
+                "  'from': '1234',",
+                "  'to_add': [ '987654321' ],",
+                "  'body': {",
+                "    'url':'http://my.test.url/image.jpg'",
+                "  },",
+                "  'parameters': null",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+}
diff --git a/src/test/java/com/sinch/xms/api/MtBatchTextSmsCreateTest.java b/src/test/java/com/sinch/xms/api/MtBatchTextSmsCreateTest.java
index 515f462..dd6c16d 100644
--- a/src/test/java/com/sinch/xms/api/MtBatchTextSmsCreateTest.java
+++ b/src/test/java/com/sinch/xms/api/MtBatchTextSmsCreateTest.java
@@ -175,12 +175,11 @@ public void canDeserializeWithParametersAndDefault() throws Exception {
   }
 
   @Test
-  public void canSerializeWithDatesAndTags() throws Exception {
+  public void canSerializeWithDates() throws Exception {
     MtBatchSmsCreate input =
         minimalBatchBuilder()
             .sendAt(OffsetDateTime.of(2016, 12, 1, 10, 20, 30, 0, ZoneOffset.UTC))
             .expireAt(OffsetDateTime.of(2016, 12, 20, 10, 0, 0, 0, ZoneOffset.UTC))
-            .addTag("tag1", "tag2")
             .build();
 
     String expected =
@@ -192,8 +191,7 @@ public void canSerializeWithDatesAndTags() throws Exception {
                 "  'to': [ '987654321' ],",
                 "  'body': 'Hello, world!',",
                 "  'send_at': '2016-12-01T10:20:30Z',",
-                "  'expire_at': '2016-12-20T10:00:00Z',",
-                "  'tags': ['tag1', 'tag2']",
+                "  'expire_at': '2016-12-20T10:00:00Z'",
                 "}")
             .replace('\'', '"');
 
diff --git a/src/test/java/com/sinch/xms/api/RecipientDeliveryReportMmsTest.java b/src/test/java/com/sinch/xms/api/RecipientDeliveryReportMmsTest.java
new file mode 100644
index 0000000..3dee8c0
--- /dev/null
+++ b/src/test/java/com/sinch/xms/api/RecipientDeliveryReportMmsTest.java
@@ -0,0 +1,153 @@
+/*-
+ * #%L
+ * SDK for Sinch SMS
+ * %%
+ * Copyright (C) 2016 Sinch
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.sinch.xms.api;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import com.sinch.testsupport.TestUtils;
+import com.sinch.xms.ApiObjectMapper;
+import com.sinch.xms.Utils;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import org.junit.Test;
+
+public class RecipientDeliveryReportMmsTest {
+
+  private final ApiObjectMapper json = new ApiObjectMapper();
+
+  @Test
+  public void canSerialize() throws Exception {
+    OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+    OffsetDateTime time2 = OffsetDateTime.of(2016, 11, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+
+    RecipientDeliveryReport input =
+        new RecipientDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .recipient("12345")
+            .code(10)
+            .status(DeliveryStatus.DELIVERED)
+            .statusMessage("status message")
+            .at(time1)
+            .operatorStatusAt(time2)
+            .operator("818181")
+            .encoding("GSM")
+            .clientReference("client_ref")
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'batch_id': 'batchid',",
+                "  'recipient': '12345',",
+                "  'type': 'recipient_delivery_report_mms',",
+                "  'code': 10,",
+                "  'status': 'Delivered',",
+                "  'status_message': 'status message',",
+                "  'operator': '818181',",
+                "  'at': '2016-10-02T09:34:28.542Z',",
+                "  'operator_status_at': '2016-11-02T09:34:28.542Z',",
+                "  'client_reference': 'client_ref',",
+                "  'encoding': 'GSM'",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canSerializeMandatoryFields() throws Exception {
+    OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+
+    RecipientDeliveryReport input =
+        new RecipientDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .recipient("12345")
+            .code(10)
+            .status(DeliveryStatus.DELIVERED)
+            .at(time1)
+            .build();
+
+    String expected =
+        Utils.join(
+                "\n",
+                "{",
+                "  'batch_id': 'batchid',",
+                "  'recipient': '12345',",
+                "  'type': 'recipient_delivery_report_mms',",
+                "  'code': 10,",
+                "  'status': 'Delivered',",
+                "  'at': '2016-10-02T09:34:28.542Z'",
+                "}")
+            .replace('\'', '"');
+
+    String actual = json.writeValueAsString(input);
+
+    assertThat(actual, is(TestUtils.jsonEqualTo(expected)));
+  }
+
+  @Test
+  public void canDeserialize() throws Exception {
+    OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+    OffsetDateTime time2 = OffsetDateTime.of(2016, 11, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+
+    RecipientDeliveryReport expected =
+        new RecipientDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .recipient("1235")
+            .code(10)
+            .status(DeliveryStatus.DELIVERED)
+            .statusMessage("status message")
+            .at(time1)
+            .operatorStatusAt(time2)
+            .encoding("GSM")
+            .clientReference("client_ref")
+            .build();
+
+    String input = json.writeValueAsString(expected);
+
+    RecipientDeliveryReportMms actual = json.readValue(input, RecipientDeliveryReportMms.class);
+
+    assertThat(actual, is(expected));
+  }
+
+  @Test
+  public void canDeserializeMandatoryFields() throws Exception {
+    OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
+
+    RecipientDeliveryReport expected =
+        new RecipientDeliveryReportMms.Builder()
+            .batchId(BatchId.of("batchid"))
+            .recipient("1235")
+            .code(10)
+            .status(DeliveryStatus.DELIVERED)
+            .at(time1)
+            .build();
+
+    String input = json.writeValueAsString(expected);
+
+    RecipientDeliveryReportMms actual = json.readValue(input, RecipientDeliveryReportMms.class);
+
+    assertThat(actual, is(expected));
+  }
+}
diff --git a/src/test/java/com/sinch/xms/api/RecipientDeliveryReportTest.java b/src/test/java/com/sinch/xms/api/RecipientDeliveryReportSmsTest.java
similarity index 90%
rename from src/test/java/com/sinch/xms/api/RecipientDeliveryReportTest.java
rename to src/test/java/com/sinch/xms/api/RecipientDeliveryReportSmsTest.java
index df30852..165921e 100644
--- a/src/test/java/com/sinch/xms/api/RecipientDeliveryReportTest.java
+++ b/src/test/java/com/sinch/xms/api/RecipientDeliveryReportSmsTest.java
@@ -29,7 +29,7 @@
 import java.time.ZoneOffset;
 import org.junit.Test;
 
-public class RecipientDeliveryReportTest {
+public class RecipientDeliveryReportSmsTest {
 
   private final ApiObjectMapper json = new ApiObjectMapper();
 
@@ -38,8 +38,8 @@ public void canSerialize() throws Exception {
     OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
     OffsetDateTime time2 = OffsetDateTime.of(2016, 11, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
 
-    RecipientDeliveryReport input =
-        new RecipientDeliveryReport.Builder()
+    RecipientDeliveryReportSms input =
+        new RecipientDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .recipient("12345")
             .code(10)
@@ -82,7 +82,7 @@ public void canSerializeMandatoryFields() throws Exception {
     OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
 
     RecipientDeliveryReport input =
-        new RecipientDeliveryReport.Builder()
+        new RecipientDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .recipient("12345")
             .code(10)
@@ -114,7 +114,7 @@ public void canDeserialize() throws Exception {
     OffsetDateTime time2 = OffsetDateTime.of(2016, 11, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
 
     RecipientDeliveryReport expected =
-        new RecipientDeliveryReport.Builder()
+        new RecipientDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .recipient("1235")
             .code(10)
@@ -129,7 +129,7 @@ public void canDeserialize() throws Exception {
 
     String input = json.writeValueAsString(expected);
 
-    RecipientDeliveryReport actual = json.readValue(input, RecipientDeliveryReport.class);
+    RecipientDeliveryReportSms actual = json.readValue(input, RecipientDeliveryReportSms.class);
 
     assertThat(actual, is(expected));
   }
@@ -139,7 +139,7 @@ public void canDeserializeMandatoryFields() throws Exception {
     OffsetDateTime time1 = OffsetDateTime.of(2016, 10, 2, 9, 34, 28, 542000000, ZoneOffset.UTC);
 
     RecipientDeliveryReport expected =
-        new RecipientDeliveryReport.Builder()
+        new RecipientDeliveryReportSms.Builder()
             .batchId(BatchId.of("batchid"))
             .recipient("1235")
             .code(10)
@@ -149,7 +149,7 @@ public void canDeserializeMandatoryFields() throws Exception {
 
     String input = json.writeValueAsString(expected);
 
-    RecipientDeliveryReport actual = json.readValue(input, RecipientDeliveryReport.class);
+    RecipientDeliveryReportSms actual = json.readValue(input, RecipientDeliveryReportSms.class);
 
     assertThat(actual, is(expected));
   }
diff --git a/suppression.xml b/suppression.xml
index 5aecb74..885d6d2 100644
--- a/suppression.xml
+++ b/suppression.xml
@@ -1,4 +1,9 @@
 
 
-
+  
+    
+    ^pkg:maven/com.fasterxml.jackson.core/jackson-databind@.*$
+    
+    CVE-2023-35116
+