Skip to content

Commit

Permalink
feat: Add Video API (#10)
Browse files Browse the repository at this point in the history
* wip: Video API files

* Bump surefire plugin

* Add Video implementation

* Test sessions

* Test Experience Composer

* Test generateToken

* Factor out JWT duplication

* Rename ExistingX IDs

* Archive & Broadcast responses

* Fix failing tests

* Stream selection tests

* listArchives / listBroadcasts tests

* Test createArchive / createBroadcast
  • Loading branch information
SMadani authored Aug 30, 2024
1 parent ed07b33 commit 22ec333
Show file tree
Hide file tree
Showing 15 changed files with 1,271 additions and 43 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.0.0-beta1] - 2024-09-??

### Added
- Video API

## [0.9.0] - 2024-08-19

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ You'll need to have [created a Vonage account](https://dashboard.nexmo.com/sign-
- [SMS](https://developer.vonage.com/en/messaging/sms/overview)
- [Subaccounts](https://developer.vonage.com/en/account/subaccounts/overview)
- [Verify](https://developer.vonage.com/en/verify/overview)
- [Video](https://developer.vonage.com/en/video/overview)
- [Voice](https://developer.vonage.com/en/voice/voice-api/overview)

## Other SDKs
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/vonage/client/kt/Subaccounts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ class Subaccounts internal constructor(private val client: SubaccountsClient) {

fun subaccount(subaccountKey: String): ExistingSubaccount = ExistingSubaccount(subaccountKey)

inner class ExistingSubaccount internal constructor(val subaccountKey: String) {
inner class ExistingSubaccount internal constructor(val key: String) {

fun get(): Account = client.getSubaccount(subaccountKey)
fun get(): Account = client.getSubaccount(key)

fun suspended(suspend: Boolean): Account =
client.updateSubaccount(UpdateSubaccountRequest.builder(subaccountKey).suspended(suspend).build())
client.updateSubaccount(UpdateSubaccountRequest.builder(key).suspended(suspend).build())

fun update(name: String? = null, usePrimaryAccountBalance: Boolean? = null): Account {
val builder = UpdateSubaccountRequest.builder(subaccountKey).name(name)
val builder = UpdateSubaccountRequest.builder(key).name(name)
if (usePrimaryAccountBalance != null) {
builder.usePrimaryAccountBalance(usePrimaryAccountBalance)
}
Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/com/vonage/client/kt/Users.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@ class Users internal constructor(private val client: UsersClient) {

fun user(user: BaseUser): ExistingUser = ExistingUser(user.id)

inner class ExistingUser internal constructor(val userId: String) {
fun get(): User = client.getUser(userId)
inner class ExistingUser internal constructor(val id: String) {
fun get(): User = client.getUser(id)

fun update(properties: User.Builder.() -> Unit): User =
client.updateUser(userId, User.builder().apply(properties).build())
client.updateUser(id, User.builder().apply(properties).build())

fun delete(): Unit = client.deleteUser(userId)
fun delete(): Unit = client.deleteUser(id)

@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
return id == other.id
}

@Override
override fun hashCode(): Int {
return userId.hashCode()
return id.hashCode()
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/vonage/client/kt/Verify.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class Verify(private val client: Verify2Client) {
VerificationRequest.builder().brand(brand).apply(init).build()
)

inner class ExistingRequest internal constructor(val requestId: UUID) {
inner class ExistingRequest internal constructor(val id: UUID) {

fun cancel(): Unit = client.cancelVerification(requestId)
fun cancel(): Unit = client.cancelVerification(id)

fun nextWorkflow(): Unit = client.nextWorkflow(requestId)
fun nextWorkflow(): Unit = client.nextWorkflow(id)

fun checkVerificationCode(code: String): VerifyCodeResponse =
client.checkVerificationCode(requestId, code)
client.checkVerificationCode(id, code)

fun isValidVerificationCode(code: String): Boolean {
try {
Expand Down
14 changes: 7 additions & 7 deletions src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,27 @@ class VerifyLegacy internal constructor(private val client: VerifyClient) {

fun request(response: VerifyResponse): ExistingRequest = request(response.requestId)

inner class ExistingRequest internal constructor(private val requestId: String) {
inner class ExistingRequest internal constructor(val id: String) {

fun cancel(): ControlResponse = client.cancelVerification(requestId)
fun cancel(): ControlResponse = client.cancelVerification(id)

fun advance(): ControlResponse = client.advanceVerification(requestId)
fun advance(): ControlResponse = client.advanceVerification(id)

fun check(code: String): CheckResponse = client.check(requestId, code)
fun check(code: String): CheckResponse = client.check(id, code)

fun search(): SearchVerifyResponse = client.search(requestId)
fun search(): SearchVerifyResponse = client.search(id)

@Override
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ExistingRequest
return requestId == other.requestId
return id == other.id
}

@Override
override fun hashCode(): Int {
return requestId.hashCode()
return id.hashCode()
}
}

Expand Down
196 changes: 196 additions & 0 deletions src/main/kotlin/com/vonage/client/kt/Video.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* 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.video.*
import java.util.*

class Video(private val client: VideoClient) {

fun createSession(properties: CreateSessionRequest.Builder.() -> Unit = {}): CreateSessionResponse =
client.createSession(CreateSessionRequest.builder().apply(properties).build())

fun session(sessionId: String): ExistingSession = ExistingSession(sessionId)

inner class ExistingSession internal constructor(val id: String) {

fun stream(streamId: String): ExistingStream = ExistingStream(streamId)

inner class ExistingStream internal constructor(val streamId: String) {

fun info(): GetStreamResponse = client.getStream(id, streamId)

fun mute(): Unit = client.muteStream(id, streamId)

fun setLayout(vararg layoutClasses: String): Unit =
client.setStreamLayout(id,
SessionStream.builder(streamId).layoutClassList(layoutClasses.toList()).build()
)
}

fun connection(connectionId: String): ExistingConnection = ExistingConnection(connectionId)

inner class ExistingConnection internal constructor(val id: String) {

fun disconnect(): Unit = client.forceDisconnect(this@ExistingSession.id, id)

fun signal(type: String, data: String): Unit =
client.signal(this@ExistingSession.id, id, signalRequest(type, data))

fun sendDtmf(digits: String): Unit = client.sendDtmf(this@ExistingSession.id, id, digits)
}

fun muteStreams(active: Boolean = true, vararg excludedStreamIds: String): ProjectDetails =
client.muteSession(id, active,
if (excludedStreamIds.isNotEmpty()) excludedStreamIds.toList() else null
)

fun listStreams(): List<GetStreamResponse> = client.listStreams(id)

fun listArchives(count: Int = 1000, offset: Int = 0): List<Archive> =
client.listArchives(listCompositionsFilter(count, offset, id))

fun listBroadcasts(count: Int = 1000, offset: Int = 0): List<Broadcast> =
client.listBroadcasts(listCompositionsFilter(count, offset, id))

fun signalAll(type: String, data: String): Unit =
client.signalAll(id, signalRequest(type, data))

fun sendDtmf(digits: String): Unit = client.sendDtmf(id, digits)

fun startCaptions(token: String, properties: CaptionsRequest.Builder.() -> Unit): UUID =
client.startCaptions(CaptionsRequest.builder()
.apply(properties).sessionId(id).token(token).build()
).captionsId

fun stopCaptions(captionsId: String): Unit = client.stopCaptions(captionsId)

fun createArchive(properties: Archive.Builder.() -> Unit = {}): Archive =
client.createArchive(Archive.builder(id).apply(properties).build())

fun startBroadcast(properties: Broadcast.Builder.() -> Unit): Broadcast =
client.createBroadcast(Broadcast.builder(id).apply(properties).build())

fun generateToken(options: TokenOptions.Builder.() -> Unit = {}): String =
client.generateToken(id, TokenOptions.builder().apply(options).build())
}

fun sipDial(properties: SipDialRequest.Builder.() -> Unit): SipDialResponse =
client.sipDial(SipDialRequest.builder().apply(properties).build())

fun connectToWebsocket(properties: ConnectRequest.Builder.() -> Unit): ConnectResponse =
client.connectToWebsocket(ConnectRequest.builder().apply(properties).build())

fun listArchives(count: Int = 1000, offset: Int = 0): List<Archive> =
client.listArchives(listCompositionsFilter(count, offset))

fun archive(archiveId: String): ExistingArchive = ExistingArchive(archiveId)

inner class ExistingArchive internal constructor(val id: String) {

fun info(): Archive = client.getArchive(id)

fun stop(): Archive = client.stopArchive(id)

fun delete(): Unit = client.deleteArchive(id)

fun setLayout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Unit =
client.updateArchiveLayout(id,
StreamCompositionLayout.builder(initialLayout)
.screenshareType(screenshareType)
.stylesheet(stylesheet)
.build()
)

fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
client.addArchiveStream(id, streamId, audio, video)

fun removeStream(streamId: String): Unit = client.removeArchiveStream(id, streamId)
}

fun listBroadcasts(count: Int = 1000, offset: Int = 0): List<Broadcast> =
client.listBroadcasts(listCompositionsFilter(count, offset))

fun broadcast(broadcastId: String): ExistingBroadcast = ExistingBroadcast(broadcastId)

inner class ExistingBroadcast internal constructor(val id: String) {

fun info(): Broadcast = client.getBroadcast(id)

fun stop(): Broadcast = client.stopBroadcast(id)

fun setLayout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Unit =
client.updateBroadcastLayout(id,
StreamCompositionLayout.builder(initialLayout)
.screenshareType(screenshareType)
.stylesheet(stylesheet)
.build()
)

fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
client.addBroadcastStream(id, streamId, audio, video)

fun removeStream(streamId: String): Unit = client.removeBroadcastStream(id, streamId)
}

fun startRender(properties: RenderRequest.Builder.() -> Unit): RenderResponse =
client.startRender(RenderRequest.builder().apply(properties).build())

fun listRenders(count: Int = 1000, offset: Int = 0): List<RenderResponse> =
client.listRenders(ListStreamCompositionsRequest.builder().count(count).offset(offset).build())

fun render(renderId: String): ExistingRender = ExistingRender(renderId)

inner class ExistingRender internal constructor(val id: String) {

fun info(): RenderResponse = client.getRender(id)

fun stop(): Unit = client.stopRender(id)
}
}

private fun listCompositionsFilter(count: Int, offset: Int, sessionId: String? = null):
ListStreamCompositionsRequest = ListStreamCompositionsRequest.builder()
.count(count).offset(offset).sessionId(sessionId).build()

private fun signalRequest(type: String, data: String): SignalRequest =
SignalRequest.builder().type(type).data(data).build()

private fun streamCompositionLayout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType?,
stylesheet: String?): StreamCompositionLayout =
StreamCompositionLayout.builder(initialLayout)
.screenshareType(screenshareType).stylesheet(stylesheet).build()

fun Broadcast.Builder.addRtmpStream(properties: Rtmp.Builder.() -> Unit): Broadcast.Builder =
addRtmpStream(Rtmp.builder().apply(properties).build())

fun Broadcast.Builder.hls(hls: Hls.Builder.() -> Unit = {}): Broadcast.Builder =
hls(Hls.builder().apply(hls).build())

fun Archive.Builder.layout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Archive.Builder =
layout(streamCompositionLayout(initialLayout, screenshareType, stylesheet))

fun Broadcast.Builder.layout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Broadcast.Builder =
layout(streamCompositionLayout(initialLayout, screenshareType, stylesheet))
28 changes: 14 additions & 14 deletions src/main/kotlin/com/vonage/client/kt/Voice.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,37 @@ class Voice internal constructor(private val client: VoiceClient) {

fun call(callId: String): ExistingCall = ExistingCall(callId)

inner class ExistingCall internal constructor(val callId: String) {
inner class ExistingCall internal constructor(val id: String) {

fun info(): CallInfo = client.getCallDetails(callId)
fun info(): CallInfo = client.getCallDetails(id)

fun hangup(): Unit = client.terminateCall(callId)
fun hangup(): Unit = client.terminateCall(id)

fun mute(): Unit = client.muteCall(callId)
fun mute(): Unit = client.muteCall(id)

fun unmute(): Unit = client.unmuteCall(callId)
fun unmute(): Unit = client.unmuteCall(id)

fun earmuff(): Unit = client.earmuffCall(callId)
fun earmuff(): Unit = client.earmuffCall(id)

fun unearmuff(): Unit = client.unearmuffCall(callId)
fun unearmuff(): Unit = client.unearmuffCall(id)

fun transfer(vararg actions: Action): Unit = client.transferCall(callId, Ncco(actions.asList()))
fun transfer(vararg actions: Action): Unit = client.transferCall(id, Ncco(actions.asList()))

fun transfer(nccoUrl: String): Unit = client.transferCall(callId, nccoUrl)
fun transfer(nccoUrl: String): Unit = client.transferCall(id, nccoUrl)

fun transfer(nccoUrl: URI): Unit = transfer(nccoUrl.toString())

fun sendDtmf(digits: String): DtmfResponse = client.sendDtmf(callId, digits)
fun sendDtmf(digits: String): DtmfResponse = client.sendDtmf(id, digits)

fun streamAudio(streamUrl: String, loop: Int = 1, level: Double = 0.0): StreamResponse =
client.startStream(callId, streamUrl, loop, level)
client.startStream(id, streamUrl, loop, level)

fun stopStream(): StreamResponse = client.stopStream(callId)
fun stopStream(): StreamResponse = client.stopStream(id)

fun startTalk(text: String, properties: (TalkPayload.Builder.() -> Unit) = {}): TalkResponse =
client.startTalk(callId, TalkPayload.builder(text).apply(properties).build())
client.startTalk(id, TalkPayload.builder(text).apply(properties).build())

fun stopTalk(): TalkResponse = client.stopTalk(callId)
fun stopTalk(): TalkResponse = client.stopTalk(id)
}

fun listCalls(filter: (CallsFilter.Builder.() -> Unit)? = null): CallInfoPage =
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/com/vonage/client/kt/Vonage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class Vonage(init: VonageClient.Builder.() -> Unit) {
val users = Users(client.usersClient)
val verify = Verify(client.verify2Client)
val verifyLegacy = VerifyLegacy(client.verifyClient)
val video = Video(client.videoClient)
val voice = Voice(client.voiceClient)
}

Expand Down
Loading

0 comments on commit 22ec333

Please sign in to comment.