Skip to content

Commit

Permalink
fix: Misc NCCO input validation
Browse files Browse the repository at this point in the history
  • Loading branch information
SMadani committed Jul 25, 2024
1 parent aeb9088 commit 3d75a36
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 103 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

# [8.9.4] - 2024-07-25
- Fixed UUID validation in `ConversationAction.Builder#canHear` and `canSpeak`
- Changed signature of `canHear` and `canSpeak` methods to return Strings instead of UUIDs
- Validation for `endpoint`, `limit` and `timeOut` in `ConnectAction`
- Added Builder to `DtmfSettings` with validation

# [8.9.3] - 2024-07-23
- Made `com.vonage.client.conversations.GenericEvent` public

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.vonage</groupId>
<artifactId>server-sdk</artifactId>
<version>8.9.3</version>
<version>8.9.4</version>

<name>Vonage Java Server SDK</name>
<description>Java client for Vonage APIs</description>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/vonage/client/HttpWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
public class HttpWrapper {
private static final String
CLIENT_NAME = "vonage-java-sdk",
CLIENT_VERSION = "8.9.3",
CLIENT_VERSION = "8.9.4",
JAVA_VERSION = System.getProperty("java.version"),
USER_AGENT = String.format("%s/%s java/%s", CLIENT_NAME, CLIENT_VERSION, JAVA_VERSION);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public static Builder builder() {
return new Builder();
}

/**
* Builder for specifying the Advanced Machine Detection properties.
*/
public static class Builder {
private MachineDetection behavior;
private Mode mode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static class Builder {
this.user = user;
}

@Deprecated
public Builder user(String user) {
this.user = user;
return this;
Expand Down
26 changes: 18 additions & 8 deletions src/main/java/com/vonage/client/voice/ncco/ConnectAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,25 @@ public class ConnectAction extends JsonableBaseObject implements Action {
ConnectAction() {}

private ConnectAction(Builder builder) {
endpoint = builder.endpoint;
if ((endpoint = builder.endpoint) == null || endpoint.isEmpty()) {
throw new IllegalStateException("An endpoint must be specified.");
}
if ((limit = builder.limit) != null && (limit < 1 || limit > 7200)) {
throw new IllegalArgumentException("'limit' must be positive and less than 7200 seconds.");
}
if ((timeOut = builder.timeOut) != null && (timeOut < 3 || timeOut > 7200)) {
throw new IllegalArgumentException("'timeOut' must be between 3 and 7200 seconds.");
}
from = builder.from;
if ((randomFromNumber = builder.randomFromNumber) != null && from != null) {
throw new IllegalStateException("'randomFromNumber' and 'from' cannot be used together.");
}
eventType = builder.eventType;
timeOut = builder.timeOut;
limit = builder.limit;
machineDetection = builder.machineDetection;
advancedMachineDetection = builder.advancedMachineDetection;
eventUrl = builder.eventUrl;
eventMethod = builder.eventMethod;
ringbackTone = builder.ringbackTone;
if ((randomFromNumber = builder.randomFromNumber) != null && from != null) {
throw new IllegalStateException("'randomFromNumber' and 'from' cannot be used together.");
}
}

@JsonProperty("action")
Expand Down Expand Up @@ -163,7 +169,9 @@ public static class Builder {
* @param endpoint The endpoints to connect to.
*
* @return This builder.
* @deprecated This will be removed in the next major release.
*/
@Deprecated
public Builder endpoint(Collection<Endpoint> endpoint) {
this.endpoint = endpoint;
return this;
Expand Down Expand Up @@ -213,8 +221,10 @@ public Builder eventType(EventType eventType) {
}

/**
* @param timeOut If the call is unanswered, set the number in seconds before Vonage stops ringing endpoint.
* The default value is 60.
* If the call is unanswered, set the number in seconds before Vonage stops ringing endpoint.
* The default value is 60, minimum is 3 and maximum is 7200 (2 hours).
*
* @param timeOut The call timeout in seconds.
*
* @return This builder.
*/
Expand Down
112 changes: 61 additions & 51 deletions src/main/java/com/vonage/client/voice/ncco/ConversationAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.vonage.client.JsonableBaseObject;
import java.util.*;
import java.util.stream.Collectors;

/**
* An NCCO conversation action which enables the ability to host conference calls.
Expand All @@ -28,8 +29,7 @@ public class ConversationAction extends JsonableBaseObject implements Action {
private String name;
private Boolean startOnEnter, endOnExit, record, mute;
private EventMethod eventMethod;
private Collection<String> musicOnHoldUrl, eventUrl;
private Collection<UUID> canSpeak, canHear;
private Collection<String> musicOnHoldUrl, eventUrl, canSpeak, canHear;
private TranscriptionSettings transcription;

ConversationAction() {}
Expand Down Expand Up @@ -96,12 +96,12 @@ public EventMethod getEventMethod() {
}

@JsonProperty("canSpeak")
public Collection<UUID> getCanSpeak() {
public Collection<String> getCanSpeak() {
return canSpeak;
}

@JsonProperty("canHear")
public Collection<UUID> getCanHear() {
public Collection<String> getCanHear() {
return canHear;
}

Expand All @@ -121,20 +121,24 @@ public static Builder builder(String name) {
return new Builder(name);
}

/**
* Builder for specifying the properties of a Conversation NCCO.
*/
public static class Builder {
private String name;
private EventMethod eventMethod;
private Boolean startOnEnter, endOnExit, record, mute;
private Collection<String> musicOnHoldUrl, eventUrl;
private Collection<UUID> canSpeak, canHear;
private Collection<String> musicOnHoldUrl, eventUrl, canSpeak, canHear;
private TranscriptionSettings transcription;

Builder(String name) {
this.name = name;
}

/**
* @param name The name of the Conversation room.
* Sets the name of the Conversation room.
*
* @param name The conversation name.
*
* @return This builder.
*/
Expand All @@ -150,17 +154,21 @@ public Builder name(String name) {
* conversation, set startOnEnter to false for all users other than the moderator.
*
* @return This builder.
* @deprecated This will be removed in the next major release.
*/
@Deprecated
public Builder musicOnHoldUrl(Collection<String> musicOnHoldUrl) {
this.musicOnHoldUrl = musicOnHoldUrl;
return this;
}

/**
* @param musicOnHoldUrl A URL to the mp3 file to stream to participants until the conversation starts.
* By default, the conversation starts when the first person calls the virtual number
* associated with your Voice app. To stream this mp3 before the moderator joins the
* conversation, set startOnEnter to false for all users other than the moderator.
* A URL to the mp3 file to stream to participants until the conversation starts.
* By default, the conversation starts when the first person calls the virtual number
* associated with your Voice app. To stream this mp3 before the moderator joins the
* conversation, set startOnEnter to false for all users other than the moderator.
*
* @param musicOnHoldUrl Absolute URL to the hold music in MP3 format, as a string.
*
* @return This builder.
*/
Expand All @@ -169,8 +177,10 @@ public Builder musicOnHoldUrl(String... musicOnHoldUrl) {
}

/**
* @param startOnEnter The default value of true ensures that the conversation starts when this caller joins
* conversation name. Set to false for attendees in a moderated conversation.
* The default value of {@code true} ensures that the conversation starts when this caller joins
* the conversation. Set to false for attendees in a moderated conversation.
*
* @param startOnEnter Whether to start the conversation when joining.
*
* @return This builder.
*/
Expand All @@ -180,10 +190,11 @@ public Builder startOnEnter(Boolean startOnEnter) {
}

/**
* @param endOnExit For moderated conversations, set to true in the moderator NCCO so the conversation is
* ended when the moderator hangs up. The default value of false means the conversation
* is not terminated when a caller hangs up; the conversation ends when the last caller
* hangs up.
* For moderated conversations, set to {@code true} in the moderator NCCO so the conversation is ended
* when the moderator hangs up. The default value of false means the conversation is not terminated
* when a caller hangs up; the conversation ends when the last caller hangs up.
*
* @param endOnExit Whether to end the conversation when the moderator hangs up.
*
* @return This builder.
*/
Expand All @@ -193,13 +204,17 @@ public Builder endOnExit(Boolean endOnExit) {
}

/**
* @param record Set to true to record this conversation. For standard conversations, recordings start when one
* or more attendees connects to the conversation. For moderated conversations, recordings start
* when the moderator joins. That is, when an NCCO is executed for the named conversation where
* startOnEnter is set to true. When the recording is terminated, the URL you download the
* recording from is sent to the event URL.
* <p>
* By default, audio is recorded in MP3 format. See the <a href="https://developer.nexmo.com/voice/voice-api/guides/recordingfile-formats">recording guide</a> for more details
* Set to {@code true} to record this conversation. For standard conversations, recordings start
* when one or more attendees connects to the conversation. For moderated conversations, recordings
* start when the moderator joins. That is, when an NCCO is executed for the named conversation where
* startOnEnter is set to true. When the recording is terminated, the URL you download the recording
* from is sent to the event URL.
* <p>
* By default, audio is recorded in MP3 format. See the
* <a href="https://developer.nexmo.com/voice/voice-api/guides/recordingfile-formats">recording guide</a>
* for more details.
*
* @param record Whether to enable recording.
*
* @return This builder.
*/
Expand All @@ -213,15 +228,19 @@ public Builder record(Boolean record) {
* <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flowcall-states">Call States</a>.
*
* @return This builder.
* @deprecated This will be removed in the next major release.
*/
@Deprecated
public Builder eventUrl(Collection<String> eventUrl) {
this.eventUrl = eventUrl;
return this;
}

/**
* @param eventUrl Set the URL to the webhook endpoint Vonage calls asynchronously on each of the
* <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flowcall-states">Call States</a>.
* Set the URL to the webhook endpoint Vonage calls asynchronously on each of the
* <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flowcall-states">Call States</a>.
*
* @param eventUrl The event URL as a string.
*
* @return This builder.
*/
Expand All @@ -230,7 +249,9 @@ public Builder eventUrl(String... eventUrl) {
}

/**
* @param eventMethod Set the HTTP method used to make the request to eventUrl. The default value is POST.
* Set the HTTP method used to make the request to eventUrl. The default value is POST.
*
* @param eventMethod The event method as an enum.
*
* @return This builder.
*/
Expand Down Expand Up @@ -264,11 +285,11 @@ public Builder mute(boolean mute) {
* @since 8.2.0
* @see #canSpeak(Collection)
*/
public Builder addCanSpeak(String uuid) {
public Builder addCanSpeak(String... uuid) {
if (canSpeak == null) {
canSpeak = new LinkedHashSet<>();
}
canSpeak.add(UUID.fromString(uuid));
canSpeak.addAll(Arrays.asList(uuid));
return this;
}

Expand All @@ -280,13 +301,12 @@ public Builder addCanSpeak(String uuid) {
*
* @return This builder.
* @since 8.2.0
* @see #canHear(Collection)
*/
public Builder addCanHear(String uuid) {
public Builder addCanHear(String... uuid) {
if (canHear == null) {
canHear = new LinkedHashSet<>();
canHear = new LinkedHashSet<>(uuid.length);
}
canHear.add(UUID.fromString(uuid));
canHear.addAll(Arrays.asList(uuid));
return this;
}

Expand All @@ -299,15 +319,10 @@ public Builder addCanHear(String uuid) {
*
* @return This builder.
* @since 8.2.0
* @see #addCanSpeak(String)
* @see #addCanSpeak(String...)
*/
public Builder canSpeak(Collection<UUID> canSpeak) {
if (canSpeak == null) {
this.canSpeak = null;
}
else {
this.canSpeak = new LinkedHashSet<>(canSpeak);
}
public Builder canSpeak(Collection<String> canSpeak) {
this.canSpeak = canSpeak;
return this;
}

Expand All @@ -320,15 +335,10 @@ public Builder canSpeak(Collection<UUID> canSpeak) {
*
* @return This builder.
* @since 8.2.0
* @see #addCanHear(String)
* @see #addCanHear(String...)
*/
public Builder canHear(Collection<UUID> canHear) {
if (canHear == null) {
this.canHear = null;
}
else {
this.canHear = new LinkedHashSet<>(canHear);
}
public Builder canHear(Collection<String> canHear) {
this.canHear = canHear;
return this;
}

Expand All @@ -353,10 +363,10 @@ public Builder transcription(TranscriptionSettings transcription) {
*/
public ConversationAction build() {
if (canSpeak != null) {
canSpeak = new ArrayList<>(canSpeak);
canSpeak = canSpeak.stream().distinct().collect(Collectors.toList());
}
if (canHear != null) {
canHear = new ArrayList<>(canHear);
canHear = canHear.stream().distinct().collect(Collectors.toList());
}
return new ConversationAction(this);
}
Expand Down
Loading

0 comments on commit 3d75a36

Please sign in to comment.