From a38b69b7315e03ec316d67b4f1c935e91b20677e Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:28:25 +0100 Subject: [PATCH] SMS: webhooks (#5) * feat: WebHooks --- .../com/sinch/sdk/domains/sms/SMSService.java | 8 + .../sdk/domains/sms/WebHooksService.java | 103 ++++++++++++ .../sdk/domains/sms/adapters/SMSService.java | 10 ++ .../domains/sms/adapters/WebHooksService.java | 39 +++++ .../sms/models/DeliveryReportStatus.java | 85 ++++++++++ .../models/webhooks/BaseDeliveryReport.java | 72 +++++++++ .../sms/models/webhooks/BaseIncomingSMS.java | 98 ++++++++++++ .../sms/models/webhooks/DeliveryReport.java | 59 +++++++ .../models/webhooks/DeliveryReportMMS.java | 35 ++++ .../webhooks/DeliveryReportRecipient.java | 151 ++++++++++++++++++ .../webhooks/DeliveryReportRecipientMMS.java | 70 ++++++++ .../webhooks/DeliveryReportRecipientSMS.java | 70 ++++++++ .../models/webhooks/DeliveryReportSMS.java | 35 ++++ .../webhooks/DeliveryReportStatusDetails.java | 75 +++++++++ .../models/webhooks/IncomingSMSBinary.java | 58 +++++++ .../sms/models/webhooks/IncomingSMSText.java | 48 ++++++ .../sms/models/webhooks/package-info.java | 6 + .../sms/adapters/WebHKooksServiceTest.java | 134 ++++++++++++++++ .../sdk/core/exceptions/ApiException.java | 4 + .../core/exceptions/ApiMappingException.java | 8 + .../v1/webhooks/DeliveryReportDtoTest.java | 53 ++++++ .../DeliveryReportRecipientDtoTest.java | 62 +++++++ .../v1/webhooks/IncomingSMSBinaryDtoTest.java | 34 ++++ .../v1/webhooks/IncomingSMSTextDtoTest.java | 33 ++++ .../sample/sms/webhooks/DeliveryReport.java | 46 ++++++ .../sample/sms/webhooks/IncomingSMS.java | 40 +++++ .../sms/v1/webhooks/DeliveryReportMMS.json | 15 ++ .../webhooks/DeliveryReportRecipientMMS.json | 15 ++ .../webhooks/DeliveryReportRecipientSMS.json | 15 ++ .../sms/v1/webhooks/DeliveryReportSMS.json | 15 ++ .../sms/v1/webhooks/IncomingSMSBinary.json | 11 ++ .../sms/v1/webhooks/IncomingSMSText.json | 9 ++ 32 files changed, 1516 insertions(+) create mode 100644 client/src/main/com/sinch/sdk/domains/sms/WebHooksService.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/adapters/WebHooksService.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/DeliveryReportStatus.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseDeliveryReport.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseIncomingSMS.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReport.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportMMS.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipient.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientMMS.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientSMS.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportSMS.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportStatusDetails.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSBinary.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSText.java create mode 100644 client/src/main/com/sinch/sdk/domains/sms/models/webhooks/package-info.java create mode 100644 client/src/test/java/com/sinch/sdk/domains/sms/adapters/WebHKooksServiceTest.java create mode 100644 core/src/main/com/sinch/sdk/core/exceptions/ApiMappingException.java create mode 100644 openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportDtoTest.java create mode 100644 openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportRecipientDtoTest.java create mode 100644 openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSBinaryDtoTest.java create mode 100644 openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSTextDtoTest.java create mode 100644 sample-app/src/main/java/com/sinch/sample/sms/webhooks/DeliveryReport.java create mode 100644 sample-app/src/main/java/com/sinch/sample/sms/webhooks/IncomingSMS.java create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportMMS.json create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportSMS.json create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSBinary.json create mode 100644 test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSText.json diff --git a/client/src/main/com/sinch/sdk/domains/sms/SMSService.java b/client/src/main/com/sinch/sdk/domains/sms/SMSService.java index 2947577e..daa31986 100644 --- a/client/src/main/com/sinch/sdk/domains/sms/SMSService.java +++ b/client/src/main/com/sinch/sdk/domains/sms/SMSService.java @@ -16,4 +16,12 @@ public interface SMSService { * @since 1.0 */ BatchesService batches(); + + /** + * WebHooksService Service instance + * + * @return service instance for project + * @since 1.0 + */ + WebHooksService webHooks(); } diff --git a/client/src/main/com/sinch/sdk/domains/sms/WebHooksService.java b/client/src/main/com/sinch/sdk/domains/sms/WebHooksService.java new file mode 100644 index 00000000..5dbfe23e --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/WebHooksService.java @@ -0,0 +1,103 @@ +package com.sinch.sdk.domains.sms; + +import com.sinch.sdk.core.exceptions.ApiMappingException; +import com.sinch.sdk.domains.sms.models.webhooks.BaseDeliveryReport; +import com.sinch.sdk.domains.sms.models.webhooks.BaseIncomingSMS; + +/** + * WebHooks + * + *

Callbacks + * + *

A callback is an HTTP POST request with a notification made by the Sinch SMS REST API to a URI + * of your choosing. + * + *

The REST API expects the receiving server to respond with a response code within the 2xx + * success range. For 5xx the callback will be retried. For 429 + * the callback will be retried and the throughput will be lowered. For other status codes in the + * 4xx range the callback will not be retried. The first initial retry will happen 5 + * seconds after the first try. The next attempt is after 10 seconds, then after 20 seconds, after + * 40 seconds, after 80 seconds, doubling on every attempt. The last retry will be at 81920 seconds + * (or 22 hours 45 minutes) after the initial failed attempt. + * + *

The SMS REST API offers the following callback options which can be configured for your + * account upon request to your account manager. + * + *

+ * + * @see https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/ + * @since 1.0 + */ +public interface WebHooksService { + + /** + * Incoming SMS WebHook + * + *

An inbound message is a message sent to one of your short codes or long numbers from a + * mobile phone. To receive inbound message callbacks, a URL needs to be added to your REST API. + * This URL can be specified in your Dashboard. + * + * @param jsonPayload The incoming message to your sinch number + * @return Decoded payload + * @see https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/operation/incomingSMS + * @since 1.0 + */ + BaseIncomingSMS incomingSMS(String jsonPayload) throws ApiMappingException; + + /** + * Delivery Report WebHook + * + *

A delivery report contains the status and status code for each recipient of a batch. To get + * a delivery report callback for a message or batch of messages, set the delivery_report + * field accordingly when creating a batch. + * + *

The following is provided so you can better understand our webhooks/callbacks. Configuration + * of both webhooks and the type of delivery report requested happens when sending a batch. + * + *

Callback URL + * + *

The callback URL can either be provided for each batch or provisioned globally for your + * account in your Sinch Customer + * Dashboard. Learn how to configure a webhook/callback here + * + *

Type + * + *

The type is the type of delivery report webhook. The response will vary + * depending on the webhook delivery report you selected when the batch was sent, so choose the + * appropriate selection under "One of". + * + *

+ * + * @param jsonPayload The incoming delivery report + * @return Decoded payload + * @see https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/operation/deliveryReport + * @since 1.0 + */ + BaseDeliveryReport deliveryReport(String jsonPayload) throws ApiMappingException; +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/adapters/SMSService.java b/client/src/main/com/sinch/sdk/domains/sms/adapters/SMSService.java index fd2f3a26..bbc23470 100644 --- a/client/src/main/com/sinch/sdk/domains/sms/adapters/SMSService.java +++ b/client/src/main/com/sinch/sdk/domains/sms/adapters/SMSService.java @@ -2,6 +2,7 @@ import com.sinch.sdk.core.http.HttpClient; import com.sinch.sdk.domains.sms.BatchesService; +import com.sinch.sdk.domains.sms.WebHooksService; import com.sinch.sdk.models.Configuration; public class SMSService implements com.sinch.sdk.domains.sms.SMSService { @@ -9,6 +10,7 @@ public class SMSService implements com.sinch.sdk.domains.sms.SMSService { private final Configuration configuration; private final HttpClient httpClient; private BatchesService batches; + private WebHooksService webHooks; public SMSService(Configuration configuration, HttpClient httpClient) { this.configuration = configuration; @@ -23,4 +25,12 @@ public BatchesService batches() { } return this.batches; } + + @Override + public WebHooksService webHooks() { + if (null == this.webHooks) { + this.webHooks = new com.sinch.sdk.domains.sms.adapters.WebHooksService(); + } + return this.webHooks; + } } diff --git a/client/src/main/com/sinch/sdk/domains/sms/adapters/WebHooksService.java b/client/src/main/com/sinch/sdk/domains/sms/adapters/WebHooksService.java new file mode 100644 index 00000000..858ace2e --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/adapters/WebHooksService.java @@ -0,0 +1,39 @@ +package com.sinch.sdk.domains.sms.adapters; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.sinch.sdk.core.exceptions.ApiMappingException; +import com.sinch.sdk.core.utils.StringUtil; +import com.sinch.sdk.core.utils.databind.Mapper; +import com.sinch.sdk.domains.sms.models.webhooks.BaseDeliveryReport; +import com.sinch.sdk.domains.sms.models.webhooks.BaseIncomingSMS; + +public class WebHooksService implements com.sinch.sdk.domains.sms.WebHooksService { + + @Override + public BaseIncomingSMS incomingSMS(String jsonPayload) throws ApiMappingException { + try { + BaseIncomingSMS generic = + Mapper.getInstance().readValue(jsonPayload, BaseIncomingSMS.class); + if (null == generic && !StringUtil.isEmpty(jsonPayload)) { + throw new ApiMappingException(jsonPayload, null); + } + return generic; + } catch (JsonProcessingException e) { + throw new ApiMappingException(jsonPayload, e); + } + } + + @Override + public BaseDeliveryReport deliveryReport(String jsonPayload) throws ApiMappingException { + try { + BaseDeliveryReport generic = + Mapper.getInstance().readValue(jsonPayload, BaseDeliveryReport.class); + if (null == generic && !StringUtil.isEmpty(jsonPayload)) { + throw new ApiMappingException(jsonPayload, null); + } + return generic; + } catch (JsonProcessingException e) { + throw new ApiMappingException(jsonPayload, e); + } + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/DeliveryReportStatus.java b/client/src/main/com/sinch/sdk/domains/sms/models/DeliveryReportStatus.java new file mode 100644 index 00000000..d411ae1f --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/DeliveryReportStatus.java @@ -0,0 +1,85 @@ +package com.sinch.sdk.domains.sms.models; + +import com.sinch.sdk.core.utils.EnumDynamic; +import com.sinch.sdk.core.utils.EnumSupportDynamic; +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * The status field describes which state a particular message is in. Note that statuses of type + * Intermediate will only be reported if you request a status per_recipient or + * per_recipient_final ( @see Retrieve + * a recipient delivery report). + * + * @see https://developers.sinch.com/docs/sms/api-reference/sms/tag/Delivery-reports/#tag/Delivery-reports/section/Delivery-report-statuses + * @since 1.0 + */ +public class DeliveryReportStatus extends EnumDynamic { + + /** + * Message is queued within REST API system and will be dispatched according to the rate of the + * account. + */ + public static final DeliveryReportStatus QUEUED = new DeliveryReportStatus("Queued"); + /** Message has been dispatched and accepted for delivery by the SMSC. */ + public static final DeliveryReportStatus DISPATCHED = new DeliveryReportStatus("Dispatched"); + /** Message was aborted before reaching the SMSC. */ + public static final DeliveryReportStatus ABORTED = new DeliveryReportStatus("Aborted"); + /** Message was cancelled by user before reaching SMSC. */ + public static final DeliveryReportStatus CANCELLED = new DeliveryReportStatus("Cancelled"); + /** Message was rejected by the SMSC. */ + public static final DeliveryReportStatus REJECTED = new DeliveryReportStatus("Rejected"); + /** + * Message has been deleted. Message was deleted by a remote SMSC. This may happen if the + * destination is an invalid MSISDN or opted out subscriber. + */ + public static final DeliveryReportStatus DELETED = new DeliveryReportStatus("Deleted"); + /** Message has been delivered. */ + public static final DeliveryReportStatus DELIVERED = new DeliveryReportStatus("Delivered"); + /** Message failed to be delivered. */ + public static final DeliveryReportStatus FAILED = new DeliveryReportStatus("Failed"); + /** + * Message expired before delivery to the SMSC. This may happen if the expiry time for the message + * was very short. + */ + public static final DeliveryReportStatus EXPIRED = new DeliveryReportStatus("Expired"); + /** + * Message was delivered to the SMSC but no Delivery Receipt has been received or a Delivery + * Receipt that couldn't be interpreted was received. + */ + public static final DeliveryReportStatus UNKNOWN = new DeliveryReportStatus("Unknown"); + + private static final EnumSupportDynamic ENUM_SUPPORT = + new EnumSupportDynamic<>( + DeliveryReportStatus.class, + DeliveryReportStatus::new, + Arrays.asList( + QUEUED, + DISPATCHED, + ABORTED, + CANCELLED, + REJECTED, + DELETED, + DELIVERED, + FAILED, + EXPIRED, + UNKNOWN)); + + private DeliveryReportStatus(String value) { + super(value); + } + + public static Stream values() { + return ENUM_SUPPORT.values(); + } + + public static DeliveryReportStatus from(String value) { + return ENUM_SUPPORT.from(value); + } + + public static String valueOf(DeliveryReportStatus e) { + return ENUM_SUPPORT.valueOf(e); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseDeliveryReport.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseDeliveryReport.java new file mode 100644 index 00000000..767b1c11 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseDeliveryReport.java @@ -0,0 +1,72 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.Objects; +import java.util.Optional; + +/** + * Base class for Delivery Report WebHook + * + * @since 1.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type", + visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type( + value = DeliveryReportRecipientSMS.class, + name = "recipient_delivery_report_sms"), + @JsonSubTypes.Type( + value = DeliveryReportRecipientMMS.class, + name = "recipient_delivery_report_mms"), + @JsonSubTypes.Type(value = DeliveryReportSMS.class, name = "delivery_report_sms"), + @JsonSubTypes.Type(value = DeliveryReportMMS.class, name = "delivery_report_mms") +}) +public abstract class BaseDeliveryReport { + + static final String JSON_PROPERTY_BATCH_ID = "batch_id"; + private final String batchId; + static final String JSON_PROPERTY_CLIENT_REFERENCE = "client_reference"; + private final String clientReference; + + /** + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + */ + @JsonCreator + public BaseDeliveryReport( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference) { + Objects.requireNonNull(batchId); + this.batchId = batchId; + this.clientReference = clientReference; + } + + public String getBatchId() { + return batchId; + } + + public Optional getClientReference() { + return Optional.ofNullable(clientReference); + } + + @Override + public String toString() { + return "BaseDeliveryReport{" + + "batchId='" + + batchId + + '\'' + + ", clientReference='" + + clientReference + + '\'' + + '}'; + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseIncomingSMS.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseIncomingSMS.java new file mode 100644 index 00000000..608e7073 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/BaseIncomingSMS.java @@ -0,0 +1,98 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.util.Optional; + +/** + * Base class for Incoming SMS Webhook + * + * @param Type of SMS body + * @since 1.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type", + visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = IncomingSMSBinary.class, name = "mo_binary"), + @JsonSubTypes.Type(value = IncomingSMSText.class, name = "mo_text") +}) +public abstract class BaseIncomingSMS { + + static final String JSON_PROPERTY_BODY = "body"; + private final T body; + static final String JSON_PROPERTY_FROM = "from"; + private final String from; + static final String JSON_PROPERTY_ID = "id"; + private final String id; + static final String JSON_PROPERTY_RECEIVED_AT = "received_at"; + private final Instant receivedAt; + static final String JSON_PROPERTY_TO = "to"; + private final String to; + static final String JSON_PROPERTY_CLIENT_REFERENCE = "client_reference"; + private final String clientReference; + static final String JSON_PROPERTY_OPERATOR_ID = "operator_id"; + private final String operatorId; + static final String JSON_PROPERTY_SENT_AT = "sent_at"; + private final Instant sentAt; + + @JsonCreator + public BaseIncomingSMS( + @JsonProperty(JSON_PROPERTY_BODY) T body, + @JsonProperty(JSON_PROPERTY_FROM) String from, + @JsonProperty(JSON_PROPERTY_ID) String id, + @JsonProperty(JSON_PROPERTY_RECEIVED_AT) OffsetDateTime receivedAt, + @JsonProperty(JSON_PROPERTY_TO) String to, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_OPERATOR_ID) String operatorId, + @JsonProperty(JSON_PROPERTY_SENT_AT) OffsetDateTime sentAt) { + this.body = body; + this.from = from; + this.id = id; + this.receivedAt = receivedAt.toInstant(); + this.to = to; + this.clientReference = clientReference; + this.operatorId = operatorId; + this.sentAt = null != sentAt ? sentAt.toInstant() : null; + } + + public T getBody() { + return body; + } + + public String getFrom() { + return from; + } + + public String getId() { + return id; + } + + public Instant getReceivedAt() { + return receivedAt; + } + + public String getTo() { + return to; + } + + public Optional getClientReference() { + return Optional.ofNullable(clientReference); + } + + public Optional getOperatorId() { + return Optional.ofNullable(operatorId); + } + + public Optional getSentAt() { + return Optional.ofNullable(sentAt); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReport.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReport.java new file mode 100644 index 00000000..7f87aa0a --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReport.java @@ -0,0 +1,59 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collection; +import java.util.Objects; + +/** + * Common Definition to Delivery Report + * + * @since 1.0 + */ +public abstract class DeliveryReport extends BaseDeliveryReport { + + static final String JSON_PROPERTY_STATUSES = "statuses"; + private final Collection statuses; + static final String JSON_PROPERTY_TOTAL_MESSAGE_COUNT = "total_message_count"; + private final Integer totalMessageCount; + + /** + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param statuses Required. Array with status objects. Only status codes with at + * least one recipient will be listed. + * @param totalMessageCount Required. The total number of messages in the batch. + */ + @JsonCreator + public DeliveryReport( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_STATUSES) Collection statuses, + @JsonProperty(JSON_PROPERTY_TOTAL_MESSAGE_COUNT) Integer totalMessageCount) { + super(batchId, clientReference); + Objects.requireNonNull(statuses); + Objects.requireNonNull(totalMessageCount); + this.statuses = statuses; + this.totalMessageCount = totalMessageCount; + } + + public Collection getStatuses() { + return statuses; + } + + public Integer getTotalMessageCount() { + return totalMessageCount; + } + + @Override + public String toString() { + return "DeliveryReport{" + + "statuses=" + + statuses + + ", totalMessageCount=" + + totalMessageCount + + "} " + + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportMMS.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportMMS.java new file mode 100644 index 00000000..a98b4673 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportMMS.java @@ -0,0 +1,35 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collection; + +/** + * MMS delivery Report + * + * @since 1.0 + */ +public class DeliveryReportMMS extends DeliveryReport { + + /** + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param statuses Required. Array with status objects. Only status codes with at + * least one recipient will be listed. + * @param totalMessageCount Required. The total number of messages in the batch. + */ + @JsonCreator + public DeliveryReportMMS( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_STATUSES) Collection statuses, + @JsonProperty(JSON_PROPERTY_TOTAL_MESSAGE_COUNT) Integer totalMessageCount) { + super(batchId, clientReference, statuses, totalMessageCount); + } + + @Override + public String toString() { + return "DeliveryReportMMS{} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipient.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipient.java new file mode 100644 index 00000000..d4a42b0b --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipient.java @@ -0,0 +1,151 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sinch.sdk.domains.sms.models.DeliveryReportStatus; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.util.Optional; + +/** + * Common Definition to Delivery Report + * + * @since 1.0 + */ +public abstract class DeliveryReportRecipient extends BaseDeliveryReport { + + static final String JSON_PROPERTY_AT = "at"; + private final Instant at; + static final String JSON_PROPERTY_CODE = "code"; + private final Integer code; + static final String JSON_PROPERTY_RECIPIENT = "recipient"; + private final String recipient; + static final String JSON_PROPERTY_STATUS = "status"; + private final DeliveryReportStatus status; + static final String JSON_PROPERTY_APPLIED_ORIGINATOR = "applied_originator"; + private final String appliedOriginator; + static final String JSON_PROPERTY_ENCODING = "encoding"; + private final String encoding; + static final String JSON_PROPERTY_NUMBER_OF_MESSAGE_PARTS = "number_of_message_parts"; + private final Integer numberOfMessageParts; + static final String JSON_PROPERTY_OPERATOR = "operator"; + private final String operator; + static final String JSON_PROPERTY_OPERATOR_STATUS_AT = "operator_status_at"; + private final Instant operatorStatusAt; + + /** + * Recipient Delivery Report Recipient + * + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param at Required. A timestamp of when the Delivery Report was created in the + * Sinch service + * @param code Required. The detailed status + * code. + * @param recipient Required. Phone number that was queried. + * @param status Required. The simplified status as described in Delivery Report + * Statuses + * @param appliedOriginator The default originator used for the recipient this delivery report + * belongs to, if default originator pool configured and no originator set when submitting + * batch. + * @param encoding Applied encoding for message. Present only if smart encoding is enabled. + * @param numberOfMessageParts The number of parts the message was split into. Present only if + * max_number_of_message_parts parameter was set. + * @param operator The operator that was used for delivering the message to this recipient, if + * enabled on the account by Sinch. + * @param operatorStatusAt A timestamp extracted from the Delivery Receipt from the originating + * SMSC + */ + @JsonCreator + public DeliveryReportRecipient( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_AT) OffsetDateTime at, + @JsonProperty(JSON_PROPERTY_CODE) Integer code, + @JsonProperty(JSON_PROPERTY_RECIPIENT) String recipient, + @JsonProperty(JSON_PROPERTY_STATUS) String status, + @JsonProperty(JSON_PROPERTY_APPLIED_ORIGINATOR) String appliedOriginator, + @JsonProperty(JSON_PROPERTY_ENCODING) String encoding, + @JsonProperty(JSON_PROPERTY_NUMBER_OF_MESSAGE_PARTS) Integer numberOfMessageParts, + @JsonProperty(JSON_PROPERTY_OPERATOR) String operator, + @JsonProperty(JSON_PROPERTY_OPERATOR_STATUS_AT) OffsetDateTime operatorStatusAt) { + super(batchId, clientReference); + this.at = null != at ? at.toInstant() : null; + this.code = code; + this.recipient = recipient; + this.status = DeliveryReportStatus.from(status); + this.appliedOriginator = appliedOriginator; + this.encoding = encoding; + this.numberOfMessageParts = numberOfMessageParts; + this.operator = operator; + this.operatorStatusAt = null != operatorStatusAt ? operatorStatusAt.toInstant() : null; + } + + public Instant getAt() { + return at; + } + + public Integer getCode() { + return code; + } + + public String getRecipient() { + return recipient; + } + + public DeliveryReportStatus getStatus() { + return status; + } + + public Optional getAppliedOriginator() { + return Optional.ofNullable(appliedOriginator); + } + + public Optional getEncoding() { + return Optional.ofNullable(encoding); + } + + public Optional getNumberOfMessageParts() { + return Optional.ofNullable(numberOfMessageParts); + } + + public Optional getOperator() { + return Optional.ofNullable(operator); + } + + public Optional getOperatorStatusAt() { + return Optional.ofNullable(operatorStatusAt); + } + + @Override + public String toString() { + return "DeliveryReportRecipient{" + + "at=" + + at + + ", code='" + + code + + '\'' + + ", recipient='" + + recipient + + '\'' + + ", status=" + + status + + ", appliedOriginator='" + + appliedOriginator + + '\'' + + ", encoding='" + + encoding + + '\'' + + ", numberOfMessageParts=" + + numberOfMessageParts + + ", operator='" + + operator + + '\'' + + ", operatorStatusAt=" + + operatorStatusAt + + "} " + + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientMMS.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientMMS.java new file mode 100644 index 00000000..09508a32 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientMMS.java @@ -0,0 +1,70 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * SMS Delivery Report Recipient for MMS + * + * @since 1.0 + */ +public class DeliveryReportRecipientMMS extends DeliveryReportRecipient { + + /** + * Recipient Delivery Report for MMS + * + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param at Required. A timestamp of when the Delivery Report was created in the + * Sinch service + * @param code Required. The detailed status + * code. + * @param recipient Required. Phone number that was queried. + * @param status Required. The simplified status as described in Delivery Report + * Statuses + * @param appliedOriginator The default originator used for the recipient this delivery report + * belongs to, if default originator pool configured and no originator set when submitting + * batch. + * @param encoding Applied encoding for message. Present only if smart encoding is enabled. + * @param numberOfMessageParts The number of parts the message was split into. Present only if + * max_number_of_message_parts parameter was set. + * @param operator The operator that was used for delivering the message to this recipient, if + * enabled on the account by Sinch. + * @param operatorStatusAt A timestamp extracted from the Delivery Receipt from the originating + * SMSC + */ + @JsonCreator + public DeliveryReportRecipientMMS( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_AT) OffsetDateTime at, + @JsonProperty(JSON_PROPERTY_CODE) Integer code, + @JsonProperty(JSON_PROPERTY_RECIPIENT) String recipient, + @JsonProperty(JSON_PROPERTY_STATUS) String status, + @JsonProperty(JSON_PROPERTY_APPLIED_ORIGINATOR) String appliedOriginator, + @JsonProperty(JSON_PROPERTY_ENCODING) String encoding, + @JsonProperty(JSON_PROPERTY_NUMBER_OF_MESSAGE_PARTS) Integer numberOfMessageParts, + @JsonProperty(JSON_PROPERTY_OPERATOR) String operator, + @JsonProperty(JSON_PROPERTY_OPERATOR_STATUS_AT) OffsetDateTime operatorStatusAt) { + super( + batchId, + clientReference, + at, + code, + recipient, + status, + appliedOriginator, + encoding, + numberOfMessageParts, + operator, + operatorStatusAt); + } + + @Override + public String toString() { + return "DeliveryReportRecipientMMS{} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientSMS.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientSMS.java new file mode 100644 index 00000000..1399d503 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportRecipientSMS.java @@ -0,0 +1,70 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * SMS Delivery Report Recipient for SMS + * + * @since 1.0 + */ +public class DeliveryReportRecipientSMS extends DeliveryReportRecipient { + + /** + * Recipient Delivery Report for SMS + * + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param at Required. A timestamp of when the Delivery Report was created in the + * Sinch service + * @param code Required. The detailed status + * code. + * @param recipient Required. Phone number that was queried. + * @param status Required. The simplified status as described in Delivery Report + * Statuses + * @param appliedOriginator The default originator used for the recipient this delivery report + * belongs to, if default originator pool configured and no originator set when submitting + * batch. + * @param encoding Applied encoding for message. Present only if smart encoding is enabled. + * @param numberOfMessageParts The number of parts the message was split into. Present only if + * max_number_of_message_parts parameter was set. + * @param operator The operator that was used for delivering the message to this recipient, if + * enabled on the account by Sinch. + * @param operatorStatusAt A timestamp extracted from the Delivery Receipt from the originating + * SMSC + */ + @JsonCreator + public DeliveryReportRecipientSMS( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_AT) OffsetDateTime at, + @JsonProperty(JSON_PROPERTY_CODE) Integer code, + @JsonProperty(JSON_PROPERTY_RECIPIENT) String recipient, + @JsonProperty(JSON_PROPERTY_STATUS) String status, + @JsonProperty(JSON_PROPERTY_APPLIED_ORIGINATOR) String appliedOriginator, + @JsonProperty(JSON_PROPERTY_ENCODING) String encoding, + @JsonProperty(JSON_PROPERTY_NUMBER_OF_MESSAGE_PARTS) Integer numberOfMessageParts, + @JsonProperty(JSON_PROPERTY_OPERATOR) String operator, + @JsonProperty(JSON_PROPERTY_OPERATOR_STATUS_AT) OffsetDateTime operatorStatusAt) { + super( + batchId, + clientReference, + at, + code, + recipient, + status, + appliedOriginator, + encoding, + numberOfMessageParts, + operator, + operatorStatusAt); + } + + @Override + public String toString() { + return "DeliveryReportRecipientSMS{} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportSMS.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportSMS.java new file mode 100644 index 00000000..86f59969 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportSMS.java @@ -0,0 +1,35 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collection; + +/** + * SMS delivery Report + * + * @since 1.0 + */ +public class DeliveryReportSMS extends DeliveryReport { + + /** + * @param batchId Required. The ID of the batch this delivery report belongs to. + * @param clientReference The client identifier of the batch this delivery report belongs to, if + * set when submitting batch. + * @param statuses Required. Array with status objects. Only status codes with at + * least one recipient will be listed. + * @param totalMessageCount Required. The total number of messages in the batch. + */ + @JsonCreator + public DeliveryReportSMS( + @JsonProperty(JSON_PROPERTY_BATCH_ID) String batchId, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_STATUSES) Collection statuses, + @JsonProperty(JSON_PROPERTY_TOTAL_MESSAGE_COUNT) Integer totalMessageCount) { + super(batchId, clientReference, statuses, totalMessageCount); + } + + @Override + public String toString() { + return "DeliveryReportSMS{} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportStatusDetails.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportStatusDetails.java new file mode 100644 index 00000000..fd1e9724 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/DeliveryReportStatusDetails.java @@ -0,0 +1,75 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sinch.sdk.domains.sms.models.DeliveryReportStatus; +import java.util.Collection; + +/** + * Details related to a delivery report status + * + * @since 1.0 + */ +public class DeliveryReportStatusDetails { + + static final String JSON_PROPERTY_CODE = "code"; + private final Integer code; + static final String JSON_PROPERTY_COUNT = "count"; + private final Integer count; + static final String JSON_PROPERTY_RECIPIENTS = "recipients"; + private final Collection recipients; + static final String JSON_PROPERTY_STATUS = "status"; + private final DeliveryReportStatus status; + + /** + * @param code Required. The detailed status + * code. + * @param count Required. The number of messages that currently has this code + * @param recipients Required. Only for full report. A list of the phone number + * recipients which messages has this status code + * @param status Required. The simplified status as described in Delivery Report + * Statuses + */ + @JsonCreator + public DeliveryReportStatusDetails( + @JsonProperty(JSON_PROPERTY_CODE) Integer code, + @JsonProperty(JSON_PROPERTY_COUNT) Integer count, + @JsonProperty(JSON_PROPERTY_RECIPIENTS) Collection recipients, + @JsonProperty(JSON_PROPERTY_STATUS) String status) { + this.code = code; + this.count = count; + this.recipients = recipients; + this.status = DeliveryReportStatus.from(status); + } + + public Integer getCode() { + return code; + } + + public Integer getCount() { + return count; + } + + public Collection getRecipients() { + return recipients; + } + + public DeliveryReportStatus getStatus() { + return status; + } + + @Override + public String toString() { + return "DeliveryReportStatusDetails{" + + "code=" + + code + + ", count=" + + count + + ", recipients=" + + recipients + + ", status=" + + status + + '}'; + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSBinary.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSBinary.java new file mode 100644 index 00000000..764647eb --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSBinary.java @@ -0,0 +1,58 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * Incoming SMS Binary + * + * @since 1.0 + */ +public class IncomingSMSBinary extends BaseIncomingSMS { + + static final String JSON_PROPERTY_UDH = "udh"; + private final String udh; + + /** + * Binary MO class + * + * @param body The message content Base64 encoded. Max 140 bytes together with udh. + * @param from The phone number that sent the message. @see https://community.sinch.com/t5/Glossary/MSISDN/ta-p/7628 + * @param id The ID of this inbound message. + * @param receivedAt When the system received the message. + * @param to The Sinch phone number or short code to which the message was sent. + * @param clientReference If this inbound message is in response to a previously sent message that + * contained a client reference, then this field contains that client reference. Utilizing + * this feature requires additional setup on your account. Contact your account manager to + * enable this feature. + * @param operatorId The MCC/MNC of the sender's operator if known. + * @param sendAt When the message left the originating device. Only available if provided by + * operator. + * @param udh The UDH header of a binary message HEX encoded. Max 140 bytes together with body. + */ + @JsonCreator + public IncomingSMSBinary( + @JsonProperty(JSON_PROPERTY_BODY) String body, + @JsonProperty(JSON_PROPERTY_FROM) String from, + @JsonProperty(JSON_PROPERTY_ID) String id, + @JsonProperty(JSON_PROPERTY_RECEIVED_AT) OffsetDateTime receivedAt, + @JsonProperty(JSON_PROPERTY_TO) String to, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_OPERATOR_ID) String operatorId, + @JsonProperty(JSON_PROPERTY_SENT_AT) OffsetDateTime sendAt, + @JsonProperty(JSON_PROPERTY_UDH) String udh) { + super(body, from, id, receivedAt, to, clientReference, operatorId, sendAt); + this.udh = udh; + } + + public String getUdh() { + return udh; + } + + @Override + public String toString() { + return "IncomingSMSBinary{" + "udh='" + udh + '\'' + "} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSText.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSText.java new file mode 100644 index 00000000..b560119e --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/IncomingSMSText.java @@ -0,0 +1,48 @@ +package com.sinch.sdk.domains.sms.models.webhooks; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * Incoming SMS Text + * + * @since 1.0 + */ +public class IncomingSMSText extends BaseIncomingSMS { + + /** + * Text MO class + * + * @param body The message content Base64 encoded. Max 140 bytes together with udh. + * @param from The phone number that sent the message. @see https://community.sinch.com/t5/Glossary/MSISDN/ta-p/7628 + * @param id The ID of this inbound message. + * @param receivedAt When the system received the message. + * @param to The Sinch phone number or short code to which the message was sent. + * @param clientReference If this inbound message is in response to a previously sent message that + * contained a client reference, then this field contains that client reference. Utilizing + * this feature requires additional setup on your account. Contact your account manager to + * enable this feature. + * @param operatorId The MCC/MNC of the sender's operator if known. + * @param sendAt When the message left the originating device. Only available if provided by + * operator. + */ + @JsonCreator + public IncomingSMSText( + @JsonProperty(JSON_PROPERTY_BODY) String body, + @JsonProperty(JSON_PROPERTY_FROM) String from, + @JsonProperty(JSON_PROPERTY_ID) String id, + @JsonProperty(JSON_PROPERTY_RECEIVED_AT) OffsetDateTime receivedAt, + @JsonProperty(JSON_PROPERTY_TO) String to, + @JsonProperty(JSON_PROPERTY_CLIENT_REFERENCE) String clientReference, + @JsonProperty(JSON_PROPERTY_OPERATOR_ID) String operatorId, + @JsonProperty(JSON_PROPERTY_SENT_AT) OffsetDateTime sendAt) { + super(body, from, id, receivedAt, to, clientReference, operatorId, sendAt); + } + + @Override + public String toString() { + return "IncomingSMSText{} " + super.toString(); + } +} diff --git a/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/package-info.java b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/package-info.java new file mode 100644 index 00000000..daf15120 --- /dev/null +++ b/client/src/main/com/sinch/sdk/domains/sms/models/webhooks/package-info.java @@ -0,0 +1,6 @@ +/** + * SMS WebHooks models + * + * @since 1.0 + */ +package com.sinch.sdk.domains.sms.models.webhooks; diff --git a/client/src/test/java/com/sinch/sdk/domains/sms/adapters/WebHKooksServiceTest.java b/client/src/test/java/com/sinch/sdk/domains/sms/adapters/WebHKooksServiceTest.java new file mode 100644 index 00000000..29f47415 --- /dev/null +++ b/client/src/test/java/com/sinch/sdk/domains/sms/adapters/WebHKooksServiceTest.java @@ -0,0 +1,134 @@ +package com.sinch.sdk.domains.sms.adapters; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.adelean.inject.resources.junit.jupiter.GivenTextResource; +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.core.exceptions.ApiException; +import com.sinch.sdk.core.exceptions.ApiMappingException; +import com.sinch.sdk.domains.sms.models.dto.v1.webhooks.DeliveryReportDtoTest; +import com.sinch.sdk.domains.sms.models.dto.v1.webhooks.DeliveryReportRecipientDtoTest; +import com.sinch.sdk.domains.sms.models.dto.v1.webhooks.IncomingSMSBinaryDtoTest; +import com.sinch.sdk.domains.sms.models.dto.v1.webhooks.IncomingSMSTextDtoTest; +import com.sinch.sdk.domains.sms.models.webhooks.BaseDeliveryReport; +import com.sinch.sdk.domains.sms.models.webhooks.BaseIncomingSMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportMMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportRecipientMMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportRecipientSMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportSMS; +import com.sinch.sdk.domains.sms.models.webhooks.IncomingSMSBinary; +import com.sinch.sdk.domains.sms.models.webhooks.IncomingSMSText; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; + +@TestWithResources +public class WebHKooksServiceTest extends BaseTest { + + @GivenTextResource("/domains/sms/v1/webhooks/IncomingSMSBinary.json") + String incomingSMSBinaryJsonString; + + @GivenTextResource("/domains/sms/v1/webhooks/IncomingSMSText.json") + String incomingSMSTextJsonString; + + @GivenTextResource("/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json") + String deliveryReportRecipientSMSJsonString; + + @GivenTextResource("/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json") + String deliveryReportRecipientMMSJsonString; + + @GivenTextResource("/domains/sms/v1/webhooks/DeliveryReportSMS.json") + String deliveryReportSMSJsonString; + + @GivenTextResource("/domains/sms/v1/webhooks/DeliveryReportMMS.json") + String deliveryReportMMSJsonString; + + @InjectMocks WebHooksService service; + + @Test + void incomingSMSBinary() throws ApiException { + + BaseIncomingSMS response = service.incomingSMS(incomingSMSBinaryJsonString); + + Assertions.assertThat(response).isInstanceOf(IncomingSMSBinary.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(IncomingSMSBinaryDtoTest.incomingSMSBinary); + } + + @Test + void incomingSMSText() throws ApiException { + + BaseIncomingSMS response = service.incomingSMS(incomingSMSTextJsonString); + + Assertions.assertThat(response).isInstanceOf(IncomingSMSText.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(IncomingSMSTextDtoTest.incomingSMSText); + } + + @Test + void handleExceptionIncomingSMS() throws ApiException { + + String jsonPayload = incomingSMSBinaryJsonString.replace("mo_binary", "foo type"); + ApiMappingException thrown = + assertThrows(ApiMappingException.class, () -> service.incomingSMS(jsonPayload)); + assertTrue(thrown.getMessage().contains(jsonPayload)); + } + + @Test + void deliveryReportRecipientDeliveryReportSms() throws ApiException { + + BaseDeliveryReport response = service.deliveryReport(deliveryReportRecipientSMSJsonString); + + Assertions.assertThat(response).isInstanceOf(DeliveryReportRecipientSMS.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(DeliveryReportRecipientDtoTest.deliveryReportRecipientSMS); + } + + @Test + void deliveryReportRecipientDeliveryReportMms() throws ApiException { + + BaseDeliveryReport response = service.deliveryReport(deliveryReportRecipientMMSJsonString); + + Assertions.assertThat(response).isInstanceOf(DeliveryReportRecipientMMS.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(DeliveryReportRecipientDtoTest.deliveryReportRecipientMMS); + } + + @Test + void deliveryReportDeliveryReportSms() throws ApiException { + + BaseDeliveryReport response = service.deliveryReport(deliveryReportSMSJsonString); + + Assertions.assertThat(response).isInstanceOf(DeliveryReportSMS.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(DeliveryReportDtoTest.deliveryReportSMS); + } + + @Test + void deliveryReportDeliveryReportMms() throws ApiException { + + BaseDeliveryReport response = service.deliveryReport(deliveryReportMMSJsonString); + + Assertions.assertThat(response).isInstanceOf(DeliveryReportMMS.class); + Assertions.assertThat(response) + .usingRecursiveComparison() + .isEqualTo(DeliveryReportDtoTest.deliveryReportMMS); + } + + @Test + void handleExceptionDeliveryReport() throws ApiException { + + String jsonPayload = + deliveryReportRecipientMMSJsonString.replace("recipient_delivery_report_mms", "foo type"); + ApiMappingException thrown = + assertThrows(ApiMappingException.class, () -> service.deliveryReport(jsonPayload)); + assertTrue(thrown.getMessage().contains(jsonPayload)); + } +} diff --git a/core/src/main/com/sinch/sdk/core/exceptions/ApiException.java b/core/src/main/com/sinch/sdk/core/exceptions/ApiException.java index d7b31989..883fb3fa 100644 --- a/core/src/main/com/sinch/sdk/core/exceptions/ApiException.java +++ b/core/src/main/com/sinch/sdk/core/exceptions/ApiException.java @@ -15,6 +15,10 @@ public ApiException(String message) { super(message); } + public ApiException(String message, Throwable throwable) { + super(message, throwable); + } + public ApiException(String message, Throwable throwable, int code) { super(message, throwable); this.code = code; diff --git a/core/src/main/com/sinch/sdk/core/exceptions/ApiMappingException.java b/core/src/main/com/sinch/sdk/core/exceptions/ApiMappingException.java new file mode 100644 index 00000000..426aedcd --- /dev/null +++ b/core/src/main/com/sinch/sdk/core/exceptions/ApiMappingException.java @@ -0,0 +1,8 @@ +package com.sinch.sdk.core.exceptions; + +public class ApiMappingException extends ApiException { + + public ApiMappingException(String payload, Throwable throwable) { + super(String.format("Unable to map string '%s'", payload), throwable); + } +} diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportDtoTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportDtoTest.java new file mode 100644 index 00000000..3295dc2e --- /dev/null +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportDtoTest.java @@ -0,0 +1,53 @@ +package com.sinch.sdk.domains.sms.models.dto.v1.webhooks; + +import com.adelean.inject.resources.junit.jupiter.GivenJsonResource; +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportMMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportSMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportStatusDetails; +import java.util.Collections; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +@TestWithResources +public class DeliveryReportDtoTest extends BaseTest { + + @GivenJsonResource("/domains/sms/v1/webhooks/DeliveryReportSMS.json") + DeliveryReportSMS deliveryReportSMSClient; + + @GivenJsonResource("/domains/sms/v1/webhooks/DeliveryReportMMS.json") + DeliveryReportMMS deliveryReportMMSClient; + + public static DeliveryReportSMS deliveryReportSMS = + new DeliveryReportSMS( + "01FC66621XXXXX119Z8PMV1QPQ", + null, + Collections.singletonList( + new DeliveryReportStatusDetails( + 0, 1, Collections.singletonList("44231235674"), "Delivered")), + 1); + + public static DeliveryReportMMS deliveryReportMMS = + new DeliveryReportMMS( + "01FC66621XXXXX119Z8PMV1QPQ", + null, + Collections.singletonList( + new DeliveryReportStatusDetails( + 0, 1, Collections.singletonList("44231235674"), "Delivered")), + 1); + + @Test + void deserializeDeliveryReportRecipientSMS() { + Assertions.assertThat(deliveryReportSMS) + .usingRecursiveComparison() + .isEqualTo(deliveryReportSMSClient); + } + + @Test + void deserializeDeliveryReportRecipientMMS() { + Assertions.assertThat(deliveryReportMMS) + .usingRecursiveComparison() + .isEqualTo(deliveryReportMMSClient); + } +} diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportRecipientDtoTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportRecipientDtoTest.java new file mode 100644 index 00000000..03778ca9 --- /dev/null +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/DeliveryReportRecipientDtoTest.java @@ -0,0 +1,62 @@ +package com.sinch.sdk.domains.sms.models.dto.v1.webhooks; + +import com.adelean.inject.resources.junit.jupiter.GivenJsonResource; +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportRecipientMMS; +import com.sinch.sdk.domains.sms.models.webhooks.DeliveryReportRecipientSMS; +import java.time.OffsetDateTime; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +@TestWithResources +public class DeliveryReportRecipientDtoTest extends BaseTest { + + @GivenJsonResource("/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json") + DeliveryReportRecipientSMS deliveryReportRecipientSMSClient; + + @GivenJsonResource("/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json") + DeliveryReportRecipientMMS deliveryReportRecipientMMSClient; + + public static DeliveryReportRecipientSMS deliveryReportRecipientSMS = + new DeliveryReportRecipientSMS( + "01FC66621XXXXX119Z8PMV1QPQ", + "client reference", + OffsetDateTime.parse("2022-08-30T08:16:08.930Z"), + 401, + "+44231235674", + "Dispatched", + "applied originator", + "encoding", + 123, + "operator", + OffsetDateTime.parse("2022-08-30T08:16:08.150Z")); + + public static DeliveryReportRecipientMMS deliveryReportRecipientMMS = + new DeliveryReportRecipientMMS( + "01FC66621XXXXX119Z8PMV1QPQ", + "client reference", + OffsetDateTime.parse("2022-08-30T08:16:08.930Z"), + 401, + "+44231235674", + "Dispatched", + "applied originator", + "encoding", + 123, + "operator", + OffsetDateTime.parse("2022-08-30T08:16:08.150Z")); + + @Test + void deserializeDeliveryReportRecipientSMS() { + Assertions.assertThat(deliveryReportRecipientSMS) + .usingRecursiveComparison() + .isEqualTo(deliveryReportRecipientSMSClient); + } + + @Test + void deserializeDeliveryReportRecipientMMS() { + Assertions.assertThat(deliveryReportRecipientMMS) + .usingRecursiveComparison() + .isEqualTo(deliveryReportRecipientMMSClient); + } +} diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSBinaryDtoTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSBinaryDtoTest.java new file mode 100644 index 00000000..c4427793 --- /dev/null +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSBinaryDtoTest.java @@ -0,0 +1,34 @@ +package com.sinch.sdk.domains.sms.models.dto.v1.webhooks; + +import com.adelean.inject.resources.junit.jupiter.GivenJsonResource; +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.domains.sms.models.webhooks.IncomingSMSBinary; +import java.time.OffsetDateTime; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +@TestWithResources +public class IncomingSMSBinaryDtoTest extends BaseTest { + + @GivenJsonResource("/domains/sms/v1/webhooks/IncomingSMSBinary.json") + IncomingSMSBinary client; + + public static IncomingSMSBinary incomingSMSBinary = + new IncomingSMSBinary( + "VGV4dCBtZXNzYWdl", + "16051234567", + "01XXXXX21XXXXX119Z8P1XXXXX", + OffsetDateTime.parse("2022-08-24T14:15:22Z"), + "13185551234", + null, + "operator", + OffsetDateTime.parse("2022-08-24T14:15:44Z"), + "10010203040506070809000a0b0c0d0e0f"); + + @Test + void deserialize() { + + Assertions.assertThat(incomingSMSBinary).usingRecursiveComparison().isEqualTo(client); + } +} diff --git a/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSTextDtoTest.java b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSTextDtoTest.java new file mode 100644 index 00000000..c049199b --- /dev/null +++ b/openapi-contracts/src/test/java/com/sinch/sdk/domains/sms/models/dto/v1/webhooks/IncomingSMSTextDtoTest.java @@ -0,0 +1,33 @@ +package com.sinch.sdk.domains.sms.models.dto.v1.webhooks; + +import com.adelean.inject.resources.junit.jupiter.GivenJsonResource; +import com.adelean.inject.resources.junit.jupiter.TestWithResources; +import com.sinch.sdk.BaseTest; +import com.sinch.sdk.domains.sms.models.webhooks.IncomingSMSText; +import java.time.OffsetDateTime; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +@TestWithResources +public class IncomingSMSTextDtoTest extends BaseTest { + + @GivenJsonResource("/domains/sms/v1/webhooks/IncomingSMSText.json") + IncomingSMSText client; + + public static IncomingSMSText incomingSMSText = + new IncomingSMSText( + "This is a test message.", + "16051234567", + "01XXXXX21XXXXX119Z8P1XXXXX", + OffsetDateTime.parse("2022-08-24T14:15:22Z"), + "13185551234", + null, + "string", + null); + + @Test + void deserialize() { + + Assertions.assertThat(incomingSMSText).usingRecursiveComparison().isEqualTo(client); + } +} diff --git a/sample-app/src/main/java/com/sinch/sample/sms/webhooks/DeliveryReport.java b/sample-app/src/main/java/com/sinch/sample/sms/webhooks/DeliveryReport.java new file mode 100644 index 00000000..e90fed9a --- /dev/null +++ b/sample-app/src/main/java/com/sinch/sample/sms/webhooks/DeliveryReport.java @@ -0,0 +1,46 @@ +package com.sinch.sample.sms.webhooks; + +import com.sinch.sample.BaseApplication; +import com.sinch.sdk.domains.sms.models.webhooks.BaseDeliveryReport; +import java.io.IOException; +import java.util.logging.Logger; + +public class DeliveryReport extends BaseApplication { + private static final Logger LOGGER = Logger.getLogger(DeliveryReport.class.getName()); + + public DeliveryReport() throws IOException {} + + public static void main(String[] args) { + try { + new DeliveryReport().run(); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + e.printStackTrace(); + } + } + + public void run() { + + String payload = + "{\n" + + " \"type\": \"delivery_report_sms\",\n" + + " \"batch_id\": \"01FC66621XXXXX119Z8PMV1QPQ\",\n" + + " \"statuses\": [\n" + + " {\n" + + " \"code\": 0,\n" + + " \"count\": 1,\n" + + " \"recipients\": [\n" + + " \"44231235674\"\n" + + " ],\n" + + " \"status\": \"Delivered\"\n" + + " }\n" + + " ],\n" + + " \"total_message_count\": 1\n" + + "}"; + LOGGER.info("Convert payload" + payload); + + BaseDeliveryReport value = client.sms().webHooks().deliveryReport(payload); + + LOGGER.info("Conversion result: " + value); + } +} diff --git a/sample-app/src/main/java/com/sinch/sample/sms/webhooks/IncomingSMS.java b/sample-app/src/main/java/com/sinch/sample/sms/webhooks/IncomingSMS.java new file mode 100644 index 00000000..1889bd59 --- /dev/null +++ b/sample-app/src/main/java/com/sinch/sample/sms/webhooks/IncomingSMS.java @@ -0,0 +1,40 @@ +package com.sinch.sample.sms.webhooks; + +import com.sinch.sample.BaseApplication; +import com.sinch.sdk.domains.sms.models.webhooks.BaseIncomingSMS; +import java.io.IOException; +import java.util.logging.Logger; + +public class IncomingSMS extends BaseApplication { + private static final Logger LOGGER = Logger.getLogger(IncomingSMS.class.getName()); + + public IncomingSMS() throws IOException {} + + public static void main(String[] args) { + try { + new IncomingSMS().run(); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + e.printStackTrace(); + } + } + + public void run() { + + String payload = + "{\n" + + " \"body\": \"This is a test message.\",\n" + + " \"from\": \"16051234567\",\n" + + " \"id\": \"01XXXXX21XXXXX119Z8P1XXXXX\",\n" + + " \"operator_id\": \"string\",\n" + + " \"received_at\": \"2022-08-24T14:15:22Z\",\n" + + " \"to\": \"13185551234\",\n" + + " \"type\": \"mo_text\"\n" + + "}"; + LOGGER.info("Convert payload" + payload); + + BaseIncomingSMS value = client.sms().webHooks().incomingSMS(payload); + + LOGGER.info("Conversion result: " + value); + } +} diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportMMS.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportMMS.json new file mode 100644 index 00000000..a1bf1338 --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportMMS.json @@ -0,0 +1,15 @@ +{ + "type": "delivery_report_mms", + "batch_id": "01FC66621XXXXX119Z8PMV1QPQ", + "statuses": [ + { + "code": 0, + "count": 1, + "recipients": [ + "44231235674" + ], + "status": "Delivered" + } + ], + "total_message_count": 1 +} \ No newline at end of file diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json new file mode 100644 index 00000000..3d7c97b7 --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientMMS.json @@ -0,0 +1,15 @@ +{ + + "type": "recipient_delivery_report_mms", + "batch_id": "01FC66621XXXXX119Z8PMV1QPQ", + "recipient": "+44231235674", + "code": 401, + "status": "Dispatched", + "at": "2022-08-30T08:16:08.930Z", + "operator": "operator", + "applied_originator": "applied originator", + "client_reference": "client reference", + "encoding": "encoding", + "number_of_message_parts": 123, + "operator_status_at":"2022-08-30T08:16:08.150Z" +} \ No newline at end of file diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json new file mode 100644 index 00000000..896f6cad --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportRecipientSMS.json @@ -0,0 +1,15 @@ +{ + + "type": "recipient_delivery_report_sms", + "batch_id": "01FC66621XXXXX119Z8PMV1QPQ", + "recipient": "+44231235674", + "code": 401, + "status": "Dispatched", + "at": "2022-08-30T08:16:08.930Z", + "operator": "operator", + "applied_originator": "applied originator", + "client_reference": "client reference", + "encoding": "encoding", + "number_of_message_parts": 123, + "operator_status_at":"2022-08-30T08:16:08.150Z" +} \ No newline at end of file diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportSMS.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportSMS.json new file mode 100644 index 00000000..f35d84e4 --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/DeliveryReportSMS.json @@ -0,0 +1,15 @@ +{ + "type": "delivery_report_sms", + "batch_id": "01FC66621XXXXX119Z8PMV1QPQ", + "statuses": [ + { + "code": 0, + "count": 1, + "recipients": [ + "44231235674" + ], + "status": "Delivered" + } + ], + "total_message_count": 1 +} \ No newline at end of file diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSBinary.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSBinary.json new file mode 100644 index 00000000..2c698b57 --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSBinary.json @@ -0,0 +1,11 @@ +{ + "body": "VGV4dCBtZXNzYWdl", + "from": "16051234567", + "id": "01XXXXX21XXXXX119Z8P1XXXXX", + "operator_id": "operator", + "received_at": "2022-08-24T14:15:22Z", + "sent_at": "2022-08-24T14:15:44Z", + "to": "13185551234", + "type": "mo_binary", + "udh": "10010203040506070809000a0b0c0d0e0f" +} \ No newline at end of file diff --git a/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSText.json b/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSText.json new file mode 100644 index 00000000..a97b50f0 --- /dev/null +++ b/test-resources/src/test/resources/domains/sms/v1/webhooks/IncomingSMSText.json @@ -0,0 +1,9 @@ +{ + "body": "This is a test message.", + "from": "16051234567", + "id": "01XXXXX21XXXXX119Z8P1XXXXX", + "operator_id": "string", + "received_at": "2022-08-24T14:15:22Z", + "to": "13185551234", + "type": "mo_text" +} \ No newline at end of file