Skip to content

Commit

Permalink
Merge pull request #529 from Vonage/messages-updates
Browse files Browse the repository at this point in the history
  • Loading branch information
SMadani authored May 16, 2024
2 parents d1a9cd6 + a15b2a1 commit fe06b81
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 41 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

# [8.7.0] - 2024-05-??
# [8.7.0] - 2024-05-16
- Added missing supported languages to `TextToSpeechLanguage` enum
- Added `ttl` field to outbound MMS messages
- Added message reply context to Whatsapp outbound requests
- Added network code to `InboundMessage`

# [8.6.0] - 2024-04-18
- Added Experience Composer to Video API
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/vonage/client/messages/InboundMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class InboundMessage extends JsonableBaseObject {

protected static class UrlWrapper extends JsonableBaseObject {
@JsonProperty("url") protected URI url;
@JsonProperty("name") protected String name;
}

protected static class UrlWrapperWithCaption extends UrlWrapper {
Expand All @@ -48,6 +49,10 @@ protected static class Whatsapp extends JsonableBaseObject {
@JsonProperty("referral") protected Referral referral;
}

protected static class Origin extends JsonableBaseObject {
@JsonProperty("network_code") protected String networkCode;
}

protected InboundMessage() {}

@JsonAnySetter protected Map<String, Object> unknownProperties;
Expand Down Expand Up @@ -79,6 +84,7 @@ protected InboundMessage() {}
@JsonProperty("order") protected Order whatsappOrder;
@JsonProperty("usage") protected MessageStatus.Usage usage;
@JsonProperty("sms") protected SmsInboundMetadata smsMetadata;
@JsonProperty("origin") protected Origin origin;

/**
* This is a catch-all method which encapsulates all fields in the response JSON
Expand Down Expand Up @@ -330,6 +336,19 @@ public SmsInboundMetadata getSmsMetadata() {
return smsMetadata;
}

/**
* If the {@linkplain #getChannel()} is {@linkplain Channel#SMS} or {@linkplain Channel#MMS},
* return the network code from which the message originated (if available).
*
* @return The origin network code if applicable, or {@code null} if unknown.
*
* @since 8.7.0
*/
@JsonIgnore
public String getNetworkCode() {
return origin != null ? origin.networkCode : null;
}

/**
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ public final class MmsAudioRequest extends MmsRequest {

MmsAudioRequest(Builder builder) {
super(builder, MessageType.AUDIO);
payload = new MessagePayload(builder.url, builder.caption);
payload.validateCaptionLength(2000);
}

@JsonProperty("audio")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ public final class MmsImageRequest extends MmsRequest {

MmsImageRequest(Builder builder) {
super(builder, MessageType.IMAGE);
payload = new MessagePayload(builder.url, builder.caption);
payload.validateUrlExtension("jpg", "jpeg", "png", "gif");
payload.validateCaptionLength(2000);
}

@JsonProperty("image")
Expand Down
46 changes: 45 additions & 1 deletion src/main/java/com/vonage/client/messages/mms/MmsRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,74 @@
*/
package com.vonage.client.messages.mms;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.vonage.client.messages.Channel;
import com.vonage.client.messages.MessageRequest;
import com.vonage.client.messages.MessageType;
import com.vonage.client.messages.internal.MessagePayload;

public abstract class MmsRequest extends MessageRequest {
MessagePayload payload;
final Integer ttl;

protected MmsRequest(Builder<?, ?> builder, MessageType messageType) {
super(builder, Channel.MMS, messageType);
payload = new MessagePayload(builder.url, builder.caption);
payload.validateCaptionLength(2000);
int min = 300, max = 259200;
if ((this.ttl = builder.ttl) != null && (ttl < min || ttl > max)) {
throw new IllegalArgumentException("TTL must be between "+min+" and "+max+" seconds.");
}
}

@JsonProperty("ttl")
public Integer getTtl() {
return ttl;
}

@SuppressWarnings("unchecked")
protected abstract static class Builder<M extends MmsRequest, B extends Builder<? extends M, ? extends B>> extends MessageRequest.Builder<M, B> {
String url, caption;
Integer ttl;

protected B url(String url) {
/**
* (REQUIRED)
* Sets the media URL.
*
* @param url The URL as a string.
* @return This builder.
*/
public B url(String url) {
this.url = url;
return (B) this;
}

/**
* (OPTIONAL)
* Additional text to accompany the media. Must be between 1 and 2000 characters.
*
* @param caption The caption string.
* @return This builder.
*/
protected B caption(String caption) {
this.caption = caption;
return (B) this;
}

/**
* (OPTIONAL)
* Time-To-Live (how long a message should exist before it is delivered successfully) in seconds.
* If a message is not delivered successfully within the TTL time, the message is considered expired
* and will be rejected if TTL is supported. Must be between 300 and 259200. Default is 600.
*
* @param ttl The message time-to-live in seconds.
*
* @return This builder.
* @since 8.7.0
*/
public B ttl(int ttl) {
this.ttl = ttl;
return (B) this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public final class MmsVcardRequest extends MmsRequest {

MmsVcardRequest(Builder builder) {
super(builder, MessageType.VCARD);
payload = new MessagePayload(builder.url, builder.caption);
payload.validateUrlExtension("vcf");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ public final class MmsVideoRequest extends MmsRequest {

MmsVideoRequest(Builder builder) {
super(builder, MessageType.VIDEO);
payload = new MessagePayload(builder.url, builder.caption);
payload.validateCaptionLength(2000);
}

@JsonProperty("video")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public final class Context extends JsonableBaseObject {

Context() {}

Context(UUID messageUuid) {
this.messageUuid = messageUuid;
}

/**
* The phone number of the original sender of the message being quoted.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
*/
package com.vonage.client.messages.whatsapp;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.vonage.client.common.E164;
import com.vonage.client.messages.Channel;
import com.vonage.client.messages.MessageRequest;
import com.vonage.client.messages.MessageType;
import com.vonage.client.common.E164;
import java.util.UUID;

public abstract class WhatsappRequest extends MessageRequest {
final Context context;

protected WhatsappRequest(Builder<?, ?> builder, MessageType messageType) {
super(builder, Channel.WHATSAPP, messageType);
context = builder.messageUuid != null ? new Context(builder.messageUuid) : null;
}

@Override
Expand All @@ -32,6 +36,29 @@ protected void validateSenderAndRecipient(String from, String to) throws Illegal
this.to = new E164(to).toString();
}

@JsonProperty("context")
public Context getContext() {
return context;
}

@SuppressWarnings("unchecked")
protected abstract static class Builder<M extends WhatsappRequest, B extends Builder<? extends M, ? extends B>> extends MessageRequest.Builder<M, B> {
UUID messageUuid;

/**
* An optional context used for quoting/replying to a specific message in a conversation. When used,
* the WhatsApp UI will display the new message along with a contextual bubble that displays the
* quoted/replied to message's content.<br>
* This field is the UUID of the message being replied to or quoted.
*
* @param messageUuid The context's message UUID as a string.
*
* @return This builder.
* @since 8.7.0
*/
public B contextMessageId(String messageUuid) {
this.messageUuid = UUID.fromString(messageUuid);
return (B) this;
}
}
}
11 changes: 11 additions & 0 deletions src/test/java/com/vonage/client/messages/InboundMessageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,15 @@ public void testWhatsappReferralOnly() {
assertEquals(sourceType, referral.getSourceType());
assertEquals(URI.create(sourceUrl), referral.getSourceUrl());
}

@Test
public void testOriginOnly() {
String networkCode = "12534", json = "{\n" +
" \"origin\": {\n" +
" \"network_code\": \""+networkCode+"\"\n" +
" }\n" +
"}";
var im = InboundMessage.fromJson(json);
assertEquals(networkCode, im.getNetworkCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ public void testConstructCaptionLength() {
}

StringBuilder sb = new StringBuilder(2001);
for (int i = 0; i < 1999; i++) {
sb.append('*');
}
sb.append("*".repeat(1999));
assertEquals(1999, sb.length());

assertEquals(sb.toString(), builder.caption(sb.toString()).build().getAudio().getCaption());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@
import static org.junit.jupiter.api.Assertions.*;

public class MmsVcardRequestTest {
String from = "447900000001", to = "317900000002",
url = "https://foo.tld/path/to/resource.vcf", caption = "!Alt text";

@Test
public void testSerializeValid() {
String from = "447900000001", to = "317900000002",
url = "https://foo.tld/path/to/resource.vcf", caption = "!Alt text";
int ttl = 1200;

MmsVcardRequest mms = MmsVcardRequest.builder().url(url).from(from).to(to).caption(caption).build();
MmsVcardRequest mms = MmsVcardRequest.builder()
.ttl(ttl).url(url).from(from).to(to).caption(caption).build();
String json = mms.toJson();
assertTrue(json.contains("\"ttl\":"+ttl));
assertTrue(json.contains("\"from\":\""+from+"\""));
assertTrue(json.contains("\"to\":\""+to+"\""));
assertTrue(json.contains("\"message_type\":\"vcard\""));
Expand All @@ -51,15 +54,25 @@ public void testSerializeNoCaption() {
@Test
public void testConstructNoUrl() {
assertThrows(NullPointerException.class, () ->
MmsVcardRequest.builder().caption("Cap").from("447900000001").to("317900000002").build()
MmsVcardRequest.builder().caption(caption).from(from).to(to).build()
);
}

@Test
public void testConstructInvalidExtension() {
assertThrows(IllegalArgumentException.class, () -> MmsVcardRequest.builder()
.to("317900000002").from("447900000001")
.url("http://foo.tld/path/to/file.csv").build()
.to(to).from(from).url("http://foo.tld/path/to/file.csv").build()
);
}

@Test
public void testTtlBounds() {
final int min = 300, max = 259200;
var request = MmsVcardRequest.builder().url(url).to(to).from(from);

assertEquals(min, request.ttl(min).build().getTtl());
assertEquals(max, request.ttl(max).build().getTtl());
assertThrows(IllegalArgumentException.class, () -> request.ttl(min-1).build());
assertThrows(IllegalArgumentException.class, () -> request.ttl(max+1).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,31 @@

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.UUID;

public class WhatsappFileRequestTest {
String from = "447900000001", to = "317900000002";

@Test
public void testSerializeValid() {
String messageUuid = UUID.randomUUID().toString();
String url = "file:///path/to/attachment.zip", caption = "Srs bzns", name = "Stuff";
String json = WhatsappFileRequest.builder()
.from("317900000002").to("447900000001")
.url(url).caption(caption).name(name)
.build().toJson();
.from(from).to(to).url(url).contextMessageId(messageUuid)
.caption(caption).name(name).build().toJson();

assertTrue(json.contains(
"\"file\":{\"url\":\""+url+ "\",\"caption\":\""+caption+"\",\"name\":\""+name+"\"}"
"\"file\":{\"url\":\""+url+"\",\"caption\":\"" + caption + "\",\"name\":\""+name+"\"}"
));
assertTrue(json.contains("\"context\":{\"message_uuid\":\""+messageUuid+"\"}"));
assertTrue(json.contains("\"message_type\":\"file\""));
assertTrue(json.contains("\"channel\":\"whatsapp\""));
}

@Test
public void testSerializeNoCaptionOrName() {
String url = "file:///path/to/spec.pdf";
WhatsappFileRequest req = WhatsappFileRequest.builder()
.url(url).from("447900000002").to("447900000001").build();
WhatsappFileRequest req = WhatsappFileRequest.builder().url(url).from(from).to(to).build();
assertNull(req.getFile().getName());
String json = req.toJson();
assertTrue(json.contains("\"file\":{\"url\":\""+url+"\"}"));
Expand All @@ -49,7 +52,7 @@ public void testSerializeNoCaptionOrName() {
@Test
public void testConstructNoUrl() {
assertThrows(NullPointerException.class, () -> WhatsappFileRequest.builder()
.caption("Description").from("447900000001").to("317900000002").build()
.caption("Description").from(from).to(to).build()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public void testSerializeValid() {
assertTrue(json.contains("\"image\":{\"url\":\""+url+"\",\"caption\":\""+caption+"\"}"));
assertTrue(json.contains("\"message_type\":\"image\""));
assertTrue(json.contains("\"channel\":\"whatsapp\""));
assertFalse(json.contains("\"context\""));
}

@Test
Expand Down
Loading

0 comments on commit fe06b81

Please sign in to comment.