From 40291d1f85fbad260a693d15e7c5b009290bed5e Mon Sep 17 00:00:00 2001 From: Sina Madani Date: Tue, 2 Jul 2024 16:38:40 +0100 Subject: [PATCH] feat: NCCO connectTo* methods --- src/main/kotlin/com/vonage/client/kt/Voice.kt | 32 ++++++ .../kotlin/com/vonage/client/kt/VoiceTest.kt | 100 ++++++++++++++++-- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/vonage/client/kt/Voice.kt b/src/main/kotlin/com/vonage/client/kt/Voice.kt index 5c572c4..3dc9649 100644 --- a/src/main/kotlin/com/vonage/client/kt/Voice.kt +++ b/src/main/kotlin/com/vonage/client/kt/Voice.kt @@ -1,5 +1,6 @@ package com.vonage.client.kt +import com.vonage.client.users.channels.Websocket.ContentType import com.vonage.client.voice.* import com.vonage.client.voice.ncco.* import java.net.URI @@ -103,3 +104,34 @@ fun conversationAction(name: String, properties: ConversationAction.Builder.() - fun connectAction(endpoint: com.vonage.client.voice.ncco.Endpoint, properties: ConnectAction.Builder.() -> Unit = {}): ConnectAction = ConnectAction.builder(endpoint).apply(properties).build() + +fun connectToPstn(number: String, dtmfAnswer: String? = null, + onAnswerUrl: String? = null, onAnswerRingback: String? = null, + properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction = + connectAction(com.vonage.client.voice.ncco.PhoneEndpoint.builder(number) + .dtmfAnswer(dtmfAnswer).onAnswer(onAnswerUrl, onAnswerRingback).build(), properties + ) + +fun connectToVbc(extension: String, properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction = + connectAction(com.vonage.client.voice.ncco.VbcEndpoint.builder(extension).build(), properties) + +fun connectToApp(user: String, properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction = + connectAction(com.vonage.client.voice.ncco.AppEndpoint.builder(user).build(), properties) + +fun connectToWebsocket(uri: String, contentType: String, headers: Map? = null, + properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction = + connectAction(com.vonage.client.voice.ncco.WebSocketEndpoint.builder(uri, contentType) + .headers(headers).build(), properties + ) + +fun connectToSip(uri: String, customHeaders: Map? = null, userToUserHeader: String? = null, + properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction { + val builder = com.vonage.client.voice.ncco.SipEndpoint.builder(uri) + if (customHeaders != null) { + builder.headers(customHeaders) + } + if (userToUserHeader != null) { + builder.userToUserHeader(userToUserHeader) + } + return connectAction(builder.build(), properties) +} diff --git a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt index 6c1d79f..9dbb70a 100644 --- a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt @@ -29,8 +29,12 @@ class VoiceTest : AbstractTest() { private val vbcExt = "4321" private val eventUrl = "https://example.com/event" private val streamUrl = "https://example.com/waiting.mp3" + private val onAnswerUrl = "https://example.com/ncco.json" private val websocketUri = "wss://example.com/socket" + private val ringbackTone = "http://example.com/ringbackTone.wav" private val sipUri = "sip:rebekka@sip.example.com" + private val wsContentType = "audio/l16;rate=8000" + private val userToUserHeader = "56a390f3d2b7310023a" private val conversationName = "selective-audio Demo" private val customHeaders = mapOf( "customer_id" to "abc123", @@ -221,6 +225,11 @@ class VoiceTest : AbstractTest() { } } + private fun testSingleNccoConnect(params: Map, connectAction: ConnectAction) = + testSingleNcco(mapOf("endpoint" to listOf(mapOf( + "type" to connectAction.endpoint.first().type) + params)), connectAction + ) + @Test fun `terminate call`() { testModifyCall("hangup", callObj::hangup) @@ -254,10 +263,9 @@ class VoiceTest : AbstractTest() { @Test fun `transfer call with answer url`() { - val answerUrl = "https://example.com/ncco.json" - testModifyCall(nccoUrl = answerUrl, invocation = { - callObj.transfer(URI.create(answerUrl)) - callObj.transfer(answerUrl) + testModifyCall(nccoUrl = onAnswerUrl, invocation = { + callObj.transfer(URI.create(onAnswerUrl)) + callObj.transfer(onAnswerUrl) }) } @@ -394,8 +402,6 @@ class VoiceTest : AbstractTest() { val eventMethod = HttpMethod.POST val amdBehaviour = MachineDetection.HANGUP val amdMode = AdvancedMachineDetection.Mode.DETECT_BEEP - val wsContentType = "audio/l16;rate=8000" - val userToUserHeader = "56a390f3d2b7310023a" testCreateCall(mapOf( "answer_url" to listOf(answerUrl), @@ -493,10 +499,85 @@ class VoiceTest : AbstractTest() { } @Test - fun `create call with connect action required parameters only`() { + fun `create call with single connect action and builder properties`() { + testSingleNcco( + mapOf( + "endpoint" to listOf(mapOf("type" to phoneType, "number" to altNumber)), + "eventUrl" to listOf(eventUrl), "ringbackTone" to ringbackTone + ), + connectAction(com.vonage.client.voice.ncco.PhoneEndpoint.builder(altNumber).build()) { + eventUrl(eventUrl); ringbackTone(ringbackTone) + } + ) + } + + @Test + fun `create call with single connect action no properties`() { testSingleNcco( - mapOf("endpoint" to listOf(mapOf("type" to "vbc", "extension" to vbcExt))), - connectAction(com.vonage.client.voice.ncco.VbcEndpoint.builder(vbcExt).build()) + mapOf( + "endpoint" to listOf(mapOf("type" to phoneType, "number" to altNumber)), + ), + connectAction(com.vonage.client.voice.ncco.PhoneEndpoint.builder(altNumber).build()) + ) + } + + @Test + fun `create call with connect to VBC ncco`() { + testSingleNccoConnect( + mapOf("extension" to vbcExt), + connectToVbc(vbcExt) + ) + } + + @Test + fun `create call with connect to App ncco`() { + testSingleNccoConnect( + mapOf("user" to user), + connectToApp(user) + ) + } + + @Test + fun `create call with connect to PSTN ncco`() { + testSingleNccoConnect( + mapOf("number" to toNumber), + connectToPstn(toNumber) + ) + + testSingleNccoConnect( + mapOf( + "number" to toNumber, "dtmfAnswer" to dtmf, + "onAnswer" to mapOf("url" to onAnswerUrl, "ringback" to ringbackTone) + ), + connectToPstn(toNumber, dtmf, onAnswerUrl, ringbackTone) + ) + } + + @Test + fun `create call with connect to WebSocket ncco`() { + testSingleNccoConnect( + mapOf("uri" to websocketUri, "content-type" to wsContentType), + connectToWebsocket(websocketUri, wsContentType) + ) + + testSingleNccoConnect( + mapOf("uri" to websocketUri, "content-type" to wsContentType, "headers" to customHeaders), + connectToWebsocket(websocketUri, wsContentType, customHeaders) + ) + } + + @Test + fun `create call with connect to SIP ncco`() { + testSingleNccoConnect( + mapOf("uri" to sipUri), + connectToSip(sipUri) + ) + + testSingleNccoConnect( + mapOf("uri" to sipUri, "headers" to customHeaders, + "standardHeaders" to mapOf("User-to-User" to userToUserHeader) + ), + connectToSip(sipUri, customHeaders, userToUserHeader) ) } @@ -614,7 +695,6 @@ class VoiceTest : AbstractTest() { val machineDetection = MachineDetection.CONTINUE val eventType = EventType.SYNCHRONOUS val connectTimeout = 38 - val ringbackTone = "http://example.com/ringbackTone.wav" val beepTimeout = 90 testCreateCall(mapOf(