From 655f84f9e9071eeeacf6df09f2848639e38f8e62 Mon Sep 17 00:00:00 2001 From: Sina Madani Date: Fri, 16 Aug 2024 12:48:26 +0100 Subject: [PATCH 1/3] feat: Add Users --- CHANGELOG.md | 2 +- src/main/kotlin/com/vonage/client/kt/Users.kt | 48 ++++ .../kotlin/com/vonage/client/kt/Vonage.kt | 1 + .../com/vonage/client/kt/AbstractTest.kt | 9 +- .../com/vonage/client/kt/MessagesTest.kt | 4 - .../kotlin/com/vonage/client/kt/UsersTest.kt | 214 ++++++++++++++++++ .../kotlin/com/vonage/client/kt/VoiceTest.kt | 1 - 7 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/vonage/client/kt/Users.kt create mode 100644 src/test/kotlin/com/vonage/client/kt/UsersTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b02f06..7caf085 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [0.9.0] - 2024-08-2? ### Added -- Application API +- Application & Users API ## [0.8.0] - 2024-08-09 diff --git a/src/main/kotlin/com/vonage/client/kt/Users.kt b/src/main/kotlin/com/vonage/client/kt/Users.kt new file mode 100644 index 0000000..3982689 --- /dev/null +++ b/src/main/kotlin/com/vonage/client/kt/Users.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Vonage + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vonage.client.kt + +import com.vonage.client.users.* + +class Users internal constructor(private val client: UsersClient) { + + fun user(userId: String): ExistingUser = ExistingUser(userId) + + fun user(user: BaseUser): ExistingUser = ExistingUser(user.id) + + inner class ExistingUser internal constructor(val userId: String) { + fun get(): User = client.getUser(userId) + + fun update(properties: User.Builder.() -> Unit): User = + client.updateUser(userId, User.builder().apply(properties).build()) + + fun delete(): Unit = client.deleteUser(userId) + } + + fun create(properties: User.Builder.() -> Unit): User = + client.createUser(User.builder().apply(properties).build()) + + fun list(filter: (ListUsersRequest.Builder.() -> Unit)? = null): ListUsersResponse { + val request = ListUsersRequest.builder() + if (filter == null) { + request.pageSize(100) + } + else { + request.apply(filter) + } + return client.listUsers(request.build()) + } +} diff --git a/src/main/kotlin/com/vonage/client/kt/Vonage.kt b/src/main/kotlin/com/vonage/client/kt/Vonage.kt index 9c0a304..5738635 100644 --- a/src/main/kotlin/com/vonage/client/kt/Vonage.kt +++ b/src/main/kotlin/com/vonage/client/kt/Vonage.kt @@ -31,6 +31,7 @@ class Vonage(init: VonageClient.Builder.() -> Unit) { val simSwap = SimSwap(client.simSwapClient) val sms = Sms(client.smsClient) val subaccounts = Subaccounts(client.subaccountsClient) + val users = Users(client.usersClient) val verify = Verify(client.verify2Client) val verifyLegacy = VerifyLegacy(client.verifyClient) val voice = Voice(client.voiceClient) diff --git a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt index a6c384c..d8833cb 100644 --- a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt @@ -58,6 +58,7 @@ abstract class AbstractTest { protected val country = "GB" protected val secret = "ABCDEFGH01234abc" protected val sipUri = "sip:rebekka@sip.example.com" + protected val websocketUri = "wss://example.com/socket" protected val clientRef = "my-personal-reference" protected val textHexEncoded = "48656c6c6f2c20576f726c6421" protected val entityId = "1101407360000017170" @@ -84,6 +85,10 @@ abstract class AbstractTest { protected val statusCallbackUrl = "$callbackUrl/status" protected val moCallbackUrl = "$callbackUrl/inbound-sms" protected val drCallbackUrl = "$callbackUrl/delivery-receipt" + protected val imageUrl = "$exampleUrlBase/image.jpg" + protected val audioUrl = "$exampleUrlBase/audio.mp3" + protected val videoUrl = "$exampleUrlBase/video.mp4" + protected val fileUrl = "$exampleUrlBase/file.pdf" private val port = 8081 private val wiremock: WireMockServer = WireMockServer( @@ -281,7 +286,7 @@ abstract class AbstractTest { protected inline fun assertApiResponseException( url: String, requestMethod: HttpMethod, actualCall: () -> Any, status: Int, - errorType: String? = null, title: String? = null, + errorType: String? = null, title: String? = null, code: String? = null, detail: String? = null, instance: String? = null): E { val responseParams = mutableMapOf() @@ -289,6 +294,7 @@ abstract class AbstractTest { if (title != null) responseParams["title"] = title if (detail != null) responseParams["detail"] = detail if (instance != null) responseParams["instance"] = instance + if (code != null) responseParams["code"] = code mockRequest(requestMethod, url).mockReturn(status, responseParams) val exception = assertThrows { actualCall.invoke() } @@ -298,6 +304,7 @@ abstract class AbstractTest { assertEquals(title, exception.title) assertEquals(instance, exception.instance) assertEquals(detail, exception.detail) + assertEquals(code, exception.code) return exception } diff --git a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt index 18e43c8..50cb21d 100644 --- a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt @@ -37,10 +37,6 @@ class MessagesTest : AbstractTest() { private val viberChannel = "viber_service" private val messengerChannel = "messenger" private val caption = "Additional text to accompany the media" - private val imageUrl = "https://example.com/image.jpg" - private val audioUrl = "https://example.com/audio.mp3" - private val videoUrl = "https://example.com/video.mp4" - private val fileUrl = "https://example.com/file.pdf" private val captionMap = mapOf("caption" to caption) diff --git a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt new file mode 100644 index 0000000..b624160 --- /dev/null +++ b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt @@ -0,0 +1,214 @@ +/* + * Copyright 2024 Vonage + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vonage.client.kt + +import com.vonage.client.common.HttpMethod +import com.vonage.client.users.* +import java.net.URI +import kotlin.test.* + +class UsersTest : AbstractTest() { + private val client = vonage.users + private val authType = AuthType.JWT + private val baseUrl = "/v1/users" + private val userId = "USR-82e028d9-5201-4f1e-8188-604b2d3471ec" + private val userUrl = "$baseUrl/$userId" + private val existingUser = client.user(userId) + private val name = "my_user_name" + private val displayName = "Test User" + private val ttl = 3600 + private val pageSize = 10 + private val customData = mapOf("custom_key" to "custom_value") + private val sipUser = "sip_user" + private val sipPassword = "Passw0rd1234" + private val messengerId = "12345abcd" + private val baseUserRequest = mapOf("name" to name) + private val userIdMapOnly = mapOf("id" to userId) + private val baseUserResponse = userIdMapOnly + baseUserRequest + private val fullUserRequest = baseUserRequest + mapOf( + "display_name" to displayName, + "image_url" to imageUrl, + "properties" to mapOf( + "custom_data" to customData, + "ttl" to ttl + ), + "custom_data" to customData, + "channels" to mapOf( + "sip" to listOf(mapOf( + "uri" to sipUri, + "username" to sipUser, + "password" to sipPassword + )), + "messenger" to listOf(mapOf( + "id" to messengerId + )) + ) + ) + private val fullUserResponse = userIdMapOnly + fullUserRequest + + private fun assertEqualsUser(parsed: User) { + assertEquals(userId, parsed.id) + assertEquals(name, parsed.name) + assertEquals(displayName, parsed.displayName) + assertEquals(URI.create(imageUrl), parsed.imageUrl) + assertEquals(customData, parsed.customData) + val channels = parsed.channels + assertNotNull(channels) + // TODO the rest + } + + private fun assertUserNotFoundException(method: HttpMethod, invocation: Users.ExistingUser.() -> Any) = + assertApiResponseException( + url = userUrl, requestMethod = method, + actualCall = { invocation.invoke(existingUser) }, + status = 404, + title = "Not found.", + errorType = "https://developer.vonage.com/api/conversation#user:error:not-found", + code = "user:error:not-found", + detail = "User does not exist, or you do not have access.", + instance = "00a5916655d650e920ccf0daf40ef4ee" + ) + + private fun assertListUsers(filter: Map, invocation: Users.() -> ListUsersResponse) { + mockGet( + expectedUrl = baseUrl, authType = authType, + expectedQueryParams = filter, + expectedResponseParams = mapOf( + "page_size" to pageSize, + "_embedded" to mapOf( + "users" to listOf( + mapOf(), + mapOf( + "id" to userId, + "name" to name, + "display_name" to displayName, + "_links" to mapOf( + "self" to mapOf( + "href" to "https://api.nexmo.com$userUrl" + ) + ) + ), + userIdMapOnly + ) + ), + "_links" to mapOf( + "first" to mapOf( + "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10" + ), + "self" to mapOf( + "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" + ), + "next" to mapOf( + "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" + ), + "prev" to mapOf( + "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" + ) + ) + ) + ) + val response = invocation.invoke(client) + assertNotNull(response) + val users = response.users + assertNotNull(users) + assertEquals(3, users.size) + // TODO remaining assertions + } + + @Test + fun `get user`() { + mockGet(expectedUrl = userUrl, authType = authType, expectedResponseParams = fullUserResponse) + assertEqualsUser(existingUser.get()) + assertUserNotFoundException(HttpMethod.GET, Users.ExistingUser::get) + } + + @Test + fun `delete user`() { + mockDelete(expectedUrl = userUrl, authType = authType) + existingUser.delete() + assertUserNotFoundException(HttpMethod.DELETE, Users.ExistingUser::delete) + } + + @Test + fun `update user`() { + val newDisplayName = "Updated DP" + val newPic = "$exampleUrlBase/new_pic.png" + mockPatch( + expectedUrl = userUrl, authType = authType, + expectedRequestParams = userIdMapOnly + mapOf( + "display_name" to newDisplayName, + "image_url" to newPic + ), + expectedResponseParams = fullUserResponse + ) + assertEqualsUser(existingUser.update { + displayName(newDisplayName) + imageUrl(newPic) + }) + + assertUserNotFoundException(HttpMethod.PATCH) { update { }} + } + + @Test + fun `create user required parameters`() { + mockPost( + expectedUrl = baseUrl, authType = authType, + expectedRequestParams = mapOf(), + expectedResponseParams = fullUserResponse + ) + assertEqualsUser(client.create {}) + + assert401ApiResponseException(baseUrl, HttpMethod.POST) { + client.create {} + } + } + + @Test + fun `create user all parameters`() { + mockPost( + expectedUrl = baseUrl, authType = authType, + expectedRequestParams = fullUserRequest, + expectedResponseParams = fullUserResponse + ) + assertEqualsUser(client.create { + name(name) + displayName(displayName) + imageUrl(imageUrl) + customData(customData) + // TODO channels + }) + } + + @Test + fun `list users no filter`() { + assertListUsers(emptyMap(), Users::list) + assert401ApiResponseException(baseUrl, HttpMethod.GET, client::list) + } + + @Test + fun `list users all filters`() { + assertListUsers(mapOf( + "order" to "desc", + "page_size" to pageSize + // TODO remaining filters + )) { + list { + order(ListUsersRequest.SortOrder.DESC) + pageSize(pageSize) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt index 2bfe797..b35667e 100644 --- a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt @@ -42,7 +42,6 @@ class VoiceTest : AbstractTest() { private val vbcExt = "4321" private val streamUrl = "$exampleUrlBase/waiting.mp3" private val onAnswerUrl = "$exampleUrlBase/ncco.json" - private val websocketUri = "wss://example.com/socket" private val ringbackTone = "http://example.org/ringbackTone.wav" private val wsContentType = "audio/l16;rate=8000" private val userToUserHeader = "56a390f3d2b7310023a" From c5fadbde9245627c34ece9360b07e0fe663213ca Mon Sep 17 00:00:00 2001 From: Sina Madani Date: Mon, 19 Aug 2024 14:53:39 +0100 Subject: [PATCH 2/3] Fix tests --- .../com/vonage/client/kt/AbstractTest.kt | 4 + .../kotlin/com/vonage/client/kt/UsersTest.kt | 149 ++++++++++++++---- .../kotlin/com/vonage/client/kt/VoiceTest.kt | 15 +- 3 files changed, 125 insertions(+), 43 deletions(-) diff --git a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt index d8833cb..7d3618b 100644 --- a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt @@ -57,8 +57,12 @@ abstract class AbstractTest { protected val text = "Hello, World!" protected val country = "GB" protected val secret = "ABCDEFGH01234abc" + protected val cursor = "7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg=" + protected val vbcExt = "4321" + protected val userName = "Sam_username" protected val sipUri = "sip:rebekka@sip.example.com" protected val websocketUri = "wss://example.com/socket" + protected val wsContentType = "audio/l16;rate=8000" protected val clientRef = "my-personal-reference" protected val textHexEncoded = "48656c6c6f2c20576f726c6421" protected val entityId = "1101407360000017170" diff --git a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt index b624160..9ee0e98 100644 --- a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt @@ -17,6 +17,7 @@ package com.vonage.client.kt import com.vonage.client.common.HttpMethod import com.vonage.client.users.* +import com.vonage.client.users.channels.* import java.net.URI import kotlin.test.* @@ -27,47 +28,113 @@ class UsersTest : AbstractTest() { private val userId = "USR-82e028d9-5201-4f1e-8188-604b2d3471ec" private val userUrl = "$baseUrl/$userId" private val existingUser = client.user(userId) - private val name = "my_user_name" private val displayName = "Test User" - private val ttl = 3600 + private val ttl = 3600 // TODO support private val pageSize = 10 private val customData = mapOf("custom_key" to "custom_value") private val sipUser = "sip_user" private val sipPassword = "Passw0rd1234" private val messengerId = "12345abcd" - private val baseUserRequest = mapOf("name" to name) + private val href = "https://api.nexmo.com/users" + private val order = "desc" + private val firstHref = "$href?order=$order&page_size=$pageSize" + private val navUrl = "$firstHref&cursor=$cursor" + private val userHref = "https://api.nexmo.com$userUrl" + private val navUrlMap = mapOf("href" to navUrl) + private val baseUserRequest = mapOf("name" to userName) private val userIdMapOnly = mapOf("id" to userId) private val baseUserResponse = userIdMapOnly + baseUserRequest private val fullUserRequest = baseUserRequest + mapOf( "display_name" to displayName, "image_url" to imageUrl, - "properties" to mapOf( - "custom_data" to customData, - "ttl" to ttl - ), - "custom_data" to customData, "channels" to mapOf( + "pstn" to listOf(mapOf( + "number" to toNumber + )), "sip" to listOf(mapOf( "uri" to sipUri, "username" to sipUser, "password" to sipPassword )), + "vbc" to listOf(mapOf( + "extension" to vbcExt + )), + "websocket" to listOf(mapOf( + "uri" to websocketUri, + "content-type" to wsContentType, + "headers" to customData + )), + "mms" to listOf(mapOf( + "number" to toNumber + )), + "whatsapp" to listOf(mapOf( + "number" to altNumber + )), + "viber" to listOf(mapOf( + "number" to altNumber + )), "messenger" to listOf(mapOf( "id" to messengerId )) + ), + "properties" to mapOf( + "custom_data" to customData ) ) private val fullUserResponse = userIdMapOnly + fullUserRequest private fun assertEqualsUser(parsed: User) { assertEquals(userId, parsed.id) - assertEquals(name, parsed.name) + assertEquals(userName, parsed.name) assertEquals(displayName, parsed.displayName) assertEquals(URI.create(imageUrl), parsed.imageUrl) assertEquals(customData, parsed.customData) val channels = parsed.channels assertNotNull(channels) - // TODO the rest + + val pstn = channels.pstn + assertNotNull(pstn) + assertEquals(1, pstn.size) + assertEquals(toNumber, pstn[0].number) + + val mms = channels.mms + assertNotNull(mms) + assertEquals(1, mms.size) + assertEquals(toNumber, mms[0].number) + + val whatsapp = channels.whatsapp + assertNotNull(whatsapp) + assertEquals(1, whatsapp.size) + assertEquals(altNumber, whatsapp[0].number) + + val viber = channels.viber + assertNotNull(viber) + assertEquals(1, viber.size) + assertEquals(altNumber, viber[0].number) + + val messenger = channels.messenger + assertNotNull(messenger) + assertEquals(1, messenger.size) + assertEquals(messengerId, messenger[0].id) + + val websocket = channels.websocket + assertNotNull(websocket) + assertEquals(1, websocket.size) + assertEquals(URI.create(websocketUri), websocket[0].uri) + assertEquals(Websocket.ContentType.fromString(wsContentType), websocket[0].contentType) + assertEquals(customData, websocket[0].headers) + + val sip = channels.sip + assertNotNull(sip) + assertEquals(1, sip.size) + assertEquals(URI.create(sipUri), sip[0].uri) + assertEquals(sipUser, sip[0].username) + assertEquals(sipPassword, sip[0].password) + + val vbc = channels.vbc + assertNotNull(vbc) + assertEquals(1, vbc.size) + assertEquals(vbcExt, vbc[0].extension) } private fun assertUserNotFoundException(method: HttpMethod, invocation: Users.ExistingUser.() -> Any) = @@ -93,30 +160,20 @@ class UsersTest : AbstractTest() { mapOf(), mapOf( "id" to userId, - "name" to name, + "name" to userName, "display_name" to displayName, "_links" to mapOf( - "self" to mapOf( - "href" to "https://api.nexmo.com$userUrl" - ) + "self" to mapOf("href" to userHref) ) ), userIdMapOnly ) ), "_links" to mapOf( - "first" to mapOf( - "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10" - ), - "self" to mapOf( - "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" - ), - "next" to mapOf( - "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" - ), - "prev" to mapOf( - "href" to "https://api.nexmo.com/v1/users?order=desc&page_size=10&cursor=7EjDNQrAcipmOnc0HCzpQRkhBULzY44ljGUX4lXKyUIVfiZay5pv9wg%3D" - ) + "first" to mapOf("href" to firstHref), + "self" to navUrlMap, + "next" to navUrlMap, + "prev" to navUrlMap ) ) ) @@ -125,7 +182,25 @@ class UsersTest : AbstractTest() { val users = response.users assertNotNull(users) assertEquals(3, users.size) - // TODO remaining assertions + val emptyUser = users[0] + assertNull(emptyUser.id) + assertNull(emptyUser.name) + val mainUser = users[1] + assertEquals(userId, mainUser.id) + assertEquals(userName, mainUser.name) + val idOnlyUser = users[2] + assertEquals(userId, idOnlyUser.id) + assertNull(idOnlyUser.name) + val links = response.links + assertNotNull(links) + assertEquals(URI.create(firstHref), links.firstUrl) + assertEquals(URI.create(navUrl), links.selfUrl) + assertEquals(URI.create(navUrl), links.nextUrl) + assertEquals(URI.create(navUrl), links.prevUrl) + + assert401ApiResponseException(baseUrl, HttpMethod.GET) { + invocation.invoke(client) + } } @Test @@ -177,18 +252,23 @@ class UsersTest : AbstractTest() { } @Test - fun `create user all parameters`() { + fun `create user all parameters and channels`() { mockPost( expectedUrl = baseUrl, authType = authType, expectedRequestParams = fullUserRequest, expectedResponseParams = fullUserResponse ) assertEqualsUser(client.create { - name(name) + name(userName) displayName(displayName) imageUrl(imageUrl) customData(customData) - // TODO channels + channels( + Pstn(toNumber), Sip(sipUri, sipUser, sipPassword), + Websocket(websocketUri, Websocket.ContentType.fromString(wsContentType), customData), + Vbc(vbcExt.toInt()), Mms(toNumber), Whatsapp(altNumber), + Viber(altNumber), Messenger(messengerId) + ) }) } @@ -201,13 +281,14 @@ class UsersTest : AbstractTest() { @Test fun `list users all filters`() { assertListUsers(mapOf( + "page_size" to pageSize, "order" to "desc", - "page_size" to pageSize - // TODO remaining filters + "cursor" to cursor, + "name" to userName )) { list { - order(ListUsersRequest.SortOrder.DESC) - pageSize(pageSize) + order(ListUsersRequest.SortOrder.DESC); name(userName) + pageSize(pageSize); cursor(URI.create(navUrl)) } } } diff --git a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt index b35667e..9d112d6 100644 --- a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt @@ -38,12 +38,9 @@ class VoiceTest : AbstractTest() { private val recordIndex = 14 private val dtmf = "p*123#" private val fromPstn = "14155550100" - private val user = "Sam" - private val vbcExt = "4321" private val streamUrl = "$exampleUrlBase/waiting.mp3" private val onAnswerUrl = "$exampleUrlBase/ncco.json" private val ringbackTone = "http://example.org/ringbackTone.wav" - private val wsContentType = "audio/l16;rate=8000" private val userToUserHeader = "56a390f3d2b7310023a" private val conversationName = "selective-audio Demo" private val customHeaders = mapOf( @@ -428,8 +425,8 @@ class VoiceTest : AbstractTest() { @Test fun `create call to App`() { - testCreateCallToSingleEndpoint(mapOf("type" to "app", "user" to user)) { - toApp(user) + testCreateCallToSingleEndpoint(mapOf("type" to "app", "user" to userName)) { + toApp(userName) } } @@ -485,7 +482,7 @@ class VoiceTest : AbstractTest() { "to" to listOf( mapOf( "type" to "app", - "user" to user + "user" to userName ), mapOf( "type" to phoneType, @@ -532,7 +529,7 @@ class VoiceTest : AbstractTest() { behavior(amdBehaviour); beepTimeout(beepTimeout); mode(amdMode) } to( - com.vonage.client.voice.AppEndpoint(user), + com.vonage.client.voice.AppEndpoint(userName), com.vonage.client.voice.PhoneEndpoint(toNumber, dtmf), com.vonage.client.voice.VbcEndpoint(vbcExt), com.vonage.client.voice.WebSocketEndpoint(websocketUri, wsContentType, customHeaders), @@ -608,8 +605,8 @@ class VoiceTest : AbstractTest() { @Test fun `create call with connect to App ncco`() { testSingleNccoConnect( - mapOf("user" to user), - connectToApp(user) + mapOf("user" to userName), + connectToApp(userName) ) } From 29bcb685708fb3b4557d39a79aad957ba79b215b Mon Sep 17 00:00:00 2001 From: Sina Madani Date: Mon, 19 Aug 2024 15:41:49 +0100 Subject: [PATCH 3/3] Equals & hashCode for ExistingUser --- src/main/kotlin/com/vonage/client/kt/Users.kt | 13 +++++++++++ .../kotlin/com/vonage/client/kt/UsersTest.kt | 22 +++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/vonage/client/kt/Users.kt b/src/main/kotlin/com/vonage/client/kt/Users.kt index 3982689..038e33a 100644 --- a/src/main/kotlin/com/vonage/client/kt/Users.kt +++ b/src/main/kotlin/com/vonage/client/kt/Users.kt @@ -30,6 +30,19 @@ class Users internal constructor(private val client: UsersClient) { client.updateUser(userId, User.builder().apply(properties).build()) fun delete(): Unit = client.deleteUser(userId) + + @Override + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as ExistingUser + return userId == other.userId + } + + @Override + override fun hashCode(): Int { + return userId.hashCode() + } } fun create(properties: User.Builder.() -> Unit): User = diff --git a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt index 9ee0e98..4e4cb66 100644 --- a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt +++ b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt @@ -43,9 +43,8 @@ class UsersTest : AbstractTest() { private val navUrlMap = mapOf("href" to navUrl) private val baseUserRequest = mapOf("name" to userName) private val userIdMapOnly = mapOf("id" to userId) - private val baseUserResponse = userIdMapOnly + baseUserRequest - private val fullUserRequest = baseUserRequest + mapOf( - "display_name" to displayName, + private val displayNameMap = mapOf("display_name" to displayName) + private val fullUserRequest = baseUserRequest + displayNameMap + mapOf( "image_url" to imageUrl, "channels" to mapOf( "pstn" to listOf(mapOf( @@ -158,10 +157,7 @@ class UsersTest : AbstractTest() { "_embedded" to mapOf( "users" to listOf( mapOf(), - mapOf( - "id" to userId, - "name" to userName, - "display_name" to displayName, + userIdMapOnly + baseUserRequest + displayNameMap + mapOf( "_links" to mapOf( "self" to mapOf("href" to userHref) ) @@ -198,11 +194,23 @@ class UsersTest : AbstractTest() { assertEquals(URI.create(navUrl), links.nextUrl) assertEquals(URI.create(navUrl), links.prevUrl) + assertEquals(existingUser.userId, client.user(idOnlyUser).userId) + assert401ApiResponseException(baseUrl, HttpMethod.GET) { invocation.invoke(client) } } + @Test + fun `existing user equals and hashCode`() { + val differentUser = client.user("USR-$testUuidStr") + assertEquals(existingUser, existingUser) + assertFalse(existingUser.equals(userId)) + assertEquals(userId.hashCode(), existingUser.hashCode()) + assertEquals(existingUser, client.user(userId)) + assertNotEquals(existingUser, differentUser) + } + @Test fun `get user`() { mockGet(expectedUrl = userUrl, authType = authType, expectedResponseParams = fullUserResponse)