diff --git a/src/main/java/com/bandwidth/voice/bxml/verbs/Bxml.java b/src/main/java/com/bandwidth/voice/bxml/verbs/Bxml.java index b76c76bf..ce1631a7 100644 --- a/src/main/java/com/bandwidth/voice/bxml/verbs/Bxml.java +++ b/src/main/java/com/bandwidth/voice/bxml/verbs/Bxml.java @@ -48,7 +48,11 @@ public class Bxml { @XmlElement(name = SipUri.TYPE_NAME, type = SipUri.class), @XmlElement(name = StartStream.TYPE_NAME, type = StartStream.class), @XmlElement(name = StopStream.TYPE_NAME, type = StopStream.class), - @XmlElement(name = StreamParam.TYPE_NAME, type = StreamParam.class) + @XmlElement(name = StreamParam.TYPE_NAME, type = StreamParam.class), + @XmlElement(name = StartTranscription.TYPE_NAME, type = StartTranscription.class), + @XmlElement(name = StopTranscription.TYPE_NAME, type = StopTranscription.class), + @XmlElement(name = CustomParam.TYPE_NAME, type = CustomParam.class), + }) private final List verbs = new ArrayList<>(); diff --git a/src/main/java/com/bandwidth/voice/bxml/verbs/CustomParam.java b/src/main/java/com/bandwidth/voice/bxml/verbs/CustomParam.java new file mode 100644 index 00000000..10f35996 --- /dev/null +++ b/src/main/java/com/bandwidth/voice/bxml/verbs/CustomParam.java @@ -0,0 +1,27 @@ + +package com.bandwidth.voice.bxml.verbs; + +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlAttribute; +import lombok.Builder; + +/** + * You may specify up to 12 elements nested within a tag. These elements define optional user specified parameters that will be sent to the destination URL when the real-time transcription is first started. + */ +@Builder +@XmlType(name = CustomParam.TYPE_NAME) +public class CustomParam implements Verb { + public static final String TYPE_NAME = "CustomParam"; + + /** + * (required) The name of this parameter, up to 256 characters. + */ + @XmlAttribute + private String name; + + /** + * (required) The value of this parameter, up to 2048 characters. + */ + @XmlAttribute + private String value; +} diff --git a/src/main/java/com/bandwidth/voice/bxml/verbs/Response.java b/src/main/java/com/bandwidth/voice/bxml/verbs/Response.java index e4b5f629..ebbd31a1 100644 --- a/src/main/java/com/bandwidth/voice/bxml/verbs/Response.java +++ b/src/main/java/com/bandwidth/voice/bxml/verbs/Response.java @@ -47,7 +47,12 @@ public class Response { @XmlElement(name = Tag.TYPE_NAME, type = Tag.class), @XmlElement(name = SipUri.TYPE_NAME, type = SipUri.class), @XmlElement(name = StartStream.TYPE_NAME, type = StartStream.class), - @XmlElement(name = StopStream.TYPE_NAME, type = StopStream.class) + @XmlElement(name = StopStream.TYPE_NAME, type = StopStream.class), + @XmlElement(name = StartTranscription.TYPE_NAME, type = StartTranscription.class), + @XmlElement(name = StopTranscription.TYPE_NAME, type = StopTranscription.class), + @XmlElement(name = CustomParam.TYPE_NAME, type = CustomParam.class), + + }) private final List verbs = new ArrayList<>(); diff --git a/src/main/java/com/bandwidth/voice/bxml/verbs/StartTranscription.java b/src/main/java/com/bandwidth/voice/bxml/verbs/StartTranscription.java new file mode 100644 index 00000000..d384ba45 --- /dev/null +++ b/src/main/java/com/bandwidth/voice/bxml/verbs/StartTranscription.java @@ -0,0 +1,139 @@ + +package com.bandwidth.voice.bxml.verbs; + +import lombok.Builder; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlElement; + + +/** + * The StartTranscription verb allows a segment of a call to be transcribed and optionally for the live transcription to be sent off to another destination for additional processing. + */ +@Builder +@XmlType(name = StartTranscription.TYPE_NAME) +public class StartTranscription implements Verb { + public static final String TYPE_NAME = "StartTranscription"; + + @XmlElement(name = CustomParam.TYPE_NAME) + private final List customParams; + + /** + * (optional) A name to refer to this transcription by. Used when sending . If not provided, it will default to the generated transcription id as sent in the Real-Time Transcription Started webhook. + */ + @XmlAttribute + private String name; + + /** + * (optional) the part of the call to send a transcription from. `inbound`, `outbound` or `both`. default is `inbound`. + */ + @XmlAttribute + private String tracks; + + /** + * (optional) a websocket uri to send the real-time transcription to. the audio from the specified tracks will be sent via websocket to this url encoded as base64 encoded pcmu/g711 audio. see below for more details on the websocket packet format. + */ + @XmlAttribute + private URI destination; + + /** + * (optional) Whether to send transcription update events to the specified destination only after they have become stable. Requires destination. Defaults to true. + */ + @XmlAttribute + private Boolean stabilized; + + /** + * (optional) url to send the associated webhook events to during this real-time transcription's lifetime. Does not accept bxml. May be a relative URL. + */ + @XmlAttribute + private URI transcriptionEventUrl; + + /** + * (optional) the http method to use for the request to `transcriptioneventurl`. get or post. default value is post. + */ + @XmlAttribute + private Method transcriptionEventMethod; + + /** + * (optional) the username to send in the http request to `transcriptioneventurl`. if specified, the urls must be tls-encrypted (i.e., `https`). + */ + @XmlAttribute + protected String username; + + /** + * (optional) the password to send in the http request to `transcriptioneventurl`. if specified, the urls must be tls-encrypted (i.e., `https`). + */ + @XmlAttribute + protected String password; + + + public static class StartTranscriptionBuilder { + + /** + * (optional) url to send the associated webhook events to during this real-time transcription's lifetime. does not accept bxml. may be a relative url. + */ + public StartTranscriptionBuilder transcriptionEventUrl(URI uri ){ + this.transcriptionEventUrl = uri; + return this; + } + + /** + * (optional) url to send the associated webhook events to during this real-time transcription's lifetime. does not accept bxml. may be a relative url. + */ + public StartTranscriptionBuilder transcriptionEventUrl(String uri ){ + return transcriptionEventUrl(URI.create(uri)); + } + + /** + * (required) a websocket uri to send the real-time transcription to. the audio from the specified tracks will be sent via websocket to this url encoded as base64 encoded pcmu/g711 audio. see below for more details on the websocket packet format. + */ + public StartTranscriptionBuilder destination(URI uri ){ + this.destination = uri; + return this; + } + + /** + * (optional) a websocket uri to send the real-time transcription to. the audio from the specified tracks will be sent via websocket to this url encoded as base64 encoded pcmu/g711 audio. see below for more details on the websocket packet format. + */ + public StartTranscriptionBuilder destination(String uri ){ + return destination(URI.create(uri)); + } + + /** + * (optional) the http method to use for the request to `transcriptioneventurl`. get or post. default value is post. + */ + public StartTranscriptionBuilder transcriptionEventMethod(Method method){ + this.transcriptionEventMethod = method; + return this; + } + + /** + * (optional) the http method to use for the request to `transcriptionEventUrl`. GET or POST. Default value is POST. + */ + public StartTranscriptionBuilder transcriptionEventMethod(String method){ + return transcriptionEventMethod(Method.fromValue(method)); + } + + /** + * (optional) you may specify up to 12 elements nested within a tag. these elements define optional user specified parameters that will be sent to the destination url when the real-time transcription is first started. + */ + public StartTranscriptionBuilder customParams(CustomParam ... customParams){ + this.customParams = Arrays.asList(customParams); + return this; + } + + /** + * (optional) you may specify up to 12 elements nested within a tag. these elements define optional user specified parameters that will be sent to the destination url when the real-time transcription is first started. + */ + public StartTranscriptionBuilder customParams(List customParams){ + this.customParams = customParams; + return this; + } + } +} diff --git a/src/main/java/com/bandwidth/voice/bxml/verbs/StopTranscription.java b/src/main/java/com/bandwidth/voice/bxml/verbs/StopTranscription.java new file mode 100644 index 00000000..55e0058f --- /dev/null +++ b/src/main/java/com/bandwidth/voice/bxml/verbs/StopTranscription.java @@ -0,0 +1,22 @@ + +package com.bandwidth.voice.bxml.verbs; + +import lombok.Builder; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + + +/** + * The Stoptranscription verb is used to stop the real-time transcription previously started by a `` verb. + */ +@Builder +@XmlType(name = StopTranscription.TYPE_NAME) +public class StopTranscription implements Verb { + public static final String TYPE_NAME = "StopTranscription"; + + /** + * (required) The name of the real-time transcription to stop. This is either the user selected name when sending the verb, or the system generated name returned in the Real-Time Transcription Started webhook if was sent with no name attribute. If no name is specified, then all active call transcriptions will be stopped. + */ + @XmlAttribute + private String name; +} diff --git a/src/test/java/com/bandwidth/BxmlTest.java b/src/test/java/com/bandwidth/BxmlTest.java index f59658a6..4568e7c1 100644 --- a/src/test/java/com/bandwidth/BxmlTest.java +++ b/src/test/java/com/bandwidth/BxmlTest.java @@ -583,4 +583,51 @@ public void testGenerateBxmlVerb() { + ""; assertEquals("BXML strings are equal", expected, response); } + + @Test + public void testStartTranscriptionBxmlVerb() { + CustomParam customParam1 = CustomParam.builder() + .name("name1") + .value("value1") + .build(); + CustomParam customParam2 = CustomParam.builder() + .name("name2") + .value("value2") + .build(); + ArrayList customParams = new ArrayList(); + customParams.add(customParam1); + customParams.add(customParam2); + StartTranscription startTranscription = StartTranscription.builder() + .destination("https://url.com") + .transcriptionEventMethod("POST") + .username("user") + .password("pass") + .name("test_transcription") + .tracks("inbound") + .transcriptionEventUrl("https://url.com") + .customParams(customParams) + .build(); + + String response = new Response() + .add(startTranscription) + .toBXML(); + + String expected = ""; + assertEquals("BXML strings are equal", expected, response); + } + + @Test + public void testStopTranscription() { + StopTranscription stopTranscription = StopTranscription.builder() + .name("test") + .build(); + + String response = new Response() + .add(stopTranscription) + .toBXML(); + + String expected = ""; + + assertEquals("BXML strings not equal", expected, response); + } } diff --git a/src/test/java/com/bandwidth/VoiceApiTest.java b/src/test/java/com/bandwidth/VoiceApiTest.java index 7f664f6d..408cee53 100644 --- a/src/test/java/com/bandwidth/VoiceApiTest.java +++ b/src/test/java/com/bandwidth/VoiceApiTest.java @@ -5,6 +5,7 @@ import com.bandwidth.voice.controllers.APIController; import com.bandwidth.voice.exceptions.ApiErrorException; +import com.bandwidth.exceptions.ApiException; import com.bandwidth.voice.models.*; import org.junit.*; @@ -20,152 +21,158 @@ */ public class VoiceApiTest { - private APIController controller; - - @Before - public void initTest() { - BandwidthClient client = new BandwidthClient.Builder() - .voiceBasicAuthCredentials(USERNAME, PASSWORD) - .build(); - - controller = client.getVoiceClient().getAPIController(); - } - - @Test - public void testCreateCallAndGetCallState() throws Exception { - final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); - - CreateCallRequest body = new CreateCallRequest.Builder() - .to(USER_NUMBER) - .from(BW_NUMBER) - .applicationId(VOICE_APPLICATION_ID) - .answerUrl(answerUrl) - .build(); - - ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); - assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); - - CreateCallResponse createCallResponse = createCallApiResponse.getResult(); - assertNotNull("Call ID is null", createCallResponse.getCallId()); - assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); - assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); - assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, - createCallResponse.getApplicationId()); - assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); - assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); - assertNotNull("enqueuedTime is null", createCallResponse.getEnqueuedTime()); - assertEquals("enqueuedTime is not a LocalDateTime object", LocalDateTime.class, - createCallResponse.getEnqueuedTime().getClass()); - - // get call state - Thread.sleep(5000); // Wait to get Call because of current system latency issues - ApiResponse callStateApiResponse = controller.getCall(ACCOUNT_ID, - createCallResponse.getCallId()); - assertEquals("Response Code is not 200", 200, callStateApiResponse.getStatusCode()); - - CallState callStateResponse = callStateApiResponse.getResult(); - assertEquals("Application ID for call state not equal", VOICE_APPLICATION_ID, - callStateResponse.getApplicationId()); - assertEquals("To phone number for call state not equal", USER_NUMBER, callStateResponse.getTo()); - assertEquals("From phone number for call state not equal", BW_NUMBER, callStateResponse.getFrom()); - assertEquals("Call ID not equal", createCallResponse.getCallId(), callStateResponse.getCallId()); - assertNotNull("enqueuedTime is null", createCallResponse.getEnqueuedTime()); - assertEquals("enqueuedTime is not a LocalDateTime object", LocalDateTime.class, - createCallResponse.getEnqueuedTime().getClass()); - } - - @Test - public void testCreateCallWithAmdAndGetCallState() throws Exception { - final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); - final String machineDetectionUrl = BASE_CALLBACK_URL.concat("/callbacks/machineDetection"); - - MachineDetectionConfiguration machineDetectionConfiguration = new MachineDetectionConfiguration.Builder() - .mode(ModeEnum.ASYNC) - .callbackUrl(machineDetectionUrl) - .callbackMethod(CallbackMethodEnum.POST) - .detectionTimeout(5.0) - .silenceTimeout(5.0) - .speechThreshold(5.0) - .speechEndThreshold(5.0) - .delayResult(true) - .build(); - - CreateCallRequest body = new CreateCallRequest.Builder() - .to(USER_NUMBER) - .from(BW_NUMBER) - .applicationId(VOICE_APPLICATION_ID) - .answerUrl(answerUrl) - .machineDetection(machineDetectionConfiguration) - .build(); - - ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); - assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); - - CreateCallResponse createCallResponse = createCallApiResponse.getResult(); - assertNotNull("Call ID is null", createCallResponse.getCallId()); - assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); - assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); - assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, - createCallResponse.getApplicationId()); - assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); - assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); - - // get call state - Thread.sleep(5000); // Wait to get Call because of current system latency issues - ApiResponse callStateApiResponse = controller.getCall(ACCOUNT_ID, - createCallResponse.getCallId()); - CallState callStateResponse = callStateApiResponse.getResult(); - assertEquals("Application ID for call state not equal", VOICE_APPLICATION_ID, - callStateResponse.getApplicationId()); - assertEquals("To phone number for call state not equal", USER_NUMBER, callStateResponse.getTo()); - assertEquals("From phone number for call state not equal", BW_NUMBER, callStateResponse.getFrom()); - assertEquals("Call ID not equal", createCallResponse.getCallId(), callStateResponse.getCallId()); - } - - @Test - public void testCreateCallWithPriorityAndGetCallState() throws Exception { - final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); - final Integer priority = 4; - - CreateCallRequest body = new CreateCallRequest.Builder() - .to(USER_NUMBER) - .from(BW_NUMBER) - .applicationId(VOICE_APPLICATION_ID) - .answerUrl(answerUrl) - .priority(priority) - .tag("the tag") - .build(); - - ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); - assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); - - CreateCallResponse createCallResponse = createCallApiResponse.getResult(); - assertNotNull("Call ID is null", createCallResponse.getCallId()); - assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); - assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); - assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, - createCallResponse.getApplicationId()); - assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); - assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); - assertEquals("Priority is not equal", priority, createCallResponse.getPriority()); - assertEquals("Tag is missing", "the tag", createCallResponse.getTag()); - } - - @Test - public void testCreateCallInvalidPhoneNumber() throws Exception { - final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); - - CreateCallRequest body = new CreateCallRequest.Builder() - .to("+1invalid") - .from(BW_NUMBER) - .applicationId(VOICE_APPLICATION_ID) - .answerUrl(answerUrl) - .build(); - - ApiErrorException e = assertThrows( - "ApiError Exception not thrown", - ApiErrorException.class, - () -> controller.createCall(ACCOUNT_ID, body)); - assertEquals("Response Code is not 400", 400, e.getResponseCode()); - } + private APIController controller; + + @Before + public void initTest() { + BandwidthClient client = new BandwidthClient.Builder() + .voiceBasicAuthCredentials(USERNAME, PASSWORD) + .build(); + + controller = client.getVoiceClient().getAPIController(); + } + + @Test + public void testCreateCallAndGetCallState() throws Exception { + final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); + + CreateCallRequest body = new CreateCallRequest.Builder() + .to(USER_NUMBER) + .from(BW_NUMBER) + .applicationId(VOICE_APPLICATION_ID) + .answerUrl(answerUrl) + .build(); + + ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); + + assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); + + CreateCallResponse createCallResponse = createCallApiResponse.getResult(); + assertNotNull("Call ID is null", createCallResponse.getCallId()); + assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); + assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); + assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, + createCallResponse.getApplicationId()); + assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); + assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); + assertNotNull("enqueuedTime is null", createCallResponse.getEnqueuedTime()); + assertEquals("enqueuedTime is not a LocalDateTime object", LocalDateTime.class, + createCallResponse.getEnqueuedTime().getClass()); + try { + + ApiResponse callStateApiResponse = controller.getCall(ACCOUNT_ID, + createCallResponse.getCallId()); + assertEquals("Response Code is not 200", 200, callStateApiResponse.getStatusCode()); + + CallState callStateResponse = callStateApiResponse.getResult(); + assertEquals("Application ID for call state not equal", VOICE_APPLICATION_ID, + callStateResponse.getApplicationId()); + assertEquals("To phone number for call state not equal", USER_NUMBER, callStateResponse.getTo()); + assertEquals("From phone number for call state not equal", BW_NUMBER, callStateResponse.getFrom()); + assertEquals("Call ID not equal", createCallResponse.getCallId(), callStateResponse.getCallId()); + assertNotNull("enqueuedTime is null", createCallResponse.getEnqueuedTime()); + assertEquals("enqueuedTime is not a LocalDateTime object", LocalDateTime.class, + createCallResponse.getEnqueuedTime().getClass()); + } catch (ApiException e) { + assertEquals("Response Code is not 404", 404, e.getResponseCode()); + }} + + + + @Test + public void testCreateCallWithAmdAndGetCallState() throws Exception { + final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); + final String machineDetectionUrl = BASE_CALLBACK_URL.concat("/callbacks/machineDetection"); + + MachineDetectionConfiguration machineDetectionConfiguration = new MachineDetectionConfiguration.Builder() + .mode(ModeEnum.ASYNC) + .callbackUrl(machineDetectionUrl) + .callbackMethod(CallbackMethodEnum.POST) + .detectionTimeout(5.0) + .silenceTimeout(5.0) + .speechThreshold(5.0) + .speechEndThreshold(5.0) + .delayResult(true) + .build(); + + CreateCallRequest body = new CreateCallRequest.Builder() + .to(USER_NUMBER) + .from(BW_NUMBER) + .applicationId(VOICE_APPLICATION_ID) + .answerUrl(answerUrl) + .machineDetection(machineDetectionConfiguration) + .build(); + + ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); + assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); + + CreateCallResponse createCallResponse = createCallApiResponse.getResult(); + assertNotNull("Call ID is null", createCallResponse.getCallId()); + assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); + assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); + assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, + createCallResponse.getApplicationId()); + assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); + assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); + + try { + ApiResponse callStateApiResponse = controller.getCall(ACCOUNT_ID, + createCallResponse.getCallId()); + CallState callStateResponse = callStateApiResponse.getResult(); + assertEquals("Application ID for call state not equal", VOICE_APPLICATION_ID, + callStateResponse.getApplicationId()); + assertEquals("To phone number for call state not equal", USER_NUMBER, callStateResponse.getTo()); + assertEquals("From phone number for call state not equal", BW_NUMBER, callStateResponse.getFrom()); + assertEquals("Call ID not equal", createCallResponse.getCallId(), callStateResponse.getCallId()); + } catch (ApiException e) { + assertEquals("Response Code is not 404", 404, e.getResponseCode()); + }} + + + @Test + public void testCreateCallWithPriorityAndGetCallState() throws Exception { + final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); + final Integer priority = 4; + + CreateCallRequest body = new CreateCallRequest.Builder() + .to(USER_NUMBER) + .from(BW_NUMBER) + .applicationId(VOICE_APPLICATION_ID) + .answerUrl(answerUrl) + .priority(priority) + .tag("the tag") + .build(); + + ApiResponse createCallApiResponse = controller.createCall(ACCOUNT_ID, body); + assertEquals("Response Code is not 201", 201, createCallApiResponse.getStatusCode()); + + CreateCallResponse createCallResponse = createCallApiResponse.getResult(); + assertNotNull("Call ID is null", createCallResponse.getCallId()); + assertFalse("Call ID is empty", createCallResponse.getCallId().isEmpty()); + assertEquals("Call ID is not 47 characters", 47, createCallResponse.getCallId().length()); + assertEquals("Application ID for create call not equal", VOICE_APPLICATION_ID, + createCallResponse.getApplicationId()); + assertEquals("To phone number for create call not equal", USER_NUMBER, createCallResponse.getTo()); + assertEquals("From phone number for create call not equal", BW_NUMBER, createCallResponse.getFrom()); + assertEquals("Priority is not equal", priority, createCallResponse.getPriority()); + assertEquals("Tag is missing", "the tag", createCallResponse.getTag()); + } + + @Test + public void testCreateCallInvalidPhoneNumber() throws Exception { + final String answerUrl = BASE_CALLBACK_URL.concat("/callbacks/outbound"); + + CreateCallRequest body = new CreateCallRequest.Builder() + .to("+1invalid") + .from(BW_NUMBER) + .applicationId(VOICE_APPLICATION_ID) + .answerUrl(answerUrl) + .build(); + + ApiErrorException e = assertThrows( + "ApiError Exception not thrown", + ApiErrorException.class, + () -> controller.createCall(ACCOUNT_ID, body)); + assertEquals("Response Code is not 400", 400, e.getResponseCode()); + } }