Skip to content

Commit

Permalink
feat: Add new properties to Messages API (#504)
Browse files Browse the repository at this point in the history
* Add webhook settings to MessageRequest

* Add vCard caption

* Add name for WhatsApp file

* Added image caption to inbound

* Add Whatsapp ContextStatus

* Add inbound SMS status objects

* Add ttl to outbound SMS

* Add outbound SMS settings

* Add inbound Whatsapp Referral

* Add whatsapp obj to MessageStatus

* Update changelog
  • Loading branch information
SMadani authored Jan 4, 2024
1 parent 8cdb975 commit fc884ba
Show file tree
Hide file tree
Showing 25 changed files with 883 additions and 69 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 54 additions & 6 deletions src/main/java/com/vonage/client/messages/InboundMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand All @@ -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.
* <p>
* Refer to the
* <a href=https://developer.vonage.com/api/messages-olympus#webhooks>Messages API Webhook reference</a>
Expand All @@ -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<String, Object> unknownProperties;

@JsonProperty("whatsapp") private Whatsapp whatsapp;

@JsonProperty("timestamp") protected Instant timestamp;
@JsonProperty("channel") protected Channel channel;
@JsonProperty("message_type") protected MessageType messageType;
Expand All @@ -57,14 +66,15 @@ 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;
@JsonProperty("vcard") protected UrlWrapper vcard;
@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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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).
Expand All @@ -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.
Expand Down
65 changes: 65 additions & 0 deletions src/main/java/com/vonage/client/messages/MessageRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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;

/**
Expand All @@ -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);
}

Expand Down Expand Up @@ -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();
Expand All @@ -133,6 +148,8 @@ public String toString() {
@SuppressWarnings("unchecked")
public abstract static class Builder<M extends MessageRequest, B extends Builder<? extends M, ? extends B>> {
protected String from, to, clientRef;
protected URI webhookUrl;
protected MessagesVersion webhookVersion;

/**
* Protected constructor to prevent users from explicitly creating this object.
Expand Down Expand Up @@ -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.
*
Expand Down
98 changes: 96 additions & 2 deletions src/main/java/com/vonage/client/messages/MessageStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, Object> unknownProperties;

@JsonProperty("timestamp") protected Instant timestamp;
@JsonProperty("message_uuid") protected UUID messageUuid;
@JsonProperty("to") protected String to;
Expand All @@ -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()}.
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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()
);
}
}
Loading

0 comments on commit fc884ba

Please sign in to comment.