diff --git a/CHANGELOG.md b/CHANGELOG.md index 39fdf8e..4a7cf8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Sandbox support in Messages +- Voice API +### Fixed +- `authFromEnv` now checks for absent environment variables before attempting to set them ## [0.1.0] - 2024-06-25 diff --git a/src/main/kotlin/com/vonage/client/kt/Voice.kt b/src/main/kotlin/com/vonage/client/kt/Voice.kt new file mode 100644 index 0000000..8560944 --- /dev/null +++ b/src/main/kotlin/com/vonage/client/kt/Voice.kt @@ -0,0 +1,14 @@ +package com.vonage.client.kt + +import com.vonage.client.voice.CallInfo +import com.vonage.client.voice.VoiceClient +import java.util.* + +class Voice(private val voiceClient: VoiceClient) { + + fun getCall(callId: UUID): CallInfo = getCall(callId.toString()) + + fun getCall(callId: String): CallInfo = voiceClient.getCallDetails(callId) + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vonage/client/kt/Vonage.kt b/src/main/kotlin/com/vonage/client/kt/Vonage.kt index 33cf10a..093599b 100644 --- a/src/main/kotlin/com/vonage/client/kt/Vonage.kt +++ b/src/main/kotlin/com/vonage/client/kt/Vonage.kt @@ -2,16 +2,12 @@ package com.vonage.client.kt import com.vonage.client.HttpConfig import com.vonage.client.VonageClient -import com.vonage.client.account.AccountClient -import com.vonage.client.messages.MessagesClient -import com.vonage.client.verify2.SmsWorkflow -import com.vonage.client.verify2.Verify2Client -import com.vonage.client.voice.VoiceClient class Vonage constructor(init: VonageClient.Builder.() -> Unit) { private val vonageClient : VonageClient = VonageClient.builder().apply(init).build(); val messages = Messages(vonageClient.messagesClient) val verify = Verify(vonageClient.verify2Client) + val voice = Voice(vonageClient.voiceClient) } fun VonageClient.Builder.authFromEnv(): VonageClient.Builder { diff --git a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt index 22cb2ac..af05a09 100644 --- a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt @@ -20,16 +20,19 @@ abstract class AbstractTest { protected val applicationId = "00000000-0000-4000-8000-000000000000" private val apiSecret = "1234567890abcdef" private val apiKeySecretEncoded = "YTFiMmMzZDQ6MTIzNDU2Nzg5MGFiY2RlZg==" + protected val testUuid = UUID.fromString("aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab") protected val toNumber = "447712345689" protected val altNumber = "447700900001" - protected val testUuid = UUID.fromString("aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab") + protected val networkCode = "65512" + protected val startTime = "2020-09-17T12:34:56Z" + protected val endTime = "2021-09-17T12:35:28Z" private val port = 8081 val wiremock: WireMockServer = WireMockServer( options().port(port).notifier(ConsoleNotifier(false)) ) - val vonageClient = Vonage { + val vonage = Vonage { apiKey(apiKey); apiSecret(apiSecret); applicationId(applicationId) privateKeyPath("src/test/resources/com/vonage/client/kt/application_key") httpConfig { @@ -68,7 +71,7 @@ abstract class AbstractTest { contentType: ContentType? = null, accept: ContentType? = null, authType: AuthType? = null, - expectedBodyParams: Map? = null): BuildingStep = + expectedParams: Map? = null): BuildingStep = wiremock.requestServerBuilderStep({ url equalTo expectedUrl headers contains "User-Agent" like "vonage-java-sdk\\/.+ java\\/.+" @@ -87,13 +90,19 @@ abstract class AbstractTest { } if (contentType != null) { headers contains "Content-Type" equalTo contentType.mime + if (expectedParams != null) when(contentType) { + ContentType.APPLICATION_JSON -> { + body equalTo ObjectMapper().writeValueAsString(expectedParams) + } + ContentType.FORM_URLENCODED -> { + // TODO recursive decomposition for nested params + expectedParams.forEach {k, v -> headers contains k equalTo v.toString() } + } + } } if (accept != null) { headers contains "Accept" equalTo accept.mime } - if (expectedBodyParams != null) { - body equalTo ObjectMapper().writeValueAsString(expectedBodyParams) - } }, when (httpMethod) { HttpMethod.GET -> WireMock::get HttpMethod.POST -> WireMock::post @@ -114,9 +123,22 @@ abstract class AbstractTest { AuthType.JWT, expectedRequestParams ).mockReturn(status, expectedResponseParams) - protected fun mockDelete(expectedUrl: String, authType: AuthType? = null): ReturnsStep = + protected fun mockDelete(expectedUrl: String, authType: AuthType? = null) = mockRequest(HttpMethod.DELETE, expectedUrl, authType = authType).mockReturn(204) + protected fun mockGet(expectedUrl: String, + expectedQueryParams: Map? = null, + status: Int = 200, + authType: AuthType? = AuthType.JWT, + expectedResponseParams: Map) = + + mockRequest(HttpMethod.GET, expectedUrl, + contentType = if (expectedQueryParams != null) ContentType.FORM_URLENCODED else null, + accept = ContentType.APPLICATION_JSON, + authType = authType, expectedQueryParams + ).mockReturn(status, expectedResponseParams) + + protected fun BuildingStep.mockReturn( status: Int? = null, expectedBody: Map? = null): ReturnsStep = returns { diff --git a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt index aaed936..c9a3e5c 100644 --- a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt @@ -14,7 +14,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class MessagesTest : AbstractTest() { - private val messagesClient = vonageClient.messages + private val messagesClient = vonage.messages private val sendUrl = "/v1/messages" private val messageUuid = testUuid private val mmsChannel = "mms" diff --git a/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt b/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt index 5dd7f78..9344e34 100644 --- a/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt @@ -9,7 +9,7 @@ import java.util.UUID import kotlin.test.* class VerifyTest : AbstractTest() { - private val verifyClient = vonageClient.verify + private val verifyClient = vonage.verify private val baseUrl = "/v2/verify" private val requestIdStr = "c11236f4-00bf-4b89-84ba-88b25df97315" private val requestId = UUID.fromString(requestIdStr) diff --git a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt new file mode 100644 index 0000000..96f30aa --- /dev/null +++ b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt @@ -0,0 +1,72 @@ +package com.vonage.client.kt + +import com.vonage.client.voice.CallDirection +import com.vonage.client.voice.CallStatus +import com.vonage.client.voice.PhoneEndpoint +import java.time.Instant +import java.util.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class VoiceTest : AbstractTest() { + private val voiceClient = vonage.voice + private val callsUrl = "/v1/calls" + private val callIdStr = "63f61863-4a51-4f6b-86e1-46edebcf9356" + private val conversationId = "CON-f972836a-550f-45fa-956c-12a2ab5b7d22" + + @Test + fun `get call`() { + val price = "23.40" + val duration = 60 + val rate = "0.39" + val phoneType = "phone" + + mockGet(expectedUrl = "$callsUrl/$callIdStr", expectedResponseParams = mapOf( + "_links" to mapOf( + "self" to mapOf( + "href" to "/calls/$callIdStr" + ) + ), + "uuid" to callIdStr, + "conversation_uuid" to conversationId, + "to" to mapOf( + "type" to phoneType, + "number" to toNumber + ), + "from" to mapOf( + "type" to phoneType, + "number" to altNumber + ), + "status" to "completed", + "direction" to "outbound", + "rate" to rate, + "price" to price, + "duration" to "$duration", + "start_time" to startTime, + "end_time" to endTime, + "network" to networkCode + )) + + val response = voiceClient.getCall(UUID.fromString(callIdStr)) + assertNotNull(response) + assertEquals(callIdStr, response.uuid) + assertEquals(conversationId, response.conversationUuid) + val to = response.to + assertNotNull(to) + assertEquals(phoneType, to.type) + assertEquals(toNumber, (to as PhoneEndpoint).number) + val from = response.from + assertNotNull(from) + assertEquals(phoneType, from.type) + assertEquals(altNumber, (from as PhoneEndpoint).number) + assertEquals(CallStatus.COMPLETED, response.status) + assertEquals(CallDirection.OUTBOUND, response.direction) + assertEquals(rate, response.rate) + assertEquals(price, response.price) + assertEquals(duration, response.duration) + assertEquals(Instant.parse(startTime), response.startTime.toInstant()) + assertEquals(Instant.parse(endTime), response.endTime.toInstant()) + assertEquals(networkCode, response.network) + } +} \ No newline at end of file