diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65f5f50a8..5a6a0c121 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [8.1.0] - 2024-01-??
+- Added various missing fields in Messages API:
+ - `webhook_version` and `webhook_url` for all outbound messages
+ - MMS vCard `caption` (outbound)
+ - MMS image `caption` (inbound)
+ - Whatsapp file `name` (outbound)
+ - Whatsapp `context_status` and `referral` (inbound)
+ - SMS `count_total` and `network_code` (inbound)
+ - SMS `ttl`, `encoding_type`, `content_id` and `entity_id` (outbound)
+ - Whatsapp conversation type and ID (status update)
- Added optional `from` parameter to Verify v2 SMS workflow
- Fixed `length` not being set in `VerifyClient.verify` overload method
- Fixed incorrect HTTP method for updating Video Broadcast layout
diff --git a/src/main/java/com/vonage/client/messages/InboundMessage.java b/src/main/java/com/vonage/client/messages/InboundMessage.java
index 23744ea56..3f590279b 100644
--- a/src/main/java/com/vonage/client/messages/InboundMessage.java
+++ b/src/main/java/com/vonage/client/messages/InboundMessage.java
@@ -15,10 +15,7 @@
*/
package com.vonage.client.messages;
-import com.fasterxml.jackson.annotation.JsonAnyGetter;
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.*;
import com.vonage.client.Jsonable;
import com.vonage.client.messages.sms.SmsInboundMetadata;
import com.vonage.client.messages.whatsapp.*;
@@ -28,7 +25,7 @@
import java.util.UUID;
/**
- * Convenience class representing an inbound message webhook.
+ * Convenience class representing an inbound message webhook. This maps all known fields for all message types.
*
* Refer to the
* Messages API Webhook reference
@@ -43,10 +40,22 @@ protected static class UrlWrapper {
@JsonProperty("url") protected URI url;
}
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ protected static class UrlWrapperWithCaption extends UrlWrapper {
+ @JsonProperty("caption") protected String caption;
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ protected static class Whatsapp {
+ @JsonProperty("referral") protected Referral referral;
+ }
+
protected InboundMessage() {}
@JsonAnySetter protected Map unknownProperties;
+ @JsonProperty("whatsapp") private Whatsapp whatsapp;
+
@JsonProperty("timestamp") protected Instant timestamp;
@JsonProperty("channel") protected Channel channel;
@JsonProperty("message_type") protected MessageType messageType;
@@ -57,7 +66,7 @@ protected InboundMessage() {}
@JsonProperty("provider_message") String providerMessage;
@JsonProperty("text") protected String text;
- @JsonProperty("image") protected UrlWrapper image;
+ @JsonProperty("image") protected UrlWrapperWithCaption image;
@JsonProperty("audio") protected UrlWrapper audio;
@JsonProperty("video") protected UrlWrapper video;
@JsonProperty("file") protected UrlWrapper file;
@@ -65,6 +74,7 @@ protected InboundMessage() {}
@JsonProperty("sticker") protected UrlWrapper sticker;
@JsonProperty("profile") protected Profile whatsappProfile;
+ @JsonProperty("context_status") protected ContextStatus whatsappContextStatus;
@JsonProperty("context") protected Context whatsappContext;
@JsonProperty("location") protected Location whatsappLocation;
@JsonProperty("reply") protected Reply whatsappReply;
@@ -164,6 +174,17 @@ public URI getImageUrl() {
return image != null ? image.url : null;
}
+ /**
+ * Additional text accompanying the image. Applicable to MMS image messages only.
+ *
+ * @return The image caption if present, or {@code null} if not applicable.
+ *
+ * @since 8.1.0
+ */
+ public String getImageCaption() {
+ return image != null ? image.caption : null;
+ }
+
/**
* If {@linkplain #getMessageType()} is {@linkplain MessageType#AUDIO}, returns the URL of the audio.
*
@@ -270,6 +291,20 @@ public Context getWhatsappContext() {
return whatsappContext;
}
+ /**
+ * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP}, returns an enum indicating whether there
+ * is a context for this inbound message. If there is a context, and it is available, the context details will be
+ * contained in a context object. If there is a context, but it is unavailable,or if there is no context for
+ * message ({@linkplain ContextStatus#NONE}), then there will be no context object included in the body.
+ *
+ * @return The deserialized WhatsApp context status, or {@code null} if not applicable.
+ *
+ * @since 8.1.0
+ */
+ public ContextStatus getWhatsappContextStatus() {
+ return whatsappContextStatus;
+ }
+
/**
* If the {@linkplain #getChannel()} is {@linkplain Channel#SMS}, returns the usage
* information (charged incurred for the message).
@@ -290,6 +325,19 @@ public SmsInboundMetadata getSmsMetadata() {
return smsMetadata;
}
+ /**
+ * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and a content referral is present in
+ * the message, returns the metadata related to the post or advertisement that the user clicked on.
+ *
+ * @return The Whatsapp referral object, or {@code null} if not present or applicable.
+ *
+ * @since 8.1.0
+ */
+ @JsonIgnore
+ public Referral getWhatsappReferral() {
+ return whatsapp != null ? whatsapp.referral : null;
+ }
+
/**
* Constructs an instance of this class from a JSON payload. Known fields will be mapped, whilst
* unknown fields can be manually obtained through the {@linkplain #getUnmappedProperties()} method.
diff --git a/src/main/java/com/vonage/client/messages/MessageRequest.java b/src/main/java/com/vonage/client/messages/MessageRequest.java
index 88c3ce62a..515738300 100644
--- a/src/main/java/com/vonage/client/messages/MessageRequest.java
+++ b/src/main/java/com/vonage/client/messages/MessageRequest.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.vonage.client.Jsonable;
import com.vonage.client.common.E164;
+import java.net.URI;
import java.util.Objects;
/**
@@ -36,6 +37,8 @@ public abstract class MessageRequest implements Jsonable {
final MessageType messageType;
final Channel channel;
final String clientRef;
+ final URI webhookUrl;
+ final MessagesVersion webhookVersion;
protected String from, to;
/**
@@ -57,6 +60,8 @@ protected MessageRequest(Builder, ?> builder, Channel channel, MessageType mes
clientRef = validateClientReference(builder.clientRef);
from = builder.from;
to = builder.to;
+ webhookUrl = builder.webhookUrl;
+ webhookVersion = builder.webhookVersion;
validateSenderAndRecipient(from, to);
}
@@ -115,6 +120,16 @@ public String getClientRef() {
return clientRef;
}
+ @JsonProperty("webhook_url")
+ public URI getWebhookUrl() {
+ return webhookUrl;
+ }
+
+ @JsonProperty("webhook_version")
+ public MessagesVersion getWebhookVersion() {
+ return webhookVersion;
+ }
+
@Override
public String toString() {
return getClass().getSimpleName()+' '+toJson();
@@ -133,6 +148,8 @@ public String toString() {
@SuppressWarnings("unchecked")
public abstract static class Builder> {
protected String from, to, clientRef;
+ protected URI webhookUrl;
+ protected MessagesVersion webhookVersion;
/**
* Protected constructor to prevent users from explicitly creating this object.
@@ -178,6 +195,54 @@ public B clientRef(String clientRef) {
return (B) this;
}
+ /**
+ * (OPTIONAL)
+ * Specifies the URL to which Status Webhook messages will be sent for this particular message.
+ * Overrides account-level and application-level Status Webhook url settings on a per-message basis.
+ *
+ * @param webhookUrl The status webhook URL as a string.
+ *
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public B webhookUrl(String webhookUrl) {
+ return webhookUrl(URI.create(webhookUrl));
+ }
+
+ /**
+ * (OPTIONAL)
+ * Specifies the URL to which Status Webhook messages will be sent for this particular message.
+ * Overrides account-level and application-level Status Webhook url settings on a per-message basis.
+ *
+ * @param webhookUrl The status webhook URL.
+ *
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ private B webhookUrl(URI webhookUrl) {
+ this.webhookUrl = webhookUrl;
+ return (B) this;
+ }
+
+ /**
+ * Specifies which version of the Messages API will be used to send Status Webhook messages for
+ * this particular message. For example, if {@linkplain MessagesVersion#V0_1} is set, then the
+ * JSON body of Status Webhook messages for this message will be sent in Messages v0.1 format.
+ * Over-rides account-level and application-level API version settings on a per-message basis.
+ *
+ * @param webhookVersion The messages API version enum.
+ *
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public B webhookVersion(MessagesVersion webhookVersion) {
+ this.webhookVersion = webhookVersion;
+ return (B) this;
+ }
+
/**
* Builds the MessageRequest.
*
diff --git a/src/main/java/com/vonage/client/messages/MessageStatus.java b/src/main/java/com/vonage/client/messages/MessageStatus.java
index 549884bf8..6e761e296 100644
--- a/src/main/java/com/vonage/client/messages/MessageStatus.java
+++ b/src/main/java/com/vonage/client/messages/MessageStatus.java
@@ -17,6 +17,7 @@
import com.fasterxml.jackson.annotation.*;
import com.vonage.client.Jsonable;
+import com.vonage.client.messages.whatsapp.ConversationType;
import java.net.URI;
import java.time.Instant;
import java.util.Currency;
@@ -186,10 +187,36 @@ public String toString() {
}
}
+ @JsonInclude(value = JsonInclude.Include.NON_NULL)
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static class Destination {
+ @JsonProperty("network_code") String networkCode;
+ }
+
+ @JsonInclude(value = JsonInclude.Include.NON_NULL)
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static class Sms {
+ @JsonProperty("count_total") Integer countTotal;
+ }
+
+ @JsonInclude(value = JsonInclude.Include.NON_NULL)
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static class Whatsapp {
+ static class Conversation {
+ static class Origin {
+ @JsonProperty("type") ConversationType type;
+ }
+ @JsonProperty("id") String id;
+ @JsonProperty("origin") Origin origin;
+ }
+ @JsonProperty("conversation") Conversation conversation;
+ }
+
protected MessageStatus() {
}
@JsonAnySetter protected Map unknownProperties;
+
@JsonProperty("timestamp") protected Instant timestamp;
@JsonProperty("message_uuid") protected UUID messageUuid;
@JsonProperty("to") protected String to;
@@ -200,6 +227,10 @@ protected MessageStatus() {
@JsonProperty("error") protected Error error;
@JsonProperty("usage") protected Usage usage;
+ @JsonProperty("destination") private Destination destination;
+ @JsonProperty("sms") private Sms sms;
+ @JsonProperty("whatsapp") private Whatsapp whatsapp;
+
/**
* Unique identifier of the message that was sent, as returned in {@link MessageResponse#getMessageUuid()}.
@@ -283,6 +314,62 @@ public Usage getUsage() {
return usage;
}
+ /**
+ * If {@linkplain #getChannel()} is {@linkplain Channel#SMS} or {@linkplain Channel#MMS},
+ * returns the network code for the destination.
+ *
+ * @return The mobile network code as a string, or {@code null} if not applicable.
+ *
+ * @since 8.1.0
+ */
+ @JsonIgnore
+ public String getDestinationNetworkCode() {
+ return destination != null ? destination.networkCode : null;
+ }
+
+ /**
+ * {@linkplain #getChannel()} is {@linkplain Channel#SMS}, returns the number of SMS messages concatenated together
+ * to comprise the submitted message. SMS messages are 160 characters, if a submitted message exceeds that size it
+ * is sent as multiple SMS messages. This number indicates how many SMS messages are required.
+ *
+ * @return The number of SMS messages used for this message, or {@code null} if not applicable.
+ *
+ * @since 8.1.0
+ */
+ @JsonIgnore
+ public Integer getSmsTotalCount() {
+ return sms != null ? sms.countTotal : null;
+ }
+
+ /**
+ * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and {@linkplain #getStatus()} is
+ * {@linkplain Status#DELIVERED}, returns the conversation's origin type.
+ *
+ * @return The WhatsApp conversation category as an enum, {@code null} if absent or not applicable.
+ *
+ * @since 8.1.0
+ */
+ @JsonIgnore
+ public ConversationType getWhatsappConversationType() {
+ return whatsapp != null &&
+ whatsapp.conversation != null &&
+ whatsapp.conversation.origin != null ?
+ whatsapp.conversation.origin.type : null;
+ }
+
+ /**
+ * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and {@linkplain #getStatus()} is
+ * {@linkplain Status#DELIVERED}, returns the conversation ID of the message that triggered this callback.
+ *
+ * @return The WhatsApp conversation ID, {@code null} if absent or not applicable.
+ *
+ * @since 8.1.0
+ */
+ @JsonIgnore
+ public String getWhatsappConversationId() {
+ return whatsapp != null && whatsapp.conversation != null ? whatsapp.conversation.id : null;
+ }
+
/**
* Catch-all for properties which are not mapped by this class during deserialization.
*
@@ -318,11 +405,18 @@ public boolean equals(Object o) {
Objects.equals(to, that.to) && Objects.equals(from, that.from) &&
status == that.status && channel == that.channel &&
Objects.equals(clientRef, that.clientRef) &&
- Objects.equals(error, that.error) && Objects.equals(usage, that.usage);
+ Objects.equals(error, that.error) && Objects.equals(usage, that.usage) &&
+ Objects.equals(getDestinationNetworkCode(), that.getDestinationNetworkCode()) &&
+ Objects.equals(getSmsTotalCount(), that.getSmsTotalCount()) &&
+ Objects.equals(getWhatsappConversationId(), that.getWhatsappConversationId()) &&
+ Objects.equals(getWhatsappConversationType(), that.getWhatsappConversationType());
}
@Override
public int hashCode() {
- return Objects.hash(timestamp, messageUuid, to, from, status, channel, clientRef, error, usage);
+ return Objects.hash(
+ timestamp, messageUuid, to, from, status, channel,
+ clientRef, error, usage, getDestinationNetworkCode(), getSmsTotalCount()
+ );
}
}
diff --git a/src/main/java/com/vonage/client/messages/MessagesVersion.java b/src/main/java/com/vonage/client/messages/MessagesVersion.java
new file mode 100644
index 000000000..7aaf63aca
--- /dev/null
+++ b/src/main/java/com/vonage/client/messages/MessagesVersion.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 Vonage
+ *
+ * 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.
+ */
+package com.vonage.client.messages;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Represents the possible versions used for webhooks in the Messages API.
+ *
+ * @since 8.1.0
+ */
+public enum MessagesVersion {
+ /**
+ * v0.1
+ */
+ V0_1,
+
+ /**
+ * v1
+ */
+ V1;
+
+ @JsonValue
+ @Override
+ public String toString() {
+ return name().toLowerCase().replace('_', '.');
+ }
+}
diff --git a/src/main/java/com/vonage/client/messages/internal/MessagePayload.java b/src/main/java/com/vonage/client/messages/internal/MessagePayload.java
index 80463459c..050a739fa 100644
--- a/src/main/java/com/vonage/client/messages/internal/MessagePayload.java
+++ b/src/main/java/com/vonage/client/messages/internal/MessagePayload.java
@@ -29,7 +29,7 @@
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class MessagePayload {
protected URI url;
- protected String caption;
+ protected String caption, name;
public MessagePayload(String url) {
this.url = URI.create(url);
@@ -42,6 +42,11 @@ public MessagePayload(String url, String caption) {
}
}
+ public MessagePayload(String url, String caption, String name) {
+ this(url, caption);
+ this.name = name;
+ }
+
@JsonProperty("url")
public URI getUrl() {
return url;
@@ -52,6 +57,11 @@ public String getCaption() {
return caption;
}
+ @JsonProperty("name")
+ public String getName() {
+ return name;
+ }
+
public static void validateExtension(String path, String... allowed) {
int lastDot = path.lastIndexOf('.');
if (lastDot < 1) return;
diff --git a/src/main/java/com/vonage/client/messages/mms/MmsAudioRequest.java b/src/main/java/com/vonage/client/messages/mms/MmsAudioRequest.java
index fe83ccd02..437dac840 100644
--- a/src/main/java/com/vonage/client/messages/mms/MmsAudioRequest.java
+++ b/src/main/java/com/vonage/client/messages/mms/MmsAudioRequest.java
@@ -39,8 +39,6 @@ public static Builder builder() {
}
public static final class Builder extends MmsRequest.Builder {
- String caption;
-
Builder() {}
/**
@@ -50,6 +48,7 @@ public static final class Builder extends MmsRequest.Builder {
- String caption;
-
Builder() {}
/**
@@ -52,6 +50,7 @@ public static final class Builder extends MmsRequest.Builder builder, MessageType messageType) {
@SuppressWarnings("unchecked")
protected abstract static class Builder> extends MessageRequest.Builder {
- String url;
+ String url, caption;
protected B url(String url) {
this.url = url;
return (B) this;
}
+
+ protected B caption(String caption) {
+ this.caption = caption;
+ return (B) this;
+ }
}
}
diff --git a/src/main/java/com/vonage/client/messages/mms/MmsVcardRequest.java b/src/main/java/com/vonage/client/messages/mms/MmsVcardRequest.java
index 40a2b2450..7e66487af 100644
--- a/src/main/java/com/vonage/client/messages/mms/MmsVcardRequest.java
+++ b/src/main/java/com/vonage/client/messages/mms/MmsVcardRequest.java
@@ -25,7 +25,7 @@ public final class MmsVcardRequest extends MmsRequest {
MmsVcardRequest(Builder builder) {
super(builder, MessageType.VCARD);
- payload = new MessagePayload(builder.url);
+ payload = new MessagePayload(builder.url, builder.caption);
payload.validateUrlExtension("vcf");
}
@@ -52,10 +52,21 @@ public Builder url(String url) {
return super.url(url);
}
+ /**
+ * (OPTIONAL)
+ * Additional text to accompany the vCard. Must be between 1 and 2000 characters.
+ *
+ * @param caption The caption string.
+ * @return This builder.
+ */
+ @Override
+ public Builder caption(String caption) {
+ return super.caption(caption);
+ }
+
@Override
public MmsVcardRequest build() {
return new MmsVcardRequest(this);
}
}
-
}
diff --git a/src/main/java/com/vonage/client/messages/mms/MmsVideoRequest.java b/src/main/java/com/vonage/client/messages/mms/MmsVideoRequest.java
index abbfe5c1e..0787febcd 100644
--- a/src/main/java/com/vonage/client/messages/mms/MmsVideoRequest.java
+++ b/src/main/java/com/vonage/client/messages/mms/MmsVideoRequest.java
@@ -39,8 +39,6 @@ public static Builder builder() {
}
public static final class Builder extends MmsRequest.Builder {
- String caption;
-
Builder() {}
/**
@@ -50,6 +48,7 @@ public static final class Builder extends MmsRequest.Builder {
- String text;
+ String text, contentId, entityId;
+ Integer ttl;
+ EncodingType encodingType;
Builder() {}
@@ -60,6 +78,69 @@ public Builder text(String text) {
return this;
}
+ /**
+ * (OPTIONAL)
+ * The duration in milliseconds the delivery of an SMS will be attempted. By default, Vonage attempts
+ * delivery for 72 hours, however the maximum effective value depends on the operator and is typically
+ * 24 to 48 hours. We recommend this value should be kept at its default or at least 30 minutes.
+ *
+ * @param ttl The time-to-live for this message before abandoning delivery attempts, in milliseconds.
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public Builder ttl(int ttl) {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * (OPTIONAL)
+ * The encoding type to use for the message. If set to either {@linkplain EncodingType#TEXT} or
+ * {@linkplain EncodingType#UNICODE}, the specified type will be used. If set to
+ * {@linkplain EncodingType#AUTO} (the default), the Messages API will automatically set the type based
+ * on the content of text; i.e. if unicode characters are detected in text, then the message will be
+ * encoded as unicode, and otherwise as text.
+ *
+ * @param encodingType The message encoding type as an enum.
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public Builder encodingType(EncodingType encodingType) {
+ this.encodingType = encodingType;
+ return this;
+ }
+
+ /**
+ * (OPTIONAL)
+ * A string parameter that satisfies regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param contentId The content ID as a string.
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public Builder contentId(String contentId) {
+ this.contentId = contentId;
+ return this;
+ }
+
+ /**
+ * (OPTIONAL)
+ * A string parameter that satisfies regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param entityId The entity ID as a string.
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public Builder entityId(String entityId) {
+ this.entityId = entityId;
+ return this;
+ }
+
+ @Override
public SmsTextRequest build() {
return new SmsTextRequest(this);
}
diff --git a/src/main/java/com/vonage/client/messages/whatsapp/Context.java b/src/main/java/com/vonage/client/messages/whatsapp/Context.java
index 169f90ab6..0cbce0b8f 100644
--- a/src/main/java/com/vonage/client/messages/whatsapp/Context.java
+++ b/src/main/java/com/vonage/client/messages/whatsapp/Context.java
@@ -57,7 +57,10 @@ public UUID getMessageUuid() {
* Only applies to Order messages.
*
* @return The referred product details, or {@code null} if not applicable.
+ *
+ * @deprecated This will be moved in a future release.
*/
+ @Deprecated
@JsonProperty("whatsapp_referred_product")
public ReferredProduct getReferredProduct() {
return referredProduct;
diff --git a/src/main/java/com/vonage/client/messages/whatsapp/ContextStatus.java b/src/main/java/com/vonage/client/messages/whatsapp/ContextStatus.java
new file mode 100644
index 000000000..02517ee66
--- /dev/null
+++ b/src/main/java/com/vonage/client/messages/whatsapp/ContextStatus.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 Vonage
+ *
+ * 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.
+ */
+package com.vonage.client.messages.whatsapp;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+/**
+ * Represents the WhatsApp {@code context_status} field in {@link com.vonage.client.messages.InboundMessage}.
+ *
+ * Describes whether there is a context for this inbound message or not. If there is a context, and it is available,
+ * the context details will be contained in a context object. If there is a context, but it is unavailable,
+ * or if there is no context for message (none), then there will be no context object included in the body.
+ *
+ * @since 8.1.0
+ */
+public enum ContextStatus {
+ NONE,
+ AVAILABLE,
+ UNAVAILABLE;
+
+ @JsonValue
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+
+ @JsonCreator
+ public static ContextStatus fromString(String value) {
+ if (value == null || value.trim().isEmpty()) return null;
+ return ContextStatus.valueOf(value.toUpperCase());
+ }
+}
diff --git a/src/main/java/com/vonage/client/messages/whatsapp/ConversationType.java b/src/main/java/com/vonage/client/messages/whatsapp/ConversationType.java
new file mode 100644
index 000000000..a4ad5884e
--- /dev/null
+++ b/src/main/java/com/vonage/client/messages/whatsapp/ConversationType.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 Vonage
+ *
+ * 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.
+ */
+package com.vonage.client.messages.whatsapp;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.vonage.client.messages.MessageStatus;
+
+/**
+ * Represents the conversation category as returned by {@link MessageStatus#getWhatsappConversationType()}.
+ *
+ * @since 8.1.0
+ */
+public enum ConversationType {
+ MARKETING,
+ UTILITY,
+ AUTHENTICATION,
+ REFERRAL_CONVERSION,
+ SERVICE;
+
+ @JsonValue
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+
+ @JsonCreator
+ public static ConversationType fromString(String value) {
+ if (value == null || value.trim().isEmpty()) return null;
+ return ConversationType.valueOf(value.toUpperCase());
+ }
+}
diff --git a/src/main/java/com/vonage/client/messages/whatsapp/Referral.java b/src/main/java/com/vonage/client/messages/whatsapp/Referral.java
new file mode 100644
index 000000000..d384155ba
--- /dev/null
+++ b/src/main/java/com/vonage/client/messages/whatsapp/Referral.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 Vonage
+ *
+ * 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.
+ */
+package com.vonage.client.messages.whatsapp;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.vonage.client.messages.InboundMessage;
+import java.net.URI;
+
+/**
+ * This is only present for situations where a user has clicked on a 'WhatsApp' button embedded in an advertisement
+ * or post on Facebook. Clicking on the button directs the user to the WhatsApp app from where they can send a message.
+ * The inbound message will contain this object which includes details of the Facebook advertisement or post which
+ * contained the embedded button. This is used in {@link InboundMessage#getWhatsappReferral()}.
+ *
+ * @since 8.1.0
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public final class Referral {
+ private String body, headline, sourceId, sourceType;
+ private URI sourceUrl;
+
+ Referral() {}
+
+ /**
+ * Body text of the referring advertisement or post.
+ *
+ * @return The referral body.
+ */
+ @JsonProperty("body")
+ public String getBody() {
+ return body;
+ }
+
+ /**
+ * Headline text of the referring advertisement or post.
+ *
+ * @return The referral headline.
+ */
+ @JsonProperty("headline")
+ public String getHeadline() {
+ return headline;
+ }
+
+ /**
+ * Meta/WhatsApp ID of the referring advertisement or post.
+ *
+ * @return The source referral ID as a String.
+ */
+ @JsonProperty("source_id")
+ public String getSourceId() {
+ return sourceId;
+ }
+
+ /**
+ * The type of the referring advertisement or post.
+ *
+ * @return The source referral type as a String.
+ */
+ @JsonProperty("source_type")
+ public String getSourceType() {
+ return sourceType;
+ }
+
+ /**
+ * A URL referencing the content of the media shown in the advertisement when the user clicked to send a message.
+ *
+ * @return Link to the advertised content.
+ */
+ @JsonProperty("source_url")
+ public URI getSourceUrl() {
+ return sourceUrl;
+ }
+}
diff --git a/src/main/java/com/vonage/client/messages/whatsapp/WhatsappFileRequest.java b/src/main/java/com/vonage/client/messages/whatsapp/WhatsappFileRequest.java
index deebad5f6..5f838c65b 100644
--- a/src/main/java/com/vonage/client/messages/whatsapp/WhatsappFileRequest.java
+++ b/src/main/java/com/vonage/client/messages/whatsapp/WhatsappFileRequest.java
@@ -26,7 +26,7 @@ public final class WhatsappFileRequest extends WhatsappRequest {
WhatsappFileRequest(Builder builder) {
super(builder, MessageType.FILE);
- file = new MessagePayload(builder.url, builder.caption);
+ file = new MessagePayload(builder.url, builder.caption, builder.name);
}
@JsonProperty("file")
@@ -39,7 +39,7 @@ public static Builder builder() {
}
public static final class Builder extends WhatsappRequest.Builder {
- String url, caption;
+ String url, caption, name;
Builder() {}
@@ -68,10 +68,24 @@ public Builder caption(String caption) {
return this;
}
+ /**
+ * (OPTIONAL)
+ * Specifies the name of the file being sent. If not included, the value for caption will be used as
+ * the file name. If neither name nor caption are included, the file name will be parsed from the url.
+ *
+ * @param name The file name.
+ * @return This builder.
+ *
+ * @since 8.1.0
+ */
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
@Override
public WhatsappFileRequest build() {
return new WhatsappFileRequest(this);
}
}
-
}
diff --git a/src/test/java/com/vonage/client/messages/InboundMessageTest.java b/src/test/java/com/vonage/client/messages/InboundMessageTest.java
index ffce35dbf..97a51b877 100644
--- a/src/test/java/com/vonage/client/messages/InboundMessageTest.java
+++ b/src/test/java/com/vonage/client/messages/InboundMessageTest.java
@@ -17,9 +17,10 @@
import com.vonage.client.VonageResponseParseException;
import com.vonage.client.messages.sms.SmsInboundMetadata;
+import com.vonage.client.messages.whatsapp.Order;
import com.vonage.client.messages.whatsapp.*;
-import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.*;
import java.net.URI;
import java.time.Instant;
import java.util.Currency;
@@ -91,6 +92,11 @@ void assertEqualsSmsWithUsageAndMetadata(InboundMessage im) {
assertEquals("HELLO", metadata.getKeyword());
}
+ @Test
+ public void testFromJsonInvalid() {
+ assertThrows(VonageResponseParseException.class, () -> InboundMessage.fromJson("{malformed]"));
+ }
+
@SuppressWarnings("unchecked")
@Test
public void testUnknownProperty() {
@@ -175,9 +181,11 @@ public void testMmsVcard() {
@Test
public void testImageOnly() {
URI image = URI.create("https://www.example.org/path/to/image.png");
- String json = "{\"image\": {\"url\":\""+image+"\"}}";
+ String caption = "Alt text accompanying the image";
+ String json = "{\"image\": {\"url\":\""+image+"\",\"caption\":\""+caption+"\"}}";
InboundMessage im = InboundMessage.fromJson(json);
assertEquals(image, im.getImageUrl());
+ assertEquals(caption, im.getImageCaption());
}
@Test
@@ -229,7 +237,7 @@ public void testWhatsappLocationOnly() {
@Test
public void testWhatsappReplyOnly() {
- String id = "row1", title = "9am", description = "Select 9am appointmaent time";
+ String id = "row1", title = "9am", description = "Select 9am appointment time";
String json = "{\"reply\":{\"id\":\""+id+"\",\"title\":\""+title+"\",\"description\":\""+description+"\"}}";
InboundMessage im = InboundMessage.fromJson(json);
Reply reply = im.getWhatsappReply();
@@ -274,6 +282,7 @@ public void testWhatsappOrderOnly() {
public void testWhatsappContextForOrderOnly() {
UUID messageId = UUID.randomUUID();
String cid = "1267260820787549", pid = "r07qei73l7", from = "447700900001", json = "{\n" +
+ " \"context_status\": \"available\",\n" +
" \"context\": {\n" +
" \"whatsapp_referred_product\": {\n" +
" \"catalog_id\": \""+cid+"\",\n" +
@@ -284,6 +293,7 @@ public void testWhatsappContextForOrderOnly() {
" }" +
"}";
InboundMessage im = InboundMessage.fromJson(json);
+ assertEquals(ContextStatus.AVAILABLE, im.getWhatsappContextStatus());
Context context = im.getWhatsappContext();
assertNotNull(context);
assertEquals(messageId, context.getMessageUuid());
@@ -295,10 +305,11 @@ public void testWhatsappContextForOrderOnly() {
}
@Test
- public void testWhatsappContextAndProfileOnly() {
+ public void testWhatsappStatusContextAndProfileOnly() {
UUID messageId = UUID.randomUUID();
String name = "Jane Smith", from = "447700900000", json = "{\n" +
" \"profile\":{\"name\": \""+name+"\"},\n" +
+ " \"context_status\": \"unavailable\",\n" +
" \"context\": {\n" +
" \"message_uuid\": \""+messageId+"\",\n" +
" \"message_from\": \""+from+"\"\n" +
@@ -308,6 +319,7 @@ public void testWhatsappContextAndProfileOnly() {
Profile profile = im.getWhatsappProfile();
assertNotNull(profile);
assertEquals(name, profile.getName());
+ assertEquals(ContextStatus.UNAVAILABLE.toString(), im.getWhatsappContextStatus().toString());
Context context = im.getWhatsappContext();
assertNotNull(context);
assertEquals(messageId, context.getMessageUuid());
@@ -315,7 +327,29 @@ public void testWhatsappContextAndProfileOnly() {
}
@Test
- public void testFromJsonInvalid() {
- assertThrows(VonageResponseParseException.class, () -> InboundMessage.fromJson("{malformed]"));
+ public void testWhatsappReferralOnly() {
+ String body = "Check out our new product offering",
+ headline = "New Products!", sourceId = "212731241638144",
+ sourceType = "post", sourceUrl = "https://fb.me/2ZulEu42P",
+ json = "{\n" +
+ " \"whatsapp\": {\n" +
+ " \"referral\": {\n" +
+ " \"body\": \""+body+"\",\n" +
+ " \"headline\": \""+headline+"\",\n" +
+ " \"source_id\": \""+sourceId+"\",\n" +
+ " \"source_type\": \""+sourceType+"\",\n" +
+ " \"source_url\": \""+sourceUrl+"\"\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ InboundMessage im = InboundMessage.fromJson(json);
+ Referral referral = im.getWhatsappReferral();
+ assertNotNull(referral);
+ assertEquals(body, referral.getBody());
+ assertEquals(headline, referral.getHeadline());
+ assertEquals(sourceId, referral.getSourceId());
+ assertEquals(sourceType, referral.getSourceType());
+ assertEquals(URI.create(sourceUrl), referral.getSourceUrl());
}
}
diff --git a/src/test/java/com/vonage/client/messages/MessageRequestTest.java b/src/test/java/com/vonage/client/messages/MessageRequestTest.java
index 497c36912..c7fc2fc68 100644
--- a/src/test/java/com/vonage/client/messages/MessageRequestTest.java
+++ b/src/test/java/com/vonage/client/messages/MessageRequestTest.java
@@ -52,10 +52,14 @@ private ConcreteMessageRequest(Builder builder) {
public void testSerializeAllFields() {
MessageRequest smr = ConcreteMessageRequest.builder(MessageType.VIDEO, Channel.MMS)
.from("447900000009").to("12002009000")
- .clientRef("<40 character string").build();
+ .clientRef("<40 character string")
+ .webhookUrl("https://example.com/status")
+ .webhookVersion(MessagesVersion.V1).build();
String generatedJson = smr.toJson();
assertTrue(generatedJson.contains("\"client_ref\":\"<40 character string\""));
+ assertTrue(generatedJson.contains("\"webhook_url\":\"https://example.com/status\""));
+ assertTrue(generatedJson.contains("\"webhook_version\":\"v1\""));
assertTrue(generatedJson.contains("\"from\":\"447900000009\""));
assertTrue(generatedJson.contains("\"to\":\"12002009000\""));
assertTrue(generatedJson.contains("\"channel\":\"mms\""));
@@ -63,12 +67,14 @@ public void testSerializeAllFields() {
}
@Test
- public void testSerializeFieldsWithoutClientRef() {
+ public void testSerializeFieldsRequiredOnly() {
MessageRequest smr = ConcreteMessageRequest.builder(MessageType.IMAGE, Channel.VIBER)
.from("447900000009").to("12002009000").build();
String generatedJson = smr.toJson();
assertFalse(generatedJson.contains("client_ref"));
+ assertFalse(generatedJson.contains("webhook_url"));
+ assertFalse(generatedJson.contains("webhook_version"));
assertTrue(generatedJson.contains("\"from\":\"447900000009\""));
assertTrue(generatedJson.contains("\"to\":\"12002009000\""));
assertTrue(generatedJson.contains("\"channel\":\"viber_service\""));
diff --git a/src/test/java/com/vonage/client/messages/MessageStatusTest.java b/src/test/java/com/vonage/client/messages/MessageStatusTest.java
index d468d9cbe..96151538a 100644
--- a/src/test/java/com/vonage/client/messages/MessageStatusTest.java
+++ b/src/test/java/com/vonage/client/messages/MessageStatusTest.java
@@ -17,6 +17,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.vonage.client.VonageUnexpectedException;
+import com.vonage.client.messages.whatsapp.ConversationType;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.net.URI;
@@ -30,12 +31,12 @@ public class MessageStatusTest {
@Test
public void testSerdesAllFields() {
UUID messageUuid = UUID.randomUUID();
- String to = "447700900000", from = "447700900001";
+ String to = "447700900000", from = "447700900001", networkCode = "54321";
String timestamp = "2020-01-01T14:00:03.010Z";
MessageStatus.Status status = MessageStatus.Status.SUBMITTED;
Channel channel = Channel.SMS;
URI type = URI.create("https://developer.nexmo.com/api-errors/messages-olympus#1000");
- int title = 1000;
+ int title = 1000, countTotal = 3;
String detail = "Throttled - You have exceeded the submission capacity allowed on this account.";
String instance = "bf0ca0bf927b3b52e3cb03217e1a1ddf";
Currency currency = Currency.getInstance("EUR");
@@ -48,6 +49,19 @@ public void testSerdesAllFields() {
MessageStatus.Usage usage = new MessageStatus.Usage();
usage.price = price;
usage.currency = currency;
+ MessageStatus.Sms sms = new MessageStatus.Sms();
+ sms.countTotal = countTotal;
+ MessageStatus.Destination destination = new MessageStatus.Destination();
+ destination.networkCode = networkCode;
+ MessageStatus.Whatsapp whatsapp = new MessageStatus.Whatsapp();
+ MessageStatus.Whatsapp.Conversation conversation = new MessageStatus.Whatsapp.Conversation();
+ whatsapp.conversation = conversation;
+ MessageStatus.Whatsapp.Conversation.Origin origin = new MessageStatus.Whatsapp.Conversation.Origin();
+ conversation.origin = origin;
+ ConversationType whatsappConversationType = ConversationType.REFERRAL_CONVERSION;
+ origin.type = whatsappConversationType;
+ String whatsappConversationId = "1234567890";
+ conversation.id = whatsappConversationId;
String json = "{\n" +
" \"message_uuid\": \""+messageUuid+"\",\n" +
@@ -65,7 +79,21 @@ public void testSerdesAllFields() {
" \"usage\": {\n" +
" \"currency\": \""+currency+"\",\n" +
" \"price\": \""+price+"\"\n" +
- " }\n" +
+ " },\n" +
+ " \"sms\": {\n" +
+ " \"count_total\": \""+countTotal+"\"\n" +
+ " },\n" +
+ " \"destination\": {\n" +
+ " \"network_code\": \""+networkCode+"\"\n" +
+ " },\n" +
+ " \"whatsapp\": {\n" +
+ " \"conversation\": {\n" +
+ " \"id\": \""+whatsappConversationId+"\",\n" +
+ " \"origin\": {\n" +
+ " \"type\": \""+whatsappConversationType+"\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
"}";
MessageStatus ms = MessageStatus.fromJson(json);
@@ -88,6 +116,10 @@ public void testSerdesAllFields() {
assertEquals(error.toString(), ms.getError().toString());
assertEquals(usage, ms.getUsage());
assertEquals(usage.toString(), ms.getUsage().toString());
+ assertEquals(networkCode, ms.getDestinationNetworkCode());
+ assertEquals(countTotal, ms.getSmsTotalCount());
+ assertEquals(whatsappConversationId, ms.getWhatsappConversationId());
+ assertEquals("referral_conversion", ms.getWhatsappConversationType().toString());
assertNull(ms.getAdditionalProperties());
}
@@ -143,10 +175,10 @@ public void testDeserializeUnknownProperties() {
" },\n" +
" \"client_ref\": \"string\",\n" +
" \"channel\": \"whatsapp\",\n" +
- " \"whatsapp\": {\n" +
- " \"conversation\": {\n" +
- " \"id\": \"1234567890\",\n" +
- " \"origin\": {\n" +
+ " \"wubwub\": {\n" +
+ " \"Prop0\": {\n" +
+ " \"id\": \"ab12cdhfgjk3\",\n" +
+ " \"N3s7ed\": {\n" +
" \"type\": \"user_initiated\"\n" +
" }\n" +
" }\n" +
@@ -156,10 +188,10 @@ public void testDeserializeUnknownProperties() {
Map unknown = ms.getAdditionalProperties();
assertNotNull(unknown);
assertEquals(1, unknown.size());
- Map whatsapp = (Map) unknown.get("whatsapp");
- Map conversation = (Map) whatsapp.get("conversation");
- assertEquals("1234567890", conversation.get("id"));
- Map origin = (Map) conversation.get("origin");
+ Map whatsapp = (Map) unknown.get("wubwub");
+ Map conversation = (Map) whatsapp.get("Prop0");
+ assertEquals("ab12cdhfgjk3", conversation.get("id"));
+ Map origin = (Map) conversation.get("N3s7ed");
assertEquals("user_initiated", origin.get("type"));
}
diff --git a/src/test/java/com/vonage/client/messages/mms/MmsVcardRequestTest.java b/src/test/java/com/vonage/client/messages/mms/MmsVcardRequestTest.java
index cf79a213e..f57f1d55b 100644
--- a/src/test/java/com/vonage/client/messages/mms/MmsVcardRequestTest.java
+++ b/src/test/java/com/vonage/client/messages/mms/MmsVcardRequestTest.java
@@ -22,6 +22,20 @@ public class MmsVcardRequestTest {
@Test
public void testSerializeValid() {
+ String from = "447900000001", to = "317900000002",
+ url = "https://foo.tld/path/to/resource.vcf", caption = "!Alt text";
+
+ MmsVcardRequest mms = MmsVcardRequest.builder().url(url).from(from).to(to).caption(caption).build();
+ String json = mms.toJson();
+ assertTrue(json.contains("\"from\":\""+from+"\""));
+ assertTrue(json.contains("\"to\":\""+to+"\""));
+ assertTrue(json.contains("\"message_type\":\"vcard\""));
+ assertTrue(json.contains("\"channel\":\"mms\""));
+ assertTrue(json.contains("\"vcard\":{\"url\":\""+url+"\",\"caption\":\""+caption+"\"}"));
+ }
+
+ @Test
+ public void testSerializeNoCaption() {
String from = "447900000001", to = "317900000002",
url = "https://foo.tld/path/to/resource.vcf";
@@ -37,7 +51,7 @@ public void testSerializeValid() {
@Test
public void testConstructNoUrl() {
assertThrows(NullPointerException.class, () ->
- MmsVcardRequest.builder().from("447900000001").to("317900000002").build()
+ MmsVcardRequest.builder().caption("Cap").from("447900000001").to("317900000002").build()
);
}
diff --git a/src/test/java/com/vonage/client/messages/sms/SmsTextRequestTest.java b/src/test/java/com/vonage/client/messages/sms/SmsTextRequestTest.java
index 2cc479cbe..aaca698db 100644
--- a/src/test/java/com/vonage/client/messages/sms/SmsTextRequestTest.java
+++ b/src/test/java/com/vonage/client/messages/sms/SmsTextRequestTest.java
@@ -19,32 +19,103 @@
import static org.junit.jupiter.api.Assertions.*;
public class SmsTextRequestTest {
+ final String
+ from = "447900000001", to = "317900000002", msg = "Hello, World!",
+ contentId = "1107457532145798767", entityId = "1101456324675322134";
@Test
- public void testSerializeValid() {
- String from = "447900000001", to = "317900000002", msg = "Hello, World!";
+ public void testSerializeAllParameters() {
+ int ttl = 900000;
+ SmsTextRequest sms = SmsTextRequest.builder()
+ .from(from).to(to).text(msg).ttl(ttl).contentId(contentId)
+ .encodingType(EncodingType.UNICODE).entityId(entityId).build();
+
+ String json = sms.toJson();
+ assertTrue(json.contains("\"text\":\""+msg+"\""));
+ assertTrue(json.contains("\"from\":\""+from+"\""));
+ assertTrue(json.contains("\"to\":\""+to+"\""));
+ assertTrue(json.contains("\"message_type\":\"text\""));
+ assertTrue(json.contains("\"channel\":\"sms\""));
+ assertTrue(json.contains("\"ttl\":"+ttl));
+ assertTrue(json.contains("\"sms\":{" +
+ "\"encoding_type\":\"unicode\"," +
+ "\"content_id\":\""+contentId+"\"," +
+ "\"entity_id\":\""+entityId+"\"}"
+ ));
+ assertEquals("SmsTextRequest "+json, sms.toString());
+ }
+
+ @Test
+ public void testRequiredParametersOnly() {
SmsTextRequest sms = SmsTextRequest.builder().from(from).to(to).text(msg).build();
+
String json = sms.toJson();
assertTrue(json.contains("\"text\":\""+msg+"\""));
assertTrue(json.contains("\"from\":\""+from+"\""));
assertTrue(json.contains("\"to\":\""+to+"\""));
assertTrue(json.contains("\"message_type\":\"text\""));
assertTrue(json.contains("\"channel\":\"sms\""));
+ assertEquals("SmsTextRequest "+json, sms.toString());
+
+ assertNull(sms.getMessageSettings());
+ assertNull(sms.getTtl());
+ }
+ @Test
+ public void testEncodingTypeSettingsOnly() {
+ EncodingType encodingType = EncodingType.AUTO;
+ SmsTextRequest sms = SmsTextRequest.builder()
+ .from(from).to(to).text(msg).encodingType(encodingType).build();
+
+ String json = sms.toJson();
+ assertTrue(json.contains("\"text\":\""+msg+"\""));
+ assertTrue(json.contains("\"from\":\""+from+"\""));
+ assertTrue(json.contains("\"to\":\""+to+"\""));
+ assertTrue(json.contains("\"message_type\":\"text\""));
+ assertTrue(json.contains("\"channel\":\"sms\""));
+ assertTrue(json.contains("\"sms\":{\"encoding_type\":\""+encodingType+"\"}"));
assertEquals("SmsTextRequest "+json, sms.toString());
+
+ assertNull(sms.getTtl());
+ OutboundSettings settings = sms.getMessageSettings();
+ assertNotNull(settings);
+ assertEquals(encodingType, EncodingType.fromString(settings.getEncodingType().toString()));
+ assertNull(settings.getEntityId());
+ assertNull(settings.getContentId());
+ }
+
+ @Test
+ public void testInvalidContentId() {
+ assertThrows(IllegalArgumentException.class, () ->
+ OutboundSettings.construct(EncodingType.TEXT, " ", entityId)
+ );
+ }
+
+ @Test
+ public void testInvalidEntityId() {
+ assertThrows(IllegalArgumentException.class, () ->
+ OutboundSettings.construct(EncodingType.TEXT, contentId, " ")
+ );
+ }
+
+ @Test
+ public void testTtlTooShort() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SmsTextRequest.builder().from(from).to(to).text(msg).ttl(0).build()
+ );
}
@Test
public void testNullText() {
- assertThrows(NullPointerException.class, () -> SmsTextRequest.builder()
- .from("447900000001").to("317900000002").build()
+ assertThrows(NullPointerException.class, () ->
+ SmsTextRequest.builder().from(from).to(to).build()
);
}
@Test
public void testEmptyText() {
- assertThrows(IllegalArgumentException.class, () -> SmsTextRequest.builder()
- .from("447900000001").to("317900000002").text("").build()
+ assertThrows(IllegalArgumentException.class, () ->
+ SmsTextRequest.builder().from(from).to(to).text("").build()
);
}
@@ -56,11 +127,7 @@ public void testLongText() {
}
assertEquals(999, text.length());
- SmsTextRequest sms = SmsTextRequest.builder()
- .text(text.toString())
- .from("447900000001")
- .to("317900000002")
- .build();
+ SmsTextRequest sms = SmsTextRequest.builder().text(text.toString()).from(from).to(to).build();
assertEquals(text.toString(), sms.getText());
text.append("xy");
diff --git a/src/test/java/com/vonage/client/messages/whatsapp/WhatsappFileRequestTest.java b/src/test/java/com/vonage/client/messages/whatsapp/WhatsappFileRequestTest.java
index efd584e9a..5c5d7a0e1 100644
--- a/src/test/java/com/vonage/client/messages/whatsapp/WhatsappFileRequestTest.java
+++ b/src/test/java/com/vonage/client/messages/whatsapp/WhatsappFileRequestTest.java
@@ -22,22 +22,25 @@ public class WhatsappFileRequestTest {
@Test
public void testSerializeValid() {
- String url = "file:///path/to/attachment.zip", caption = "Srs bzns";
+ String url = "file:///path/to/attachment.zip", caption = "Srs bzns", name = "Stuff";
String json = WhatsappFileRequest.builder()
.from("317900000002").to("447900000001")
- .url(url).caption(caption)
+ .url(url).caption(caption).name(name)
.build().toJson();
- assertTrue(json.contains("\"file\":{\"url\":\""+url+"\",\"caption\":\""+caption+"\"}"));
+ assertTrue(json.contains(
+ "\"file\":{\"url\":\""+url+ "\",\"caption\":\""+caption+"\",\"name\":\""+name+"\"}"
+ ));
assertTrue(json.contains("\"message_type\":\"file\""));
assertTrue(json.contains("\"channel\":\"whatsapp\""));
}
@Test
- public void testSerializeNoCaption() {
+ public void testSerializeNoCaptionOrName() {
String url = "file:///path/to/spec.pdf";
- String json = WhatsappFileRequest.builder()
- .url(url).from("447900000002").to("447900000001")
- .build().toJson();
+ WhatsappFileRequest req = WhatsappFileRequest.builder()
+ .url(url).from("447900000002").to("447900000001").build();
+ assertNull(req.getFile().getName());
+ String json = req.toJson();
assertTrue(json.contains("\"file\":{\"url\":\""+url+"\"}"));
assertTrue(json.contains("\"message_type\":\"file\""));
assertTrue(json.contains("\"channel\":\"whatsapp\""));