Skip to content

Commit 22ec333

Browse files
authored
feat: Add Video API (#10)
* 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
1 parent ed07b33 commit 22ec333

File tree

15 files changed

+1271
-43
lines changed

15 files changed

+1271
-43
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [1.0.0-beta1] - 2024-09-??
8+
9+
### Added
10+
- Video API
11+
712
## [0.9.0] - 2024-08-19
813

914
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ You'll need to have [created a Vonage account](https://dashboard.nexmo.com/sign-
3131
- [SMS](https://developer.vonage.com/en/messaging/sms/overview)
3232
- [Subaccounts](https://developer.vonage.com/en/account/subaccounts/overview)
3333
- [Verify](https://developer.vonage.com/en/verify/overview)
34+
- [Video](https://developer.vonage.com/en/video/overview)
3435
- [Voice](https://developer.vonage.com/en/voice/voice-api/overview)
3536

3637
## Other SDKs

src/main/kotlin/com/vonage/client/kt/Subaccounts.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ class Subaccounts internal constructor(private val client: SubaccountsClient) {
3333

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

36-
inner class ExistingSubaccount internal constructor(val subaccountKey: String) {
36+
inner class ExistingSubaccount internal constructor(val key: String) {
3737

38-
fun get(): Account = client.getSubaccount(subaccountKey)
38+
fun get(): Account = client.getSubaccount(key)
3939

4040
fun suspended(suspend: Boolean): Account =
41-
client.updateSubaccount(UpdateSubaccountRequest.builder(subaccountKey).suspended(suspend).build())
41+
client.updateSubaccount(UpdateSubaccountRequest.builder(key).suspended(suspend).build())
4242

4343
fun update(name: String? = null, usePrimaryAccountBalance: Boolean? = null): Account {
44-
val builder = UpdateSubaccountRequest.builder(subaccountKey).name(name)
44+
val builder = UpdateSubaccountRequest.builder(key).name(name)
4545
if (usePrimaryAccountBalance != null) {
4646
builder.usePrimaryAccountBalance(usePrimaryAccountBalance)
4747
}

src/main/kotlin/com/vonage/client/kt/Users.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,25 @@ class Users internal constructor(private val client: UsersClient) {
2323

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

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

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

32-
fun delete(): Unit = client.deleteUser(userId)
32+
fun delete(): Unit = client.deleteUser(id)
3333

3434
@Override
3535
override fun equals(other: Any?): Boolean {
3636
if (this === other) return true
3737
if (javaClass != other?.javaClass) return false
3838
other as ExistingUser
39-
return userId == other.userId
39+
return id == other.id
4040
}
4141

4242
@Override
4343
override fun hashCode(): Int {
44-
return userId.hashCode()
44+
return id.hashCode()
4545
}
4646
}
4747

src/main/kotlin/com/vonage/client/kt/Verify.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ class Verify(private val client: Verify2Client) {
2727
VerificationRequest.builder().brand(brand).apply(init).build()
2828
)
2929

30-
inner class ExistingRequest internal constructor(val requestId: UUID) {
30+
inner class ExistingRequest internal constructor(val id: UUID) {
3131

32-
fun cancel(): Unit = client.cancelVerification(requestId)
32+
fun cancel(): Unit = client.cancelVerification(id)
3333

34-
fun nextWorkflow(): Unit = client.nextWorkflow(requestId)
34+
fun nextWorkflow(): Unit = client.nextWorkflow(id)
3535

3636
fun checkVerificationCode(code: String): VerifyCodeResponse =
37-
client.checkVerificationCode(requestId, code)
37+
client.checkVerificationCode(id, code)
3838

3939
fun isValidVerificationCode(code: String): Boolean {
4040
try {

src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,27 @@ class VerifyLegacy internal constructor(private val client: VerifyClient) {
3232

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

35-
inner class ExistingRequest internal constructor(private val requestId: String) {
35+
inner class ExistingRequest internal constructor(val id: String) {
3636

37-
fun cancel(): ControlResponse = client.cancelVerification(requestId)
37+
fun cancel(): ControlResponse = client.cancelVerification(id)
3838

39-
fun advance(): ControlResponse = client.advanceVerification(requestId)
39+
fun advance(): ControlResponse = client.advanceVerification(id)
4040

41-
fun check(code: String): CheckResponse = client.check(requestId, code)
41+
fun check(code: String): CheckResponse = client.check(id, code)
4242

43-
fun search(): SearchVerifyResponse = client.search(requestId)
43+
fun search(): SearchVerifyResponse = client.search(id)
4444

4545
@Override
4646
override fun equals(other: Any?): Boolean {
4747
if (this === other) return true
4848
if (javaClass != other?.javaClass) return false
4949
other as ExistingRequest
50-
return requestId == other.requestId
50+
return id == other.id
5151
}
5252

5353
@Override
5454
override fun hashCode(): Int {
55-
return requestId.hashCode()
55+
return id.hashCode()
5656
}
5757
}
5858

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* Copyright 2024 Vonage
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.vonage.client.kt
17+
18+
import com.vonage.client.video.*
19+
import java.util.*
20+
21+
class Video(private val client: VideoClient) {
22+
23+
fun createSession(properties: CreateSessionRequest.Builder.() -> Unit = {}): CreateSessionResponse =
24+
client.createSession(CreateSessionRequest.builder().apply(properties).build())
25+
26+
fun session(sessionId: String): ExistingSession = ExistingSession(sessionId)
27+
28+
inner class ExistingSession internal constructor(val id: String) {
29+
30+
fun stream(streamId: String): ExistingStream = ExistingStream(streamId)
31+
32+
inner class ExistingStream internal constructor(val streamId: String) {
33+
34+
fun info(): GetStreamResponse = client.getStream(id, streamId)
35+
36+
fun mute(): Unit = client.muteStream(id, streamId)
37+
38+
fun setLayout(vararg layoutClasses: String): Unit =
39+
client.setStreamLayout(id,
40+
SessionStream.builder(streamId).layoutClassList(layoutClasses.toList()).build()
41+
)
42+
}
43+
44+
fun connection(connectionId: String): ExistingConnection = ExistingConnection(connectionId)
45+
46+
inner class ExistingConnection internal constructor(val id: String) {
47+
48+
fun disconnect(): Unit = client.forceDisconnect(this@ExistingSession.id, id)
49+
50+
fun signal(type: String, data: String): Unit =
51+
client.signal(this@ExistingSession.id, id, signalRequest(type, data))
52+
53+
fun sendDtmf(digits: String): Unit = client.sendDtmf(this@ExistingSession.id, id, digits)
54+
}
55+
56+
fun muteStreams(active: Boolean = true, vararg excludedStreamIds: String): ProjectDetails =
57+
client.muteSession(id, active,
58+
if (excludedStreamIds.isNotEmpty()) excludedStreamIds.toList() else null
59+
)
60+
61+
fun listStreams(): List<GetStreamResponse> = client.listStreams(id)
62+
63+
fun listArchives(count: Int = 1000, offset: Int = 0): List<Archive> =
64+
client.listArchives(listCompositionsFilter(count, offset, id))
65+
66+
fun listBroadcasts(count: Int = 1000, offset: Int = 0): List<Broadcast> =
67+
client.listBroadcasts(listCompositionsFilter(count, offset, id))
68+
69+
fun signalAll(type: String, data: String): Unit =
70+
client.signalAll(id, signalRequest(type, data))
71+
72+
fun sendDtmf(digits: String): Unit = client.sendDtmf(id, digits)
73+
74+
fun startCaptions(token: String, properties: CaptionsRequest.Builder.() -> Unit): UUID =
75+
client.startCaptions(CaptionsRequest.builder()
76+
.apply(properties).sessionId(id).token(token).build()
77+
).captionsId
78+
79+
fun stopCaptions(captionsId: String): Unit = client.stopCaptions(captionsId)
80+
81+
fun createArchive(properties: Archive.Builder.() -> Unit = {}): Archive =
82+
client.createArchive(Archive.builder(id).apply(properties).build())
83+
84+
fun startBroadcast(properties: Broadcast.Builder.() -> Unit): Broadcast =
85+
client.createBroadcast(Broadcast.builder(id).apply(properties).build())
86+
87+
fun generateToken(options: TokenOptions.Builder.() -> Unit = {}): String =
88+
client.generateToken(id, TokenOptions.builder().apply(options).build())
89+
}
90+
91+
fun sipDial(properties: SipDialRequest.Builder.() -> Unit): SipDialResponse =
92+
client.sipDial(SipDialRequest.builder().apply(properties).build())
93+
94+
fun connectToWebsocket(properties: ConnectRequest.Builder.() -> Unit): ConnectResponse =
95+
client.connectToWebsocket(ConnectRequest.builder().apply(properties).build())
96+
97+
fun listArchives(count: Int = 1000, offset: Int = 0): List<Archive> =
98+
client.listArchives(listCompositionsFilter(count, offset))
99+
100+
fun archive(archiveId: String): ExistingArchive = ExistingArchive(archiveId)
101+
102+
inner class ExistingArchive internal constructor(val id: String) {
103+
104+
fun info(): Archive = client.getArchive(id)
105+
106+
fun stop(): Archive = client.stopArchive(id)
107+
108+
fun delete(): Unit = client.deleteArchive(id)
109+
110+
fun setLayout(initialLayout: ScreenLayoutType,
111+
screenshareType: ScreenLayoutType? = null,
112+
stylesheet: String? = null): Unit =
113+
client.updateArchiveLayout(id,
114+
StreamCompositionLayout.builder(initialLayout)
115+
.screenshareType(screenshareType)
116+
.stylesheet(stylesheet)
117+
.build()
118+
)
119+
120+
fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
121+
client.addArchiveStream(id, streamId, audio, video)
122+
123+
fun removeStream(streamId: String): Unit = client.removeArchiveStream(id, streamId)
124+
}
125+
126+
fun listBroadcasts(count: Int = 1000, offset: Int = 0): List<Broadcast> =
127+
client.listBroadcasts(listCompositionsFilter(count, offset))
128+
129+
fun broadcast(broadcastId: String): ExistingBroadcast = ExistingBroadcast(broadcastId)
130+
131+
inner class ExistingBroadcast internal constructor(val id: String) {
132+
133+
fun info(): Broadcast = client.getBroadcast(id)
134+
135+
fun stop(): Broadcast = client.stopBroadcast(id)
136+
137+
fun setLayout(initialLayout: ScreenLayoutType,
138+
screenshareType: ScreenLayoutType? = null,
139+
stylesheet: String? = null): Unit =
140+
client.updateBroadcastLayout(id,
141+
StreamCompositionLayout.builder(initialLayout)
142+
.screenshareType(screenshareType)
143+
.stylesheet(stylesheet)
144+
.build()
145+
)
146+
147+
fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
148+
client.addBroadcastStream(id, streamId, audio, video)
149+
150+
fun removeStream(streamId: String): Unit = client.removeBroadcastStream(id, streamId)
151+
}
152+
153+
fun startRender(properties: RenderRequest.Builder.() -> Unit): RenderResponse =
154+
client.startRender(RenderRequest.builder().apply(properties).build())
155+
156+
fun listRenders(count: Int = 1000, offset: Int = 0): List<RenderResponse> =
157+
client.listRenders(ListStreamCompositionsRequest.builder().count(count).offset(offset).build())
158+
159+
fun render(renderId: String): ExistingRender = ExistingRender(renderId)
160+
161+
inner class ExistingRender internal constructor(val id: String) {
162+
163+
fun info(): RenderResponse = client.getRender(id)
164+
165+
fun stop(): Unit = client.stopRender(id)
166+
}
167+
}
168+
169+
private fun listCompositionsFilter(count: Int, offset: Int, sessionId: String? = null):
170+
ListStreamCompositionsRequest = ListStreamCompositionsRequest.builder()
171+
.count(count).offset(offset).sessionId(sessionId).build()
172+
173+
private fun signalRequest(type: String, data: String): SignalRequest =
174+
SignalRequest.builder().type(type).data(data).build()
175+
176+
private fun streamCompositionLayout(initialLayout: ScreenLayoutType,
177+
screenshareType: ScreenLayoutType?,
178+
stylesheet: String?): StreamCompositionLayout =
179+
StreamCompositionLayout.builder(initialLayout)
180+
.screenshareType(screenshareType).stylesheet(stylesheet).build()
181+
182+
fun Broadcast.Builder.addRtmpStream(properties: Rtmp.Builder.() -> Unit): Broadcast.Builder =
183+
addRtmpStream(Rtmp.builder().apply(properties).build())
184+
185+
fun Broadcast.Builder.hls(hls: Hls.Builder.() -> Unit = {}): Broadcast.Builder =
186+
hls(Hls.builder().apply(hls).build())
187+
188+
fun Archive.Builder.layout(initialLayout: ScreenLayoutType,
189+
screenshareType: ScreenLayoutType? = null,
190+
stylesheet: String? = null): Archive.Builder =
191+
layout(streamCompositionLayout(initialLayout, screenshareType, stylesheet))
192+
193+
fun Broadcast.Builder.layout(initialLayout: ScreenLayoutType,
194+
screenshareType: ScreenLayoutType? = null,
195+
stylesheet: String? = null): Broadcast.Builder =
196+
layout(streamCompositionLayout(initialLayout, screenshareType, stylesheet))

src/main/kotlin/com/vonage/client/kt/Voice.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,37 @@ class Voice internal constructor(private val client: VoiceClient) {
2525

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

28-
inner class ExistingCall internal constructor(val callId: String) {
28+
inner class ExistingCall internal constructor(val id: String) {
2929

30-
fun info(): CallInfo = client.getCallDetails(callId)
30+
fun info(): CallInfo = client.getCallDetails(id)
3131

32-
fun hangup(): Unit = client.terminateCall(callId)
32+
fun hangup(): Unit = client.terminateCall(id)
3333

34-
fun mute(): Unit = client.muteCall(callId)
34+
fun mute(): Unit = client.muteCall(id)
3535

36-
fun unmute(): Unit = client.unmuteCall(callId)
36+
fun unmute(): Unit = client.unmuteCall(id)
3737

38-
fun earmuff(): Unit = client.earmuffCall(callId)
38+
fun earmuff(): Unit = client.earmuffCall(id)
3939

40-
fun unearmuff(): Unit = client.unearmuffCall(callId)
40+
fun unearmuff(): Unit = client.unearmuffCall(id)
4141

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

44-
fun transfer(nccoUrl: String): Unit = client.transferCall(callId, nccoUrl)
44+
fun transfer(nccoUrl: String): Unit = client.transferCall(id, nccoUrl)
4545

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

48-
fun sendDtmf(digits: String): DtmfResponse = client.sendDtmf(callId, digits)
48+
fun sendDtmf(digits: String): DtmfResponse = client.sendDtmf(id, digits)
4949

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

53-
fun stopStream(): StreamResponse = client.stopStream(callId)
53+
fun stopStream(): StreamResponse = client.stopStream(id)
5454

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

58-
fun stopTalk(): TalkResponse = client.stopTalk(callId)
58+
fun stopTalk(): TalkResponse = client.stopTalk(id)
5959
}
6060

6161
fun listCalls(filter: (CallsFilter.Builder.() -> Unit)? = null): CallInfoPage =

src/main/kotlin/com/vonage/client/kt/Vonage.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Vonage(init: VonageClient.Builder.() -> Unit) {
3434
val users = Users(client.usersClient)
3535
val verify = Verify(client.verify2Client)
3636
val verifyLegacy = VerifyLegacy(client.verifyClient)
37+
val video = Video(client.videoClient)
3738
val voice = Voice(client.voiceClient)
3839
}
3940

0 commit comments

Comments
 (0)