diff --git a/CHANGELOG.md b/CHANGELOG.md
index acfd3a5..ee9b375 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,13 +4,34 @@ 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] - 2024-09-12
+GA release!
+
+### [1.0.0-beta2] - 2024-09-06
+
+### Added
+- Documentation (KDocs) for all classes and methods
+
+### Changed
+- Moved Video API's `connectToWebSocket`, `startRender` and `sipDial` methods to `ExistingSession`
+- `CallsFilter.Builder.dateStart` and `dateEnd` extension functions now accept `Instant` instead of `String`
+- `Voice.inputAction` requires body
+- `Voice.connectToWebSocket` and `Call.Builder.toWebSocket` `contentType` parameter changed to (mandatory) enum
+
+### Removed
+- `Voice.ExistingCall.transfer(URI)` method
+
## [1.0.0-beta1] - 2024-09-02
+Feature-complete beta release
### Added
- Video API
### Changed
-- Standardised `Existing*` classes to extend `ExistingResource` for consistency
+- Renamed `VerifyLegacy.ExistingRequest#search` to `info` for consistency with other APIs
+
+### Changed
+- Standardised `Existing*` classes to extend `ExistingResource` for consistency.
## [0.9.0] - 2024-08-19
@@ -77,10 +98,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `authFromEnv` now checks for absent environment variables before attempting to set them
## [0.1.0] - 2024-06-25
-
-Initial version.
+Initial version
### Added
- Messages API
- Verify v2 API
-
diff --git a/pom.xml b/pom.xml
index 528e716..9381708 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.vonage
server-sdk-kotlin
- 1.0.0-beta1
+ 1.0.0-beta2
Vonage Kotlin Server SDK
Kotlin client for Vonage APIs
diff --git a/src/main/kotlin/com/vonage/client/kt/Account.kt b/src/main/kotlin/com/vonage/client/kt/Account.kt
index 89ebf86..1c3fb4d 100644
--- a/src/main/kotlin/com/vonage/client/kt/Account.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Account.kt
@@ -17,32 +17,115 @@ package com.vonage.client.kt
import com.vonage.client.account.*
+/**
+ * Implementation of the [Account API](https://developer.vonage.com/en/api/account).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Account internal constructor(private val client: AccountClient) {
+ /**
+ * Obtains the current account remaining balance.
+ *
+ * @return A [BalanceResponse] object containing the current account balance.
+ * @throws [AccountResponseException] If the balance cannot be retrieved.
+ */
fun getBalance(): BalanceResponse = client.balance
+ /**
+ * You can top up your account using this API when you have enabled auto-reload in the dashboard.
+ * The amount added by the top-up operation will be the same amount as was added in the payment when
+ * auto-reload was enabled. Your account balance is checked every 5-10 minutes and if it falls below the
+ * threshold and auto-reload is enabled, then it will be topped up automatically. Use this method if you
+ * need to top up at times when your credit may be exhausted more quickly than the auto-reload may occur.
+ *
+ * @param transactionId The top-up transaction ID.
+ *
+ * @throws [AccountResponseException] If the top-up operation fails.
+ */
fun topUp(transactionId: String): Unit = client.topUp(transactionId)
+ /**
+ * Updates the top-level account settings. Namely, the URLs for incoming SMS and delivery receipts.
+ *
+ * @param incomingSmsUrl The URL to which incoming SMS messages are sent when using SMS API.
+ * @param deliverReceiptUrl The URL to which delivery receipts are sent when using SMS API.
+ *
+ * @return The updated account settings.
+ *
+ * @throws [AccountResponseException] If the account settings could not be updated.
+ */
fun updateSettings(incomingSmsUrl: String? = null, deliverReceiptUrl: String? = null): SettingsResponse =
client.updateSettings(SettingsRequest(incomingSmsUrl, deliverReceiptUrl))
+ /**
+ * Call this method to work with account secrets.
+ *
+ * @param apiKey (OPTIONAL) The account API key to manage secrets for. If not provided,
+ * the default API key (as supplied in the top-level [Vonage] client) will be used.
+ *
+ * @return A [Secrets] object with methods to interact with account secrets.
+ */
fun secrets(apiKey: String? = null): Secrets = Secrets(apiKey)
+ /**
+ * Class for working with account secrets.
+ *
+ * @property apiKey The account API key to manage secrets for. If not provided,
+ * the default API key (as supplied in the top-level [Vonage] client) will be used.
+ */
inner class Secrets internal constructor(val apiKey: String? = null) {
+ /**
+ * Retrieves secrets for the account.
+ *
+ * @return A list of secrets details.
+ *
+ * @throws [AccountResponseException] If the secrets cannot be retrieved.
+ */
fun list(): List = (
if (apiKey == null) client.listSecrets()
else client.listSecrets(apiKey)
).secrets
+ /**
+ * Creates a new secret for the account.
+ *
+ * @param secret The secret value to associate with the API key, which must follow these rules:
+ * - Minimum 8 characters
+ * - Maximum 25 characters
+ * - Minimum 1 lower case character
+ * - Minimum 1 upper case character
+ * - Minimum 1 digit
+ *
+ * @return The created secret's metadata.
+ *
+ * @throws [AccountResponseException] If the secret cannot be created.
+ */
fun create(secret: String): SecretResponse =
if (apiKey == null) client.createSecret(secret)
else client.createSecret(apiKey, secret)
+ /**
+ * Retrieves a secret by its ID.
+ *
+ * @param secretId ID of the secret to retrieve.
+ *
+ * @return The secret's metadata.
+ *
+ * @throws [AccountResponseException] If the secret cannot be retrieved.
+ */
fun get(secretId: String): SecretResponse =
if (apiKey == null) client.getSecret(secretId)
else client.getSecret(apiKey, secretId)
+ /**
+ * Deletes a secret by its ID.
+ *
+ * @param secretId ID of the secret to delete.
+ *
+ * @throws [AccountResponseException] If the secret cannot be deleted.
+ */
fun delete(secretId: String): Unit =
if (apiKey == null) client.revokeSecret(secretId)
else client.revokeSecret(apiKey, secretId)
diff --git a/src/main/kotlin/com/vonage/client/kt/Application.kt b/src/main/kotlin/com/vonage/client/kt/Application.kt
index 2584cef..ef0aa44 100644
--- a/src/main/kotlin/com/vonage/client/kt/Application.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Application.kt
@@ -24,23 +24,88 @@ import com.vonage.client.application.capabilities.Voice
import com.vonage.client.common.Webhook
import java.util.*
+/**
+ * Implementation of the [Application API](https://developer.vonage.com/en/api/application.v2).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Application internal constructor(private val client: ApplicationClient) {
+ /**
+ * Call this method to work with an existing application.
+ *
+ * @param applicationId The UUID of the application to work with.
+ *
+ * @return An [ExistingApplication] object with methods to interact with the application.
+ */
fun application(applicationId: String): ExistingApplication = ExistingApplication(applicationId)
+
+ /**
+ * Call this method to work with an existing application.
+ *
+ * @param applicationId The UUID of the application to work with.
+ *
+ * @return An [ExistingApplication] object with methods to interact with the application.
+ */
fun application(applicationId: UUID) = application(applicationId.toString())
+ /**
+ * Class for working with an existing application.
+ *
+ * @property id The application ID.
+ */
inner class ExistingApplication internal constructor(id: String): ExistingResource(id) {
+
+ /**
+ * Retrieves the application details.
+ *
+ * @return The application details.
+ *
+ * @throws [ApplicationResponseException] If the application details could not be retrieved.
+ */
fun get(): Application = client.getApplication(id)
+ /**
+ * Updates the application details.
+ *
+ * @param properties A lambda function that takes an [Application.Builder] object as its receiver.
+ * Use this to specify properties to update on the application.
+ *
+ * @return The updated application details.
+ *
+ * @throws [ApplicationResponseException] If the application details could not be updated.
+ */
fun update(properties: Application.Builder.() -> Unit): Application =
client.updateApplication(Application.builder(get()).apply(properties).build())
+ /**
+ * Deletes the application.
+ *
+ * @throws [ApplicationResponseException] If the application could not be deleted.
+ */
fun delete(): Unit = client.deleteApplication(id)
}
+ /**
+ * Retrieves a list of all applications associated with your Vonage account (up to the first 1000).
+ *
+ * @return A list containing all application details (maximum 1000).
+ *
+ * @throws [ApplicationResponseException] If the list of applications could not be retrieved.
+ */
fun listAll(): List = client.listAllApplications()
+ /**
+ * Retrieves a list of applications associated with your Vonage account.
+ *
+ * @param page (OPTIONAL) The page number to retrieve.
+ * @param pageSize (OPTIONAL) The maximum number of applications per page.
+ *
+ * @return A list containing application details.
+ *
+ * @throws [ApplicationResponseException] If the list of applications could not be retrieved.
+ */
fun list(page: Int? = null, pageSize: Int? = null): List {
val filter = ListApplicationRequest.builder()
if (page != null) filter.page(page.toLong())
@@ -48,6 +113,16 @@ class Application internal constructor(private val client: ApplicationClient) {
return client.listApplications(filter.build()).applications
}
+ /**
+ * Creates a new application.
+ *
+ * @param properties A lambda function that takes an [Application.Builder] object as its receiver.
+ * Use this to specify properties for the new application.
+ *
+ * @return The newly created application details.
+ *
+ * @throws [ApplicationResponseException] If the application could not be created.
+ */
fun create(properties: Application.Builder.() -> Unit): Application =
client.createApplication(Application.builder().apply(properties).build())
}
@@ -55,29 +130,93 @@ class Application internal constructor(private val client: ApplicationClient) {
private fun webhookBuilder(properties: Webhook.Builder.() -> Unit): Webhook =
Webhook.builder().apply(properties).build()
+/**
+ * Adds a webhook to the application. This is an alias for [Webhook.Builder.address].
+ *
+ * @param url The URL to send the webhook to as a string.
+ *
+ * @return The webhook builder.
+ */
fun Webhook.Builder.url(url: String): Webhook.Builder = address(url)
+/**
+ * Adds an `answer_url` webhook to the [Voice] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Voice capability builder.
+ */
fun Voice.Builder.answer(properties: Webhook.Builder.() -> Unit): Voice.Builder =
addWebhook(Webhook.Type.ANSWER, webhookBuilder(properties))
+/**
+ * Adds an `fallback_answer_url` webhook to the [Voice] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Voice capability builder.
+ */
fun Voice.Builder.fallbackAnswer(properties: Webhook.Builder.() -> Unit): Voice.Builder =
addWebhook(Webhook.Type.FALLBACK_ANSWER, webhookBuilder(properties))
+/**
+ * Adds an `event_url` webhook to the [Voice] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Voice capability builder.
+ */
fun Voice.Builder.event(properties: Webhook.Builder.() -> Unit): Voice.Builder =
addWebhook(Webhook.Type.EVENT, webhookBuilder(properties))
+/**
+ * Adds an `event_url` webhook to the [Rtc] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The RTC capability builder.
+ */
fun Rtc.Builder.event(properties: Webhook.Builder.() -> Unit): Rtc.Builder =
addWebhook(Webhook.Type.EVENT, webhookBuilder(properties))
+/**
+ * Adds a `status_url` webhook to the [Verify] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Verify capability builder.
+ */
fun Verify.Builder.status(properties: Webhook.Builder.() -> Unit): Verify.Builder =
addWebhook(Webhook.Type.STATUS, webhookBuilder(properties))
+/**
+ * Adds an `inbound_url` webhook to the [Messages] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Messages capability builder.
+ */
fun Messages.Builder.inbound(properties: Webhook.Builder.() -> Unit): Messages.Builder =
addWebhook(Webhook.Type.INBOUND, webhookBuilder(properties))
+/**
+ * Adds an `status_url` webhook to the [Messages] capability.
+ *
+ * @param properties A lambda function for setting additional properties on the webhook.
+ *
+ * @return The Messages capability builder.
+ */
fun Messages.Builder.status(properties: Webhook.Builder.() -> Unit): Messages.Builder =
addWebhook(Webhook.Type.STATUS, webhookBuilder(properties))
+/**
+ * Removes one or more capabilities from the application, used in conjunction with
+ * [com.vonage.client.kt.Application.ExistingApplication#update].
+ *
+ * @param capabilities The capability types to remove.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.removeCapabilities(vararg capabilities: Capability.Type): Application.Builder {
for (capability in capabilities) {
removeCapability(capability)
@@ -85,16 +224,49 @@ fun Application.Builder.removeCapabilities(vararg capabilities: Capability.Type)
return this
}
+/**
+ * Adds a [Voice] capability to the application.
+ *
+ * @param capability A lambda function for setting additional properties on the capability.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.voice(capability: Voice.Builder.() -> Unit): Application.Builder =
addCapability(Voice.builder().apply(capability).build())
+/**
+ * Adds a [Messages] capability to the application.
+ *
+ * @param capability A lambda function for setting additional properties on the capability.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.messages(capability: Messages.Builder.() -> Unit): Application.Builder =
addCapability(Messages.builder().apply(capability).build())
+/**
+ * Adds a [Verify] capability to the application.
+ *
+ * @param capability A lambda function for setting additional properties on the capability.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.verify(capability: Verify.Builder.() -> Unit): Application.Builder =
addCapability(Verify.builder().apply(capability).build())
+/**
+ * Adds an [Rtc] capability to the application.
+ *
+ * @param capability A lambda function for setting additional properties on the capability.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.rtc(capability: Rtc.Builder.() -> Unit): Application.Builder =
addCapability(Rtc.builder().apply(capability).build())
+/**
+ * Adds a [Vbc] capability to the application.
+ *
+ * @return The application builder.
+ */
fun Application.Builder.vbc(): Application.Builder = addCapability(Vbc.builder().build())
diff --git a/src/main/kotlin/com/vonage/client/kt/Conversion.kt b/src/main/kotlin/com/vonage/client/kt/Conversion.kt
index 40b32f5..fb3996b 100644
--- a/src/main/kotlin/com/vonage/client/kt/Conversion.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Conversion.kt
@@ -19,6 +19,11 @@ import com.vonage.client.conversion.*
import java.time.Instant
import java.util.*
+/**
+ * Implementation of the [Conversion API](https://developer.vonage.com/en/api/Conversion).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Conversion internal constructor(private val client: ConversionClient) {
private fun convert(type: ConversionRequest.Type,
@@ -27,9 +32,23 @@ class Conversion internal constructor(private val client: ConversionClient) {
if (timestamp != null) Date.from(timestamp) else null
)
+ /**
+ * Submit conversion for an SMS message.
+ *
+ * @param messageId The message ID.
+ * @param delivered `true` if the message was delivered, `false` otherwise.
+ * @param timestamp (OPTIONAL) Timestamp of the conversion.
+ */
fun convertSms(messageId: String, delivered: Boolean, timestamp: Instant? = null): Unit =
convert(ConversionRequest.Type.SMS, messageId, delivered, timestamp)
+ /**
+ * Submit conversion for a voice call.
+ *
+ * @param callId The call UUID.
+ * @param delivered `true` if the call was received, `false` otherwise.
+ * @param timestamp (OPTIONAL) Timestamp of the conversion.
+ */
fun convertVoice(callId: String, delivered: Boolean, timestamp: Instant? = null): Unit =
convert(ConversionRequest.Type.VOICE, callId, delivered, timestamp)
}
diff --git a/src/main/kotlin/com/vonage/client/kt/ExistingResource.kt b/src/main/kotlin/com/vonage/client/kt/ExistingResource.kt
index e80ae63..fc24853 100644
--- a/src/main/kotlin/com/vonage/client/kt/ExistingResource.kt
+++ b/src/main/kotlin/com/vonage/client/kt/ExistingResource.kt
@@ -15,6 +15,12 @@
*/
package com.vonage.client.kt
+/**
+ * Base class for top-level endpoint domain objects with IDs.
+ * This factors out the ID field and provides a common `toString`, `equals`, and `hashCode` implementation.
+ *
+ * @param id The unique resource identifier.
+ */
open class ExistingResource internal constructor(val id: String) {
@Override
diff --git a/src/main/kotlin/com/vonage/client/kt/Messages.kt b/src/main/kotlin/com/vonage/client/kt/Messages.kt
index 4447895..554d1e4 100644
--- a/src/main/kotlin/com/vonage/client/kt/Messages.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Messages.kt
@@ -23,83 +23,285 @@ import com.vonage.client.messages.messenger.*
import com.vonage.client.messages.viber.*
import java.util.UUID
+/**
+ * Implementation of the [Messages API](https://developer.vonage.com/en/api/messages-olympus).
+ *
+ * *Authentication method:* JWT (recommended), API key & secret (limited functionality).
+ */
class Messages internal constructor(private val client: MessagesClient) {
+
+ /**
+ * Send a message.
+ *
+ * The details of its format, channel, sender, recipient etc. are specified entirely by
+ * the type and contents of the [MessageRequest]. This file contains utility functions for creating
+ * each valid combination of [Channel] and [com.vonage.client.common.MessageType]. Generally, the only
+ * required parameters are the `from` (sender), `to` (recipient), and the message content, which is
+ * typically either `text` (for text messages) or `url` (for media messages).
+ *
+ * @param message The message to send. Use one of the DSL functions to create the message.
+ * @param sandbox (OPTIONAL) Set to `true` to use the Messages API Sandbox endpoint.
+ *
+ * @return UUID of the message.
+ *
+ * @throws [MessageResponseException] If the message could not be sent. This may be for the following reasons:
+ * - **401**: Invalid credentials.
+ * - **402**: The account balance is too low.
+ * - **422**: Malformed request.
+ * - **429**: Too many requests.
+ * - **500**: Internal server error.
+ */
fun send(message: MessageRequest, sandbox: Boolean = false): UUID =
(if (sandbox) client.useSandboxEndpoint()
else client.useRegularEndpoint()).sendMessage(message).messageUuid
}
-fun smsText(init: SmsTextRequest.Builder.() -> Unit): SmsTextRequest =
- SmsTextRequest.builder().apply(init).build()
+/**
+ * Create an SMS text message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [SmsTextRequest] object with the specified properties.
+ */
+fun smsText(properties: SmsTextRequest.Builder.() -> Unit): SmsTextRequest =
+ SmsTextRequest.builder().apply(properties).build()
-fun mmsVcard(init: MmsVcardRequest.Builder.() -> Unit): MmsVcardRequest =
- MmsVcardRequest.builder().apply(init).build()
+/**
+ * Creates an MMS vCard message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return An [MmsVcardRequest] object with the specified properties.
+ */
+fun mmsVcard(properties: MmsVcardRequest.Builder.() -> Unit): MmsVcardRequest =
+ MmsVcardRequest.builder().apply(properties).build()
-fun mmsImage(init: MmsImageRequest.Builder.() -> Unit): MmsImageRequest =
- MmsImageRequest.builder().apply(init).build()
+/**
+ * Creates an MMS image message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return An [MmsImageRequest] object with the specified properties.
+ */
+fun mmsImage(properties: MmsImageRequest.Builder.() -> Unit): MmsImageRequest =
+ MmsImageRequest.builder().apply(properties).build()
-fun mmsAudio(init: MmsAudioRequest.Builder.() -> Unit): MmsAudioRequest =
- MmsAudioRequest.builder().apply(init).build()
+/**
+ * Creates an MMS audio message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return An [MmsAudioRequest] object with the specified properties.
+ */
+fun mmsAudio(properties: MmsAudioRequest.Builder.() -> Unit): MmsAudioRequest =
+ MmsAudioRequest.builder().apply(properties).build()
-fun mmsVideo(init: MmsVideoRequest.Builder.() -> Unit): MmsVideoRequest =
- MmsVideoRequest.builder().apply(init).build()
+/**
+ * Creates an MMS video message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return An [MmsVideoRequest] object with the specified properties.
+ */
+fun mmsVideo(properties: MmsVideoRequest.Builder.() -> Unit): MmsVideoRequest =
+ MmsVideoRequest.builder().apply(properties).build()
-fun whatsappText(init: WhatsappTextRequest.Builder.() -> Unit): WhatsappTextRequest =
- WhatsappTextRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp text message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappTextRequest] object with the specified properties.
+ */
+fun whatsappText(properties: WhatsappTextRequest.Builder.() -> Unit): WhatsappTextRequest =
+ WhatsappTextRequest.builder().apply(properties).build()
-fun whatsappImage(init: WhatsappImageRequest.Builder.() -> Unit): WhatsappImageRequest =
- WhatsappImageRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp image message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappImageRequest] object with the specified properties.
+ */
+fun whatsappImage(properties: WhatsappImageRequest.Builder.() -> Unit): WhatsappImageRequest =
+ WhatsappImageRequest.builder().apply(properties).build()
-fun whatsappAudio(init: WhatsappAudioRequest.Builder.() -> Unit): WhatsappAudioRequest =
- WhatsappAudioRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp audio message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappAudioRequest] object with the specified properties.
+ */
+fun whatsappAudio(properties: WhatsappAudioRequest.Builder.() -> Unit): WhatsappAudioRequest =
+ WhatsappAudioRequest.builder().apply(properties).build()
-fun whatsappVideo(init: WhatsappVideoRequest.Builder.() -> Unit): WhatsappVideoRequest =
- WhatsappVideoRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp video message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappVideoRequest] object with the specified properties.
+ */
+fun whatsappVideo(properties: WhatsappVideoRequest.Builder.() -> Unit): WhatsappVideoRequest =
+ WhatsappVideoRequest.builder().apply(properties).build()
-fun whatsappFile(init: WhatsappFileRequest.Builder.() -> Unit): WhatsappFileRequest =
- WhatsappFileRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp file message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappFileRequest] object with the specified properties.
+ */
+fun whatsappFile(properties: WhatsappFileRequest.Builder.() -> Unit): WhatsappFileRequest =
+ WhatsappFileRequest.builder().apply(properties).build()
-fun whatsappSticker(init: WhatsappStickerRequest.Builder.() -> Unit): WhatsappStickerRequest =
- WhatsappStickerRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp sticker message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappStickerRequest] object with the specified properties.
+ */
+fun whatsappSticker(properties: WhatsappStickerRequest.Builder.() -> Unit): WhatsappStickerRequest =
+ WhatsappStickerRequest.builder().apply(properties).build()
-fun whatsappLocation(init: WhatsappLocationRequest.Builder.() -> Unit): WhatsappLocationRequest =
- WhatsappLocationRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp location message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappLocationRequest] object with the specified properties.
+ */
+fun whatsappLocation(properties: WhatsappLocationRequest.Builder.() -> Unit): WhatsappLocationRequest =
+ WhatsappLocationRequest.builder().apply(properties).build()
-fun whatsappSingleProduct(init: WhatsappSingleProductRequest.Builder.() -> Unit): WhatsappSingleProductRequest =
- WhatsappSingleProductRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp single product message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappSingleProductRequest] object with the specified properties.
+ */
+fun whatsappSingleProduct(properties: WhatsappSingleProductRequest.Builder.() -> Unit): WhatsappSingleProductRequest =
+ WhatsappSingleProductRequest.builder().apply(properties).build()
-fun whatsappMultiProduct(init: WhatsappMultiProductRequest.Builder.() -> Unit): WhatsappMultiProductRequest =
- WhatsappMultiProductRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp multi product message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappMultiProductRequest] object with the specified properties.
+ */
+fun whatsappMultiProduct(properties: WhatsappMultiProductRequest.Builder.() -> Unit): WhatsappMultiProductRequest =
+ WhatsappMultiProductRequest.builder().apply(properties).build()
-fun whatsappTemplate(init: WhatsappTemplateRequest.Builder.() -> Unit): WhatsappTemplateRequest =
- WhatsappTemplateRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp template message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappTemplateRequest] object with the specified properties.
+ */
+fun whatsappTemplate(properties: WhatsappTemplateRequest.Builder.() -> Unit): WhatsappTemplateRequest =
+ WhatsappTemplateRequest.builder().apply(properties).build()
-fun whatsappCustom(init: WhatsappCustomRequest.Builder.() -> Unit): WhatsappCustomRequest =
- WhatsappCustomRequest.builder().apply(init).build()
+/**
+ * Creates a WhatsApp custom message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [WhatsappCustomRequest] object with the specified properties.
+ */
+fun whatsappCustom(properties: WhatsappCustomRequest.Builder.() -> Unit): WhatsappCustomRequest =
+ WhatsappCustomRequest.builder().apply(properties).build()
-fun messengerText(init: MessengerTextRequest.Builder.() -> Unit): MessengerTextRequest =
- MessengerTextRequest.builder().apply(init).build()
+/**
+ * Creates a Messenger text message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [MessengerTextRequest] object with the specified properties.
+ */
+fun messengerText(properties: MessengerTextRequest.Builder.() -> Unit): MessengerTextRequest =
+ MessengerTextRequest.builder().apply(properties).build()
-fun messengerImage(init: MessengerImageRequest.Builder.() -> Unit): MessengerImageRequest =
- MessengerImageRequest.builder().apply(init).build()
+/**
+ * Creates a Messenger image message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [MessengerImageRequest] object with the specified properties.
+ */
+fun messengerImage(properties: MessengerImageRequest.Builder.() -> Unit): MessengerImageRequest =
+ MessengerImageRequest.builder().apply(properties).build()
-fun messengerAudio(init: MessengerAudioRequest.Builder.() -> Unit): MessengerAudioRequest =
- MessengerAudioRequest.builder().apply(init).build()
+/**
+ * Creates a Messenger audio message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [MessengerAudioRequest] object with the specified properties.
+ */
+fun messengerAudio(properties: MessengerAudioRequest.Builder.() -> Unit): MessengerAudioRequest =
+ MessengerAudioRequest.builder().apply(properties).build()
-fun messengerVideo(init: MessengerVideoRequest.Builder.() -> Unit): MessengerVideoRequest =
- MessengerVideoRequest.builder().apply(init).build()
+/**
+ * Creates a Messenger video message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [MessengerVideoRequest] object with the specified properties.
+ */
+fun messengerVideo(properties: MessengerVideoRequest.Builder.() -> Unit): MessengerVideoRequest =
+ MessengerVideoRequest.builder().apply(properties).build()
-fun messengerFile(init: MessengerFileRequest.Builder.() -> Unit): MessengerFileRequest =
- MessengerFileRequest.builder().apply(init).build()
+/**
+ * Creates a Messenger file message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [MessengerFileRequest] object with the specified properties.
+ */
+fun messengerFile(properties: MessengerFileRequest.Builder.() -> Unit): MessengerFileRequest =
+ MessengerFileRequest.builder().apply(properties).build()
-fun viberText(init: ViberTextRequest.Builder.() -> Unit): ViberTextRequest =
- ViberTextRequest.builder().apply(init).build()
+/**
+ * Creates a Viber text message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [ViberTextRequest] object with the specified properties.
+ */
+fun viberText(properties: ViberTextRequest.Builder.() -> Unit): ViberTextRequest =
+ ViberTextRequest.builder().apply(properties).build()
-fun viberImage(init: ViberImageRequest.Builder.() -> Unit): ViberImageRequest =
- ViberImageRequest.builder().apply(init).build()
+/**
+ * Creates a Viber image message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [ViberImageRequest] object with the specified properties.
+ */
+fun viberImage(properties: ViberImageRequest.Builder.() -> Unit): ViberImageRequest =
+ ViberImageRequest.builder().apply(properties).build()
-fun viberVideo(init: ViberVideoRequest.Builder.() -> Unit): ViberVideoRequest =
- ViberVideoRequest.builder().apply(init).build()
+/**
+ * Creates a Viber video message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [ViberVideoRequest] object with the specified properties.
+ */
+fun viberVideo(properties: ViberVideoRequest.Builder.() -> Unit): ViberVideoRequest =
+ ViberVideoRequest.builder().apply(properties).build()
-fun viberFile(init: ViberFileRequest.Builder.() -> Unit): ViberFileRequest =
- ViberFileRequest.builder().apply(init).build()
\ No newline at end of file
+/**
+ * Creates a Viber file message.
+ *
+ * @param properties A lambda function for setting the message's parameters.
+ *
+ * @return A [ViberFileRequest] object with the specified properties.
+ */
+fun viberFile(properties: ViberFileRequest.Builder.() -> Unit): ViberFileRequest =
+ ViberFileRequest.builder().apply(properties).build()
diff --git a/src/main/kotlin/com/vonage/client/kt/NumberInsight.kt b/src/main/kotlin/com/vonage/client/kt/NumberInsight.kt
index f3ccbfe..adc8a14 100644
--- a/src/main/kotlin/com/vonage/client/kt/NumberInsight.kt
+++ b/src/main/kotlin/com/vonage/client/kt/NumberInsight.kt
@@ -17,22 +17,77 @@ package com.vonage.client.kt
import com.vonage.client.insight.*
+/**
+ * Implementation of the [Number Insight API](https://developer.vonage.com/en/api/number-insight).
+ *
+ * *Authentication method:* API key & secret.
+ */
class NumberInsight internal constructor(private val client: InsightClient) {
+ /**
+ * Obtain basic insight about a number.
+ *
+ * @param number The phone number to look up in E.164 format.
+ *
+ * @param countryCode (OPTIONAL) The two-character country code in ISO 3166-1 alpha-2 format.
+ *
+ * @return Basic details about the number and insight metadata.
+ */
fun basic(number: String, countryCode: String? = null): BasicInsightResponse =
client.getBasicNumberInsight(number, countryCode)
+ /**
+ * Obtain standard insight about a number.
+ *
+ * @param number The phone number to look up in E.164 format.
+ *
+ * @param countryCode (OPTIONAL) The two-character country code in ISO 3166-1 alpha-2 format.
+ *
+ * @param cnam (OPTIONAL) Whether the name of the person who owns the phone number should be looked up
+ * and returned in the response. Set to true to receive phone number owner name in the response. This
+ * feature is available for US numbers only and incurs an additional charge.
+ *
+ * @return Standard details about the number and insight metadata.
+ */
fun standard(number: String, countryCode: String? = null, cnam: Boolean? = null): StandardInsightResponse =
client.getStandardNumberInsight(StandardInsightRequest.builder()
.number(number).country(countryCode).cnam(cnam).build()
)
+ /**
+ * Obtain advanced insight about a number synchronously. This is not recommended due to potential timeouts.
+ *
+ * @param number The phone number to look up in E.164 format.
+ *
+ * @param countryCode (OPTIONAL) The two-character country code in ISO 3166-1 alpha-2 format.
+ *
+ * @param cnam (OPTIONAL) Whether the name of the person who owns the phone number should be looked up
+ * and returned in the response. Set to true to receive phone number owner name in the response. This
+ * feature is available for US numbers only and incurs an additional charge.
+ *
+ * @param realTimeData (OPTIONAL) Whether to receive real-time data back in the response.
+ *
+ * @return Advanced details about the number and insight metadata.
+ */
fun advanced(number: String, countryCode: String? = null, cnam: Boolean = false,
realTimeData: Boolean = false): AdvancedInsightResponse =
client.getAdvancedNumberInsight(AdvancedInsightRequest.builder().async(false)
.number(number).country(countryCode).cnam(cnam).realTimeData(realTimeData).build()
)
+ /**
+ * Obtain advanced insight about a number asynchronously. This is recommended to avoid timeouts.
+ *
+ * @param number The phone number to look up in E.164 format.
+ *
+ * @param callbackUrl The URL to which the response will be sent.
+ *
+ * @param countryCode (OPTIONAL) The two-character country code in ISO 3166-1 alpha-2 format.
+ *
+ * @param cnam (OPTIONAL) Whether the name of the person who owns the phone number should be looked up
+ * and returned in the response. Set to true to receive phone number owner name in the response. This
+ * feature is available for US numbers only and incurs an additional charge.
+ */
fun advancedAsync(number: String, callbackUrl: String, countryCode: String? = null, cnam: Boolean = false) {
client.getAdvancedNumberInsight(
AdvancedInsightRequest.builder().async(true)
diff --git a/src/main/kotlin/com/vonage/client/kt/NumberVerification.kt b/src/main/kotlin/com/vonage/client/kt/NumberVerification.kt
index f79413b..b2635ff 100644
--- a/src/main/kotlin/com/vonage/client/kt/NumberVerification.kt
+++ b/src/main/kotlin/com/vonage/client/kt/NumberVerification.kt
@@ -16,16 +16,64 @@
package com.vonage.client.kt
import com.vonage.client.camara.numberverification.*
+import com.vonage.client.camara.CamaraResponseException
+import com.vonage.client.auth.camara.NetworkAuthResponseException
import java.net.URI
+/**
+ * Implementation of the [Number Verification API](https://developer.vonage.com/en/api/camara/number-verification).
+ *
+ * *Authentication method:* JWT.
+ */
class NumberVerification internal constructor(private val client: NumberVerificationClient) {
private var redirectUri: URI? = null
+ /**
+ * Sets up the client for verifying the given phone number. This method will cache the provided redirect URL
+ * for verification when calling #verifyNumberWithCode(String, String). The intended usage is to call this method first,
+ * and follow the returned URL on the target device which the phone number is supposed to be associated with.
+ * When the URL is followed, it will trigger an inbound request to the `redirectUrl` with two query parameters:
+ * `CODE` and `STATE` (if provided as a parameter to this method). The code should then be extracted from the
+ * query parameters and passed to the `verifyNumberWithCode` method.
+ *
+ * @param phoneNumber The MSISDN to verify.
+ *
+ * @param redirectUrl Redirect URL, as set in your Vonage application for Network APIs.
+ *
+ * @param state An optional string for identifying the request.
+ * For simplicity, this could be set to the same value as `phoneNumber`, or it may be null.
+ *
+ * @return A link with appropriate parameters which should be followed on the end user's device. The link should
+ * be followed when using the SIM card associated with the provided phone number. Therefore, on the target device,
+ * Wi-Fi should be disabled when doing this, otherwise the result of `verifyNumber(String)` will be false.
+ */
fun createVerificationUrl(phoneNumber: String, redirectUrl: String, state: String? = null): URI {
redirectUri = URI.create(redirectUrl)
return client.initiateVerification(phoneNumber, redirectUri, state)
}
+ /**
+ * Verifies the given phone number with the provided code. This method should be called after the user has followed
+ * the URL returned by `createVerificationUrl(String, String, String)`. The code should be extracted from the
+ * query parameters of the URL and passed to this method. If `createVerificationUrl` has not been called first,
+ * then the `redirectUrl` parameter should be provided here. If it has, then it can be omitted.
+ *
+ * @param phoneNumber The MSISDN to verify.
+ *
+ * @param code The code extracted from the query parameters of the URL returned by `createVerificationUrl(String, String, String)`.
+ *
+ * @param redirectUrl Redirect URL, as set in your Vonage application for Network APIs.
+ * This defaults to the value passed in the `createVerificationUrl` method, but can be overridden here.
+ *
+ * @return `true` if the device that followed the link was using the SIM card associated with the phone number
+ * provided in `createVerificationUrl`, `false` otherwise (e.g. it was unknown, the link was not followed,
+ * the device that followed the link didn't use the SIM card with that phone number when doing so).
+ *
+ * @throws NetworkAuthResponseException If there was an error exchanging the code for an access token when
+ * using the Vonage Network Auth API.
+ *
+ * @throws CamaraResponseException If there was an error in communicating with the Number Verification API.
+ */
fun verifyNumberWithCode(phoneNumber: String, code: String, redirectUrl: String? = null): Boolean {
if (redirectUrl != null) redirectUri = URI.create(redirectUrl)
return client.verifyNumber(phoneNumber, redirectUri, code)
diff --git a/src/main/kotlin/com/vonage/client/kt/Numbers.kt b/src/main/kotlin/com/vonage/client/kt/Numbers.kt
index dff8e26..88dbb51 100644
--- a/src/main/kotlin/com/vonage/client/kt/Numbers.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Numbers.kt
@@ -17,25 +17,85 @@ package com.vonage.client.kt
import com.vonage.client.numbers.*
+/**
+ * Implementation of the [Numbers API](https://developer.vonage.com/en/api/numbers).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Numbers internal constructor(private val client: NumbersClient) {
+ /**
+ * Call this method to work with an existing number.
+ *
+ * @param countryCode The two-character country code of the number.
+ * @param msisdn The phone number in E.164 format.
+ *
+ * @return An [ExistingNumber] object with methods to interact with the number.
+ */
fun number(countryCode: String, msisdn: String) = ExistingNumber(countryCode, msisdn)
+ /**
+ * Class for working with an existing number.
+ *
+ * @property countryCode The two-character country code of the number.
+ * @property msisdn The phone number in E.164 format.
+ */
inner class ExistingNumber internal constructor(val countryCode: String, val msisdn: String) {
+ /**
+ * Purchase the number.
+ *
+ * @param targetApiKey (OPTIONAL) The API key of the subaccount to assign the number to.
+ * If unspecified (the default), the action will be performed on the main account.
+ *
+ * @throws [NumbersResponseException] If the number could not be purchased.
+ */
fun buy(targetApiKey: String? = null): Unit =
client.buyNumber(countryCode, msisdn, targetApiKey)
+ /**
+ * Cancel the number.
+ *
+ * @param targetApiKey (OPTIONAL) The API key of the subaccount to cancel the number on.
+ * If unspecified (the default), the action will be performed on the main account.
+ *
+ * @throws [NumbersResponseException] If the number could not be cancelled.
+ */
fun cancel(targetApiKey: String? = null): Unit =
client.cancelNumber(countryCode, msisdn, targetApiKey)
+ /**
+ * Update the number's assignment on your account.
+ *
+ * @param properties A lambda function for specifying the properties to update.
+ *
+ * @throws [NumbersResponseException] If the number could not be updated.
+ */
fun update(properties: UpdateNumberRequest.Builder.() -> Unit): Unit =
client.updateNumber(UpdateNumberRequest.builder(msisdn, countryCode).apply(properties).build())
}
+ /**
+ * List numbers owned by the account.
+ *
+ * @param filter (OPTIONAL) A lambda function for specifying the filter properties.
+ *
+ * @return A [ListNumbersResponse] object containing the list of owned numbers.
+ *
+ * @throws [NumbersResponseException] If the list could not be retrieved.
+ */
fun listOwned(filter: ListNumbersFilter.Builder.() -> Unit = {}): ListNumbersResponse =
client.listNumbers(ListNumbersFilter.builder().apply(filter).build())
+ /**
+ * Search for numbers that are available to purchase.
+ *
+ * @param filter A lambda function for specifying the search filter properties.
+ *
+ * @return A [SearchNumbersResponse] object containing the list of available numbers.
+ *
+ * @throws [NumbersResponseException] If the search request could not be completed.
+ */
fun searchAvailable(filter: SearchNumbersFilter.Builder.() -> Unit): SearchNumbersResponse =
client.searchNumbers(SearchNumbersFilter.builder().apply(filter).build())
}
diff --git a/src/main/kotlin/com/vonage/client/kt/Redact.kt b/src/main/kotlin/com/vonage/client/kt/Redact.kt
index 4b88237..864e164 100644
--- a/src/main/kotlin/com/vonage/client/kt/Redact.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Redact.kt
@@ -17,20 +17,53 @@ package com.vonage.client.kt
import com.vonage.client.redact.*
+/**
+ * Implementation of the [Redact API](https://developer.vonage.com/en/api/redact).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Redact internal constructor(private val client: RedactClient) {
+ /**
+ * Redact an SMS sent using the SMS API.
+ *
+ * @param messageId ID of the message to redact.
+ * @param direction Direction of the message to redact; either `INBOUND` or `OUTBOUND`.
+ */
fun redactSms(messageId: String, direction: RedactRequest.Type = RedactRequest.Type.OUTBOUND): Unit =
client.redactTransaction(messageId, RedactRequest.Product.SMS, direction)
+ /**
+ * Redact a message sent using the Messages API.
+ *
+ * @param messageId UUID of the message to redact.
+ * @param direction Direction of the message to redact; either `INBOUND` or `OUTBOUND`.
+ */
fun redactMessage(messageId: String, direction: RedactRequest.Type = RedactRequest.Type.OUTBOUND): Unit =
client.redactTransaction(messageId, RedactRequest.Product.MESSAGES, direction)
+ /**
+ * Redact a call made using the Voice API.
+ *
+ * @param callId UUID of the call to redact.
+ * @param direction Direction of the call to redact; either `INBOUND` or `OUTBOUND`.
+ */
fun redactCall(callId: String, direction: RedactRequest.Type = RedactRequest.Type.OUTBOUND): Unit =
client.redactTransaction(callId, RedactRequest.Product.VOICE, direction)
+ /**
+ * Redact a number insight request made using the Number Insight API.
+ *
+ * @param requestId ID of the insight request to redact.
+ */
fun redactInsight(requestId: String): Unit =
client.redactTransaction(requestId, RedactRequest.Product.NUMBER_INSIGHTS)
+ /**
+ * Redact a verification request made using the Verify API.
+ *
+ * @param requestId UUID of the verification request to redact.
+ */
fun redactVerification(requestId: String): Unit =
client.redactTransaction(requestId, RedactRequest.Product.VERIFY)
}
diff --git a/src/main/kotlin/com/vonage/client/kt/SimSwap.kt b/src/main/kotlin/com/vonage/client/kt/SimSwap.kt
index 3a50b79..926f0a2 100644
--- a/src/main/kotlin/com/vonage/client/kt/SimSwap.kt
+++ b/src/main/kotlin/com/vonage/client/kt/SimSwap.kt
@@ -15,14 +15,60 @@
*/
package com.vonage.client.kt
+import com.vonage.client.auth.camara.NetworkAuthResponseException
import com.vonage.client.camara.simswap.*
+import com.vonage.client.camara.CamaraResponseException
import java.time.Instant
+/**
+ * Implementation of the [Sim Swap API](https://developer.vonage.com/en/api/camara/sim-swap).
+ *
+ * *Authentication method:* JWT.
+ */
class SimSwap internal constructor(private val client: SimSwapClient) {
+ /**
+ * Check if a SIM swap has been performed during the specified past period for the given phone number.
+ *
+ * @param phoneNumber Subscriber number in E.164 format (starting with country code). Optionally prefixed with '+'.
+ *
+ * @param maxAgeHours Period in hours to be checked for SIM swap. Must be between 1 and 2400.
+ * Default is 10 days (240 hours).
+ *
+ * @return `true` if the SIM card has been swapped during the period within the provided age.
+ *
+ * @throws NetworkAuthResponseException If an error was encountered in the OAuth 2 flow when
+ * using the Vonage Network Auth API to obtain the access token.
+ *
+ * @throws CamaraResponseException If the request was unsuccessful. This could be for the following reasons:
+ * - **400**: Invalid request arguments.
+ * - **401**: Request not authenticated due to missing, invalid, or expired credentials.
+ * - **403**: Client does not have sufficient permissions to perform this action.
+ * - **404**: SIM Swap can't be checked because the phone number is unknown.
+ * - **409**: Another request is created for the same MSISDN.
+ * - **502**: Bad gateway.
+ */
fun checkSimSwap(phoneNumber: String, maxAgeHours: Int = 240): Boolean =
client.checkSimSwap(phoneNumber, maxAgeHours)
+ /**
+ * Get timestamp of last MSISDN to IMSI pairing change for a mobile user account.
+ *
+ * @param phoneNumber Subscriber number in E.164 format (starting with country code). Optionally prefixed with '+'.
+ *
+ * @return Time of the latest SIM swap performed as an Instant, or `null` if unknown / not applicable.
+ *
+ * @throws NetworkAuthResponseException If an error was encountered in the OAuth 2 flow when
+ * using the Vonage Network Auth API to obtain the access token.
+ *
+ * @throws CamaraResponseException If the request was unsuccessful. This could be for the following reasons:
+ * - **400**: Invalid request arguments.
+ * - **401**: Request not authenticated due to missing, invalid, or expired credentials.
+ * - **403**: Client does not have sufficient permissions to perform this action.
+ * - **404**: SIM Swap can't be checked because the phone number is unknown.
+ * - **409**: Another request is created for the same MSISDN.
+ * - **502**: Bad gateway.
+ */
fun retrieveSimSwapDate(phoneNumber: String): Instant? =
client.retrieveSimSwapDate(phoneNumber)
}
diff --git a/src/main/kotlin/com/vonage/client/kt/Sms.kt b/src/main/kotlin/com/vonage/client/kt/Sms.kt
index 803fbbf..31a03c7 100644
--- a/src/main/kotlin/com/vonage/client/kt/Sms.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Sms.kt
@@ -18,6 +18,11 @@ package com.vonage.client.kt
import com.vonage.client.sms.*
import com.vonage.client.sms.messages.*
+/**
+ * Implementation of the [SMS API](https://developer.vonage.com/en/api/sms).
+ *
+ * *Authentication method:* API key & secret or signature secret.
+ */
class Sms internal constructor(private val client: SmsClient) {
private fun send(msgObj: Message, statusReport: Boolean?, ttl: Int?,
@@ -34,6 +39,37 @@ class Sms internal constructor(private val client: SmsClient) {
return client.submitMessage(msgObj).messages
}
+ /**
+ * Send a text message.
+ *
+ * @param from The sender ID. This can be a phone number or a short alphanumeric string.
+ *
+ * @param to The recipient phone number in E.164 format.
+ *
+ * @param message Text of the message to send.
+ *
+ * @param unicode `true` if the message should be sent as Unicode, `false` for GSM (the default).
+ *
+ * @param statusReport (OPTIONAL) Whether to include a Delivery Receipt.
+ *
+ * @param ttl (OPTIONAL) The duration in milliseconds the delivery of an SMS will be attempted. By default, Vonage
+ * attempts delivery for 72 hours, however the maximum effective value depends on the operator and is typically
+ * 24 - 48 hours. We recommend this value should be kept at its default or at least 30 minutes.
+ *
+ * @param messageClass (OPTIONAL) Data Coding Scheme value of the message.
+ *
+ * @param clientRef (OPTIONAL) You can optionally include your own reference of up to 100 characters.
+ *
+ * @param contentId (OPTIONAL) This is to satisfy regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param entityId (OPTIONAL) This is to satisfy regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param callbackUrl (OPTIONAL) The URL to which delivery receipts for this message are sent.
+ *
+ * @return A list of [SmsSubmissionResponseMessage] objects, one for each message part sent.
+ * Multiple messages are sent if the text was too long. For convenience, you can use the
+ * [wasSuccessfullySent] method to check if all messages were sent successfully.
+ */
fun sendText(from: String, to: String, message: String, unicode: Boolean = false,
statusReport: Boolean? = null, ttl: Int? = null,
messageClass: Message.MessageClass? = null, clientRef: String? = null,
@@ -44,6 +80,39 @@ class Sms internal constructor(private val client: SmsClient) {
statusReport, ttl, messageClass, clientRef, contentId, entityId, callbackUrl
)
+ /**
+ * Send a binary (hex) message.
+ *
+ * @param from The sender ID. This can be a phone number or a short alphanumeric string.
+ *
+ * @param to The recipient phone number in E.164 format.
+ *
+ * @param body Hex encoded binary data message to send.
+ *
+ * @param udh Hex encoded User Data Header.
+ *
+ * @param protocolId (OPTIONAL) The value of the protocol identifier to use. Should be aligned with `udh`.
+ *
+ * @param statusReport (OPTIONAL) Whether to include a Delivery Receipt.
+ *
+ * @param ttl (OPTIONAL) The duration in milliseconds the delivery of an SMS will be attempted. By default, Vonage
+ * attempts delivery for 72 hours, however the maximum effective value depends on the operator and is typically
+ * 24 - 48 hours. We recommend this value should be kept at its default or at least 30 minutes.
+ *
+ * @param messageClass (OPTIONAL) Data Coding Scheme value of the message.
+ *
+ * @param clientRef (OPTIONAL) You can optionally include your own reference of up to 100 characters.
+ *
+ * @param contentId (OPTIONAL) This is to satisfy regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param entityId (OPTIONAL) This is to satisfy regulatory requirements when sending an SMS to specific countries.
+ *
+ * @param callbackUrl (OPTIONAL) The URL to which delivery receipts for this message are sent.
+ *
+ * @return A list of [SmsSubmissionResponseMessage] objects, one for each message part sent.
+ * Multiple messages are sent if the body was too long. For convenience, you can use the
+ * [wasSuccessfullySent] method to check if all messages were sent successfully.
+ */
fun sendBinary(from: String, to: String, body: ByteArray, udh: ByteArray,
protocolId: Int? = null, statusReport: Boolean? = null, ttl: Int? = null,
messageClass: Message.MessageClass? = null, clientRef: String? = null,
@@ -54,6 +123,13 @@ class Sms internal constructor(private val client: SmsClient) {
return send(msgObj, statusReport, ttl, messageClass, clientRef, contentId, entityId, callbackUrl)
}
+ /**
+ * Convenience method to check if all messages were sent successfully.
+ *
+ * @param response The list of [SmsSubmissionResponseMessage] objects returned when sending a message.
+ *
+ * @return `true` if all messages responses have a status of [MessageStatus.OK], `false` otherwise.
+ */
fun wasSuccessfullySent(response: List): Boolean =
response.all { ssrm -> ssrm.status == MessageStatus.OK }
}
diff --git a/src/main/kotlin/com/vonage/client/kt/Subaccounts.kt b/src/main/kotlin/com/vonage/client/kt/Subaccounts.kt
index fda755c..5fe8b89 100644
--- a/src/main/kotlin/com/vonage/client/kt/Subaccounts.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Subaccounts.kt
@@ -19,10 +19,44 @@ import com.vonage.client.subaccounts.*
import com.vonage.client.subaccounts.Account
import java.time.Instant
+/**
+ * Implementation of the [Subaccounts API](https://developer.vonage.com/en/api/subaccounts).
+ *
+ * *Authentication method:* API key & secret.
+ */
class Subaccounts internal constructor(private val client: SubaccountsClient) {
+ /**
+ * Retrieve all subaccounts owned by the primary account.
+ *
+ * @return The primary account details and list of subaccounts associated with it.
+ *
+ * @throws [SubaccountsResponseException] If the subaccounts could not be retrieved.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ */
fun listSubaccounts(): ListSubaccountsResponse = client.listSubaccounts()
+ /**
+ * Create a new subaccount.
+ *
+ * @param name Name of the subaccount.
+ * @param secret (OPTIONAL) Secret for the subaccount. If not provided, one will be generated.
+ * @param usePrimaryAccountBalance (OPTIONAL) Whether to use the primary account's balance.
+ *
+ * @return The newly created subaccount details.
+ *
+ * @throws [SubaccountsResponseException] If the subaccount could not be created.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **422**: Validation error.
+ */
fun createSubaccount(name: String, secret: String? = null, usePrimaryAccountBalance: Boolean? = null): Account {
val builder = CreateSubaccountRequest.builder().name(name).secret(secret)
if (usePrimaryAccountBalance != null) {
@@ -31,15 +65,71 @@ class Subaccounts internal constructor(private val client: SubaccountsClient) {
return client.createSubaccount(builder.build())
}
+ /**
+ * Call this method to work with an existing subaccount.
+ *
+ * @param subaccountKey API key of the subaccount to work with.
+ *
+ * @return An [ExistingSubaccount] object with methods to interact with the subaccount.
+ */
fun subaccount(subaccountKey: String): ExistingSubaccount = ExistingSubaccount(subaccountKey)
+ /**
+ * Class for working with an existing subaccount.
+ *
+ * @property id The subaccount's API key.
+ */
inner class ExistingSubaccount internal constructor(key: String): ExistingResource(key) {
+ /**
+ * Retrieves the subaccount details.
+ *
+ * @return The subaccount details.
+ *
+ * @throws [SubaccountsResponseException] If the subaccount details could not be retrieved.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ */
fun get(): Account = client.getSubaccount(id)
+ /**
+ * Suspends or unsuspends the subaccount. Note that suspension will not delete it;
+ * only prevent it from being used to make API calls.
+ *
+ * @param suspend Set to `true` to suspend the subaccount or `false` to restore it.
+ *
+ * @return The updated subaccount details.
+ *
+ * @throws [SubaccountsResponseException] If the subaccount could not be suspended.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **422**: Validation error.
+ */
fun suspended(suspend: Boolean): Account =
client.updateSubaccount(UpdateSubaccountRequest.builder(id).suspended(suspend).build())
+ /**
+ * Updates the subaccount details. At least one field must be provided.
+ *
+ * @param name (OPTIONAL) New name for the subaccount.
+ * @param usePrimaryAccountBalance (OPTIONAL) Whether to use the primary account's balance.
+ *
+ * @return The updated subaccount details.
+ *
+ * @throws [SubaccountsResponseException] If the subaccount details could not be updated.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **422**: Validation error.
+ */
fun update(name: String? = null, usePrimaryAccountBalance: Boolean? = null): Account {
val builder = UpdateSubaccountRequest.builder(id).name(name)
if (usePrimaryAccountBalance != null) {
@@ -49,24 +139,111 @@ class Subaccounts internal constructor(private val client: SubaccountsClient) {
}
}
+ /**
+ * Retrieve credit limit transfers that have occurred for the primary account within a specified time period.
+ *
+ * @param startDate (OPTIONAL) The start of the time period to retrieve transfers for.
+ * @param endDate (OPTIONAL) The end of the time period to retrieve transfers for.
+ * @param subaccount (OPTIONAL) The subaccount API key to retrieve transfers for.
+ *
+ * @return The list of credit transfers that have occurred which match the filter criteria.
+ *
+ * @throws [SubaccountsResponseException] If the transfers could not be retrieved.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ */
fun listCreditTransfers(startDate: Instant? = null, endDate: Instant? = null,
subaccount: String? = null): List =
client.listCreditTransfers(ListTransfersFilter.builder()
.startDate(startDate).endDate(endDate).subaccount(subaccount).build()
)
+ /**
+ * Retrieve balance transfers that have occurred for the primary account within a specified time period.
+ *
+ * @param startDate (OPTIONAL) The start of the time period to retrieve transfers for.
+ * @param endDate (OPTIONAL) The end of the time period to retrieve transfers for.
+ * @param subaccount (OPTIONAL) The subaccount API key to retrieve transfers for.
+ *
+ * @return The list of balance transfers that have occurred which match the filter criteria.
+ *
+ * @throws [SubaccountsResponseException] If the transfers could not be retrieved.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ */
fun listBalanceTransfers(startDate: Instant? = null, endDate: Instant? = null,
subaccount: String? = null): List =
client.listBalanceTransfers(ListTransfersFilter.builder()
.startDate(startDate).endDate(endDate).subaccount(subaccount).build()
)
+ /**
+ * Transfer credit limit between (sub)accounts.
+ *
+ * @param from The API key of the account to transfer credit from.
+ * @param to The API key of the subaccount to transfer credit to.
+ * @param amount The monetary amount of credit to transfer in Euros.
+ * @param ref (OPTIONAL) A reference to associate with the transfer.
+ *
+ * @return Details of the credit transfer.
+ *
+ * @throws [SubaccountsResponseException] If the credit could not be transferred.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **422**: Validation error.
+ */
fun transferCredit(from: String, to: String, amount: Double, ref: String? = null): MoneyTransfer =
client.transferCredit(MoneyTransfer.builder().from(from).to(to).amount(amount).reference(ref).build())
+ /**
+ * Transfer monetary balance between (sub)accounts.
+ *
+ * @param from The API key of the account to transfer money from.
+ * @param to The API key of the subaccount to transfer money to.
+ * @param amount The monetary amount to transfer in Euros.
+ * @param ref (OPTIONAL) A reference to associate with the transfer.
+ *
+ * @return Details of the balance transfer.
+ *
+ * @throws [SubaccountsResponseException] If the balance could not be transferred.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **422**: Validation error.
+ */
fun transferBalance(from: String, to: String, amount: Double, ref: String? = null): MoneyTransfer =
client.transferBalance(MoneyTransfer.builder().from(from).to(to).amount(amount).reference(ref).build())
+ /**
+ * Transfer a number from one account to another.
+ *
+ * @param from API key of the account to transfer the number from.
+ * @param to API key of the account to transfer the number to.
+ * @param number MSISDN of the number to transfer, in E.164 format.
+ * @param country Country the number is registered in, in ISO 3166-1 alpha-2 format.
+ *
+ * @return Details of the number transfer.
+ *
+ * @throws [SubaccountsResponseException] If the number could not be transferred.
+ * This may be for the following reasons:
+ *
+ * - **401**: Missing or invalid credentials.
+ * - **403**: Action is forbidden.
+ * - **404**: The account ID provided does not exist in the system, or you do not have access.
+ * - **409**: The number is already associated with the account you are trying to transfer it to.
+ * - **422**: Validation error.
+ */
fun transferNumber(from: String, to: String, number: String, country: String): NumberTransfer =
client.transferNumber(NumberTransfer.builder().from(from).to(to).number(number).country(country).build())
diff --git a/src/main/kotlin/com/vonage/client/kt/Users.kt b/src/main/kotlin/com/vonage/client/kt/Users.kt
index ef12970..adc5772 100644
--- a/src/main/kotlin/com/vonage/client/kt/Users.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Users.kt
@@ -17,24 +17,88 @@ package com.vonage.client.kt
import com.vonage.client.users.*
+/**
+ * Implementation of the [Users API](https://developer.vonage.com/en/api/application.v2#User).
+ *
+ * *Authentication method:* JWT.
+ */
class Users internal constructor(private val client: UsersClient) {
+ /**
+ * Call this method to work with an existing user.
+ *
+ * @param userId The UUID of the user to work with.
+ *
+ * @return An [ExistingUser] object with methods to interact with the user.
+ */
fun user(userId: String): ExistingUser = ExistingUser(userId)
+ /**
+ * Call this method to work with an existing user.
+ *
+ * @param user The user object to work with.
+ *
+ * @return An [ExistingUser] object with methods to interact with the user.
+ */
fun user(user: BaseUser): ExistingUser = ExistingUser(user.id)
+ /**
+ * Class for working with an existing user.
+ *
+ * @property id The user ID.
+ */
inner class ExistingUser internal constructor(id: String): ExistingResource(id) {
+
+ /**
+ * Retrieves the user details.
+ *
+ * @return The user details.
+ *
+ * @throws [UsersResponseException] If the user details cannot be retrieved.
+ */
fun get(): User = client.getUser(id)
+ /**
+ * Updates the user details.
+ *
+ * @param properties A lambda function to set the properties of the user.
+ *
+ * @return The updated user details.
+ *
+ * @throws [UsersResponseException] If the user details cannot be updated.
+ */
fun update(properties: User.Builder.() -> Unit): User =
client.updateUser(id, User.builder().apply(properties).build())
+ /**
+ * Deletes the user.
+ *
+ * @throws [UsersResponseException] If the user cannot be deleted.
+ */
fun delete(): Unit = client.deleteUser(id)
}
+ /**
+ * Creates a new user.
+ *
+ * @param properties A lambda function to set the properties of the user.
+ *
+ * @return The created user details.
+ *
+ * @throws [UsersResponseException] If the user cannot be created.
+ */
fun create(properties: User.Builder.() -> Unit): User =
client.createUser(User.builder().apply(properties).build())
+ /**
+ * List users in your application. The response will only contain a subset of the user's properties.
+ * Call the [user] method to get the full details of a particular user.
+ *
+ * @param filter (OPTIONAL) A lambda function to set the request properties for pagination.
+ * If omitted, the maximum number of users is returned; which is the first 100.
+ *
+ * @return The HAL response page with a list of basic user details (ID and name).
+ */
fun list(filter: (ListUsersRequest.Builder.() -> Unit)? = null): ListUsersResponse {
val request = ListUsersRequest.builder()
if (filter == null) {
diff --git a/src/main/kotlin/com/vonage/client/kt/Verify.kt b/src/main/kotlin/com/vonage/client/kt/Verify.kt
index 5350a3c..1abf91b 100644
--- a/src/main/kotlin/com/vonage/client/kt/Verify.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Verify.kt
@@ -18,24 +18,116 @@ package com.vonage.client.kt
import com.vonage.client.verify2.*
import java.util.*
+/**
+ * Implementation of the [Verify v2 API](https://developer.vonage.com/en/api/verify.v2).
+ *
+ * *Authentication method:* JWT (recommended) and API key & secret (limited functionality).
+ */
class Verify(private val client: Verify2Client) {
- fun sendVerification(
- brand: String = "Vonage",
- init: VerificationRequest.Builder.() -> Unit
- ): VerificationResponse = client.sendVerification(
- VerificationRequest.builder().brand(brand).apply(init).build()
- )
+ /**
+ * Initiate a verification request.
+ *
+ * @param brand The name of the company or app sending the verification request.
+ * This is what the user will see the sender as on their device.
+ *
+ * @param properties A lambda function for specifying the verification request properties.
+ * You must set at least one workflow and a recipient. See [VerificationRequest.Builder] methods for details.
+ *
+ * @return A [VerificationResponse] object containing the request ID.
+ *
+ * @throws [VerifyResponseException] If the request could not be sent. This may be for the following reasons:
+ * - **401**: Invalid credentials.
+ * - **402**: Account balance too low.
+ * - **409**: Verification already in progress. You must wait for it to be completed or aborted.
+ * - **422**: Invalid parameters. Check the error message for details.
+ * - **429**: Rate limit exceeded.
+ * - **500**: Internal server error.
+ */
+ fun sendVerification(brand: String = "Vonage",
+ properties: VerificationRequest.Builder.() -> Unit): VerificationResponse =
+ client.sendVerification(VerificationRequest.builder().brand(brand).apply(properties).build())
+
+
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param requestId UUID of the verification request to work with,
+ * as obtained from [VerificationResponse.getRequestId].
+ *
+ * @return An [ExistingRequest] object with methods to interact with the request.
+ */
+ fun request(requestId: UUID): ExistingRequest = ExistingRequest(requestId)
+
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param requestId UUID of the verification request to work with.
+ */
+ fun request(requestId: String): ExistingRequest = request(UUID.fromString(requestId))
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param uuid UUID of the verification request to work with, as obtained from [VerificationResponse.getRequestId].
+ */
inner class ExistingRequest internal constructor(private val uuid: UUID): ExistingResource(uuid.toString()) {
+ /**
+ * Cancels the verification request.
+ *
+ * @throws [VerifyResponseException] If the request could not be cancelled.
+ * This may be for the following reasons:
+ * - **401**: Invalid credentials.
+ * - **402**: Account balance too low.
+ * - **404**: Verification request not found.
+ * - **500**: Internal server error.
+ */
fun cancel(): Unit = client.cancelVerification(uuid)
+ /**
+ * Advanced the verification request to the next workflow.
+ *
+ * @throws [VerifyResponseException] If the request could not be moved to the next worklow.
+ * This could be for the following reasons:
+ * - **401**: Invalid credentials.
+ * - **402**: Account balance too low.
+ * - **404**: Verification request not found.
+ * - **409**: Verification already completed or cancelled.
+ * - **500**: Internal server error.
+ */
fun nextWorkflow(): Unit = client.nextWorkflow(uuid)
+ /**
+ * Checks the verification code. If successful (the code matches), this method will return normally.
+ * If the code does not match, an exception will be thrown.
+ *
+ * @param code The verification code to check, as entered by the user.
+ *
+ * @return A [VerifyCodeResponse] object containing the verification status.
+ *
+ * @throws [VerifyResponseException] If the code could not be checked. This could be for the following reasons:
+ * - **400**: Invalid code.
+ * - **401**: Invalid credentials.
+ * - **404**: Request was not found, or it has been verified already.
+ * - **409**: The current workflow does not support a code.
+ * - **410**: An incorrect code has been provided too many times. Workflow terminated.
+ * - **500**: Internal server error.
+ *
+ * @see isValidVerificationCode For a Boolean-returning version of this method.
+ */
fun checkVerificationCode(code: String): VerifyCodeResponse =
client.checkVerificationCode(uuid, code)
+ /**
+ * Checks the verification code. If successful (the code matches), this method will return `true`.
+ * If the code does not match, the method will return `false. For any other case, an exception will be thrown.
+ *
+ * @param code The verification code to check, as entered by the user.
+ *
+ * @return `true` if the code is valid, `false` if it is not.
+ * @see checkVerificationCode For the original implementation which this method delegates to.
+ */
fun isValidVerificationCode(code: String): Boolean {
try {
checkVerificationCode(code)
@@ -49,12 +141,21 @@ class Verify(private val client: Verify2Client) {
}
}
}
-
- fun request(requestId: UUID): ExistingRequest = ExistingRequest(requestId)
-
- fun request(requestId: String): ExistingRequest = request(UUID.fromString(requestId))
}
+/**
+ * Adds a Silent Authentication workflow to the verification request. Note that this must be the first workflow.
+ *
+ * @param number The recipient's phone number in E.164 format.
+ *
+ * @param sandbox (OPTIONAL) Whether to use the Vonage Sandbox (for testing purposes). Default is `false`.
+ *
+ * @param redirectUrl (OPTIONAL) Final redirect added at the end of the `check_url` request/response lifecycle.
+ * Will contain the `request_id` and `code` as a URL fragment. This can be used to redirect the user back
+ * to your application after following the verification link on their device.
+ *
+ * @return The verification request builder.
+ */
fun VerificationRequest.Builder.silentAuth(
number: String, sandbox: Boolean? = null, redirectUrl: String? = null): VerificationRequest.Builder {
val builder = SilentAuthWorkflow.builder(number)
@@ -63,18 +164,60 @@ fun VerificationRequest.Builder.silentAuth(
return addWorkflow(builder.build())
}
+/**
+ * Adds an SMS workflow to the verification request.
+ *
+ * @param number The recipient's phone number in E.164 format.
+ *
+ * @param properties (OPTIONAL) A lambda function for specifying additional SMS workflow parameters.
+ *
+ * @return The verification request builder.
+ */
fun VerificationRequest.Builder.sms(
- number: String, init: SmsWorkflow.Builder.() -> Unit = {}): VerificationRequest.Builder =
- addWorkflow(SmsWorkflow.builder(number).apply(init).build())
+ number: String, properties: SmsWorkflow.Builder.() -> Unit = {}): VerificationRequest.Builder =
+ addWorkflow(SmsWorkflow.builder(number).apply(properties).build())
+/**
+ * Adds a TTS (Text-to-Speech) workflow to the verification request.
+ *
+ * @param number The recipient's phone number in E.164 format.
+ *
+ * @return The verification request builder.
+ */
fun VerificationRequest.Builder.voice(number: String): VerificationRequest.Builder =
addWorkflow(VoiceWorkflow(number))
+/**
+ * Adds an email workflow to the verification request.
+ *
+ * @param to The recipient's email address.
+ *
+ * @param from (OPTIONAL) The email address to send the request from. This is not available by default.
+ */
fun VerificationRequest.Builder.email(to: String, from: String? = null): VerificationRequest.Builder =
addWorkflow(EmailWorkflow(to, from))
+/**
+ * Adds a WhatsApp text workflow to the verification request.
+ *
+ * @param to The recipient's phone number in E.164 format.
+ *
+ * @param from A WhatsApp Business Account connected sender number in E.164 format.
+ *
+ * @return The verification request builder.
+ */
fun VerificationRequest.Builder.whatsapp(to: String, from: String): VerificationRequest.Builder =
addWorkflow(WhatsappWorkflow(to, from))
+/**
+ * Adds a WhatsApp Interactive (codeless) workflow to the verification request.
+ * The user will receive a Yes / No prompt instead of a PIN code.
+ *
+ * @param to The recipient's phone number in E.164 format.
+ *
+ * @param from A WhatsApp Business Account connected sender number in E.164 format.
+ *
+ * @return The verification request builder.
+ */
fun VerificationRequest.Builder.whatsappCodeless(to: String, from: String): VerificationRequest.Builder =
addWorkflow(WhatsappCodelessWorkflow(to, from))
diff --git a/src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt b/src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt
index 641262c..9e04c43 100644
--- a/src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt
+++ b/src/main/kotlin/com/vonage/client/kt/VerifyLegacy.kt
@@ -17,31 +17,101 @@ package com.vonage.client.kt
import com.vonage.client.verify.*
+/**
+ * Implementation of the [Verify v1 API](https://developer.vonage.com/en/api/verify).
+ *
+ * *Authentication method:* API key & secret or signature secret.
+ */
class VerifyLegacy internal constructor(private val client: VerifyClient) {
+ /**
+ * Initiate a verification request.
+ *
+ * @param number The phone number to verify in E.164 format.
+ *
+ * @param brand The name of the company or app sending the verification request.
+ * This is what the user will see the sender as on their device.
+ *
+ * @param properties A lambda function for specifying additional parameters of the request.
+ *
+ * @return A [VerifyResponse] object containing the request ID and status.
+ */
fun verify(number: String, brand: String, properties: (VerifyRequest.Builder.() -> Unit) = {}): VerifyResponse =
client.verify(VerifyRequest.builder(number, brand).apply(properties).build())
+ /**
+ * Generate and send a PIN to your user to authorize a payment, compliant with Payment Services Directive 2.
+ *
+ * @param number The phone number to send the request to, in E.164 format.
+ *
+ * @param amount The decimal amount of the payment to be confirmed, in Euros.
+ *
+ * @param payee Name of the recipient that the user is confirming a payment to.
+ *
+ * @param properties (OPTIONAL) A lambda function for specifying additional parameters of the request.
+ *
+ * @return A [VerifyResponse] object containing the request ID and status.
+ */
fun psd2Verify(number: String, amount: Double, payee: String,
properties: (Psd2Request.Builder.() -> Unit) = {}): VerifyResponse =
client.psd2Verify(Psd2Request.builder(number, amount, payee).apply(properties).build())
+ /**
+ * Use this method to check the status of past or current verification requests.
+ *
+ * @param requestIds One or more request IDs to check.
+ *
+ * @return Information about the verification request(s).
+ *
+ * @see [ExistingRequest.info] An alias for this method.
+ */
fun search(vararg requestIds: String): SearchVerifyResponse = client.search(*requestIds)
- fun request(requestId: String): ExistingRequest = ExistingRequest(requestId)
-
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param response Response object containing the request ID.
+ */
fun request(response: VerifyResponse): ExistingRequest = request(response.requestId)
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param requestId ID of the verification request to work with.
+ */
+ fun request(requestId: String): ExistingRequest = ExistingRequest(requestId)
+
+ /**
+ * Call this method to work with an existing verification request.
+ *
+ * @param id ID of the verification request to work with.
+ */
inner class ExistingRequest internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Retrieve details about the verification request.
+ *
+ * @return Information about the verification request.
+ *
+ * @see [search] An alias for this method.
+ */
+ fun info(): SearchVerifyResponse = client.search(id)
+
+ /**
+ * Cancel the verification request.
+ */
fun cancel(): ControlResponse = client.cancelVerification(id)
+ /**
+ * Trigger the next verification step.
+ */
fun advance(): ControlResponse = client.advanceVerification(id)
+ /**
+ * Check the verification code.
+ *
+ * @param code PIN code to check, as entered by the user.
+ */
fun check(code: String): CheckResponse = client.check(id, code)
-
- fun search(): SearchVerifyResponse = client.search(id)
-
}
-
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/vonage/client/kt/Video.kt b/src/main/kotlin/com/vonage/client/kt/Video.kt
index ded55be..d6b5c88 100644
--- a/src/main/kotlin/com/vonage/client/kt/Video.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Video.kt
@@ -18,95 +18,405 @@ package com.vonage.client.kt
import com.vonage.client.video.*
import java.util.*
+/**
+ * Implementation of the [Video API](https://developer.vonage.com/en/api/video).
+ *
+ * *Authentication method:* JWT.
+ */
class Video(private val client: VideoClient) {
+ /**
+ * Create a new session.
+ *
+ * @param properties (OPTIONAL) A lambda function to set the parameters of the session.
+ *
+ * @return The created session metadata.
+ *
+ * @throws [VideoResponseException] If the session could not be created.
+ */
fun createSession(properties: CreateSessionRequest.Builder.() -> Unit = {}): CreateSessionResponse =
client.createSession(CreateSessionRequest.builder().apply(properties).build())
+ /**
+ * Call this method to work with an existing session.
+ *
+ * @param sessionId ID of the session to work with.
+ *
+ * @return An [ExistingSession] object with methods to interact with the session.
+ */
fun session(sessionId: String): ExistingSession = ExistingSession(sessionId)
+ /**
+ * Class for working with an existing session.
+ *
+ * @property id The session ID.
+ */
inner class ExistingSession internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Call this method to work with an existing stream.
+ *
+ * @param streamId UUID of the stream to work with.
+ */
fun stream(streamId: String): ExistingStream = ExistingStream(streamId)
+ /**
+ * Class for working with an existing stream.
+ *
+ * @property id The stream ID.
+ */
inner class ExistingStream internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Retrieves the stream details.
+ *
+ * @return The stream metadata.
+ *
+ * @throws [VideoResponseException] If the stream details could not be retrieved.
+ */
fun info(): GetStreamResponse = client.getStream(this@ExistingSession.id, id)
+ /**
+ * Mute the stream.
+ *
+ * @throws [VideoResponseException] If the stream could not be muted.
+ */
fun mute(): Unit = client.muteStream(this@ExistingSession.id, id)
+ /**
+ * Update the stream's video layout.
+ *
+ * @param layoutClasses The layout class names to apply to the stream.
+ *
+ * @throws [VideoResponseException] If the stream layout could not be updated.
+ */
fun setLayout(vararg layoutClasses: String): Unit =
client.setStreamLayout(this@ExistingSession.id,
SessionStream.builder(id).layoutClassList(layoutClasses.toList()).build()
)
}
+ /**
+ * Call this method to work with an existing client (participant).
+ *
+ * @param connectionId UUID of the connection to work with.
+ *
+ * @return An [ExistingConnection] object with methods to interact with the connection.
+ */
fun connection(connectionId: String): ExistingConnection = ExistingConnection(connectionId)
+ /**
+ * Class for working with an existing connection.
+ *
+ * @property id The connection ID.
+ */
inner class ExistingConnection internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Force the client to disconnect from the session.
+ *
+ * @throws [VideoResponseException] If the client could not be disconnected.
+ */
fun disconnect(): Unit = client.forceDisconnect(this@ExistingSession.id, id)
+ /**
+ * Send a signal to the client.
+ *
+ * @param type Type of data that is being sent to the client. This cannot exceed 128 bytes.
+ * @param data Payload that is being sent to the client. This cannot exceed 8kb.
+ *
+ * @throws [VideoResponseException] If the signal could not be sent.
+ */
fun signal(type: String, data: String): Unit =
client.signal(this@ExistingSession.id, id, signalRequest(type, data))
+ /**
+ * Send DTMF tones to the client. Telephony events are negotiated over SDP and transmitted as
+ * RFC4733/RFC2833 digits to the remote endpoint.
+ *
+ * @param digits The string of DTMF digits to send. This can include 0-9, '*', '#', and 'p'.
+ * A 'p' indicates a pause of 500ms (if you need to add a delay in sending the digits).
+ *
+ * @throws [VideoResponseException] If the DTMF tones could not be sent.
+ */
fun sendDtmf(digits: String): Unit = client.sendDtmf(this@ExistingSession.id, id, digits)
}
+ /**
+ * Mute or unmute all streams in the session, except for those specified.
+ *
+ * @param active Whether to mute streams in the session (`true`) and enable the mute state of the session,
+ * or to disable the mute state of the session (`false`). With the mute state enabled (true), all current
+ * and future streams published to the session (except streams in the `excludedStreamIds` array) are muted.
+ * When you call this method with the active property set to `false`, future streams published to the
+ * session are not muted (but any existing muted streams remain muted).
+ *
+ * @param excludedStreamIds (OPTIONAL) The stream IDs for streams that should not be muted. If you omit
+ * this property, all streams in the session will be muted. This property only applies when the active
+ * property is set to true. When the active property is set to false, it is ignored.
+ *
+ * @return The updated project details.
+ *
+ * @throws [VideoResponseException] If the streams could not be muted.
+ */
fun muteStreams(active: Boolean = true, vararg excludedStreamIds: String): ProjectDetails =
client.muteSession(id, active,
if (excludedStreamIds.isNotEmpty()) excludedStreamIds.toList() else null
)
+ /**
+ * List all streams in the session. This can be used to get information about layout classes used by a Vonage
+ * Video stream. The layout classes define how the stream is displayed in the layout of a broadcast stream.
+ *
+ * @return A list of streams and their layouts for this session.
+ *
+ * @throws [VideoResponseException] If the streams could not be retrieved.
+ */
fun listStreams(): List = client.listStreams(id)
+ /**
+ * List all Archives of this session.
+ *
+ * @param count (OPTIONAL) The number of Archives to return (maximum 1000).
+ * @param offset (OPTIONAL) Index of the first Archive to return (used for pagination).
+ *
+ * @return A list of Archives.
+ *
+ * @throws [VideoResponseException] If the archives could not be retrieved.
+ */
fun listArchives(count: Int = 1000, offset: Int = 0): List =
client.listArchives(listCompositionsFilter(count, offset, id))
+ /**
+ * List all Broadcasts in the application.
+ *
+ * @param count (OPTIONAL) The number of Broadcasts to return (maximum 1000).
+ * @param offset (OPTIONAL) Index of the first Broadcast to return (used for pagination).
+ *
+ * @return A list of Broadcasts.
+ *
+ * @throws [VideoResponseException] If the broadcasts could not be retrieved.
+ */
fun listBroadcasts(count: Int = 1000, offset: Int = 0): List =
client.listBroadcasts(listCompositionsFilter(count, offset, id))
+ /**
+ * Send a signal to all participants (clients) in the session.
+ *
+ * @param type Type of data that is being sent to the clients. This cannot exceed 128 bytes.
+ * @param data Payload that is being sent to the clients. This cannot exceed 8kb.
+ *
+ * @throws [VideoResponseException] If the signal could not be sent.
+ */
fun signalAll(type: String, data: String): Unit =
client.signalAll(id, signalRequest(type, data))
+ /**
+ * Send DTMF tones to all participants in the session. Telephony events are negotiated over SDP and transmitted
+ * as RFC4733/RFC2833 digits to the remote endpoint.
+ *
+ * @param digits The string of DTMF digits to send. This can include 0-9, '*', '#', and 'p'.
+ * A 'p' indicates a pause of 500ms (if you need to add a delay in sending the digits).
+ *
+ * @throws [VideoResponseException] If the DTMF tones could not be sent.
+ */
fun sendDtmf(digits: String): Unit = client.sendDtmf(id, digits)
+ /**
+ * Start real-time Live Captions for the session.
+ *
+ * The maximum allowed duration is 4 hours, after which the audio captioning will stop without any effect
+ * on the ongoing Vonage Video Session. An event will be posted to your callback URL if provided when
+ * starting the captions. Each Vonage Video Session supports only one audio captioning session.
+ *
+ * @param token A valid Vonage JWT with role set to Moderator.
+ * @param properties (OPTIONAL) A lambda function to set the parameters of the audio captioning session.
+ *
+ * @return Unique ID of the audio captioning session.
+ *
+ * @throws [VideoResponseException] If the captions could not be started.
+ */
fun startCaptions(token: String, properties: CaptionsRequest.Builder.() -> Unit = {}): UUID =
client.startCaptions(CaptionsRequest.builder()
.apply(properties).sessionId(id).token(token).build()
).captionsId
+ /**
+ * Stop Live Captions for the session.
+ *
+ * @param captionsId The unique ID of the audio captioning session.
+ *
+ * @throws [VideoResponseException] If the captions could not be stopped.
+ */
fun stopCaptions(captionsId: String): Unit = client.stopCaptions(captionsId)
+ /**
+ * Establish a SIP connection to this session.
+ *
+ * The audio from your end of the SIP call is added to the sessionnas an audio-only stream. The Vonage Video
+ * Media Router mixes audio from other streams in the session and sends the mixed audio to your SIP endpoint.
+ * The call ends when your SIP server sends a `BYE` message to terminate the call. You can also end a call
+ * using the [ExistingSession.ExistingConnection.disconnect] method. The Vonage Video SIP gateway automatically
+ * ends a call after 5 minutes of inactivity (5 minutes without media received). Also, as a security measure,
+ * the Vonage Video SIP gateway closes any SIP call that lasts longer than 6 hours. The SIP interconnect
+ * feature requires the session's media mode to be set to [MediaMode.ROUTED]. For more information, including
+ * technical details and security considerations, see the Vonage Video SIP interconnect developer guide.
+ *
+ * @param properties A lambda function to set the parameters of the SIP dial request.
+ * You need to provide a token and a SIP URI to establish the connection.
+ *
+ * @return Details of the SIP connection.
+ *
+ * @throws [VideoResponseException] If the SIP connection could not be established.
+ */
+ fun sipDial(properties: SipDialRequest.Builder.() -> Unit): SipDialResponse =
+ client.sipDial(SipDialRequest.builder().sessionId(id).apply(properties).build())
+
+ /**
+ * Send audio from the session to a WebSocket. This feature is only supported in `routed` sessions.
+ *
+ * @param properties A lambda function to set the parameters of the WebSocket connection.
+ *
+ * @return Details of the WebSocket connection.
+ *
+ * @throws [VideoResponseException] If the WebSocket connection could not be established.
+ */
+ fun connectToWebsocket(properties: ConnectRequest.Builder.() -> Unit): ConnectResponse =
+ client.connectToWebsocket(ConnectRequest.builder().sessionId(id).apply(properties).build())
+
+ /**
+ * Create a new Experience Composer for this session.
+ *
+ * @param properties A lambda function to set the parameters of the Experience Composer.
+ *
+ * @return Details of the created Experience Composer.
+ *
+ * @throws [VideoResponseException] If the Experience Composer could not be created.
+ */
+ fun startRender(properties: RenderRequest.Builder.() -> Unit): RenderResponse =
+ client.startRender(RenderRequest.builder().sessionId(id).apply(properties).build())
+
+ /**
+ * Create a new Archive from this session.
+ *
+ * @param properties (OPTIONAL) A lambda function to set the parameters of the Archive.
+ *
+ * @return Details of the created Archive.
+ *
+ * @throws [VideoResponseException] If the Archive could not be created.
+ */
fun createArchive(properties: Archive.Builder.() -> Unit = {}): Archive =
client.createArchive(Archive.builder(id).apply(properties).build())
+ /**
+ * Start a live broadcast for the session to HLS (HTTP Live-Streaming) or RTMP streams.
+ *
+ * To successfully start broadcasting a session, at least one client must be connected to the session.
+ * The live-streaming broadcast can target one HLS endpoint and up to five RTMP servers simultaneously
+ * for a session. You can only start live-streaming for sessions that use the Vonage Video Media Router
+ * (with the media mode set to [MediaMode.ROUTED]); you cannot use live-streaming with sessions that have
+ * the media mode set to [MediaMode.RELAYED].
+ *
+ * @param properties A lambda function to set the parameters of the broadcast. See [Broadcast.Builder]
+ * for details of required and optional parameters.
+ *
+ * @return Details of the created broadcast.
+ *
+ * @throws [VideoResponseException] If the broadcast could not be started.
+ */
fun startBroadcast(properties: Broadcast.Builder.() -> Unit): Broadcast =
client.createBroadcast(Broadcast.builder(id).apply(properties).build())
+ /**
+ * Generate a JWT for the session, which can be used in various other methods as required.
+ *
+ * @param options (OPTIONAL) A lambda function to set the parameters (claims) of the token.
+ *
+ * @return A new JWT with the specified claims.
+ *
+ * @throws [VideoResponseException] If the token could not be generated.
+ */
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())
-
+ /**
+ * List all Archives in the application.
+ *
+ * @param count (OPTIONAL) The number of Archives to return (maximum 1000).
+ * @param offset (OPTIONAL) Index of the first Archive to return (used for pagination).
+ *
+ * @return A list of Archives.
+ *
+ * @throws [VideoResponseException] If the archives could not be retrieved.
+ */
fun listArchives(count: Int = 1000, offset: Int = 0): List =
client.listArchives(listCompositionsFilter(count, offset))
+ /**
+ * Call this method to work with an existing archive.
+ *
+ * @param archiveId UUID of the archive to work with.
+ *
+ * @return An [ExistingArchive] object with methods to interact with the archive.
+ */
fun archive(archiveId: String): ExistingArchive = ExistingArchive(archiveId)
+ /**
+ * Class for working with an existing archive.
+ *
+ * @property id The archive ID.
+ */
inner class ExistingArchive internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Retrieve archive information.
+ *
+ * @return Details of the archive.
+ *
+ * @throws [VideoResponseException] If the archive details could not be retrieved.
+ */
fun info(): Archive = client.getArchive(id)
+ /**
+ * Stop the archive.
+ *
+ * Archives stop recording after 4 hours (14,400 seconds), or 60 seconds after the last client disconnects
+ * from the session, or 60 minutes after the last client stops publishing. However, automatic archives continue
+ * recording to multiple consecutive files of up to 4 hours in length each.
+ *
+ * Calling this method for automatic archives has no effect. Automatic archives continue recording to multiple
+ * consecutive files of up to 4 hours (14,400 seconds) in length each, until 60 seconds after the last client
+ * disconnects from the session, or 60 minutes after the last client stops publishing a stream to the session.
+ *
+ * @return Details of the archive.
+ *
+ * @throws [VideoResponseException] If the archive could not be stopped.
+ */
fun stop(): Archive = client.stopArchive(id)
+ /**
+ * Delete the archive.
+ *
+ * @throws [VideoResponseException] If the archive could not be deleted.
+ */
fun delete(): Unit = client.deleteArchive(id)
+ /**
+ * Set the layout for the archive. This only applies if it is composed.
+ *
+ * @param initialLayout The layout type to use for the archive as an enum. If set to
+ * [ScreenLayoutType.CUSTOM], then you must also set the `stylesheet` property.
+ *
+ * @param screenshareType (OPTIONAL) The layout type to use when there is a screen-sharing stream in the
+ * session. Note if you set this property, then `initialLayout` must be set to [ScreenLayoutType.BEST_FIT]
+ * and you must leave the `stylesheet` property unset (null).
+ *
+ * @param stylesheet (OPTIONAL) The CSS stylesheet to use for the archive. If you set this property,
+ * then `initialLayout` must be set to [ScreenLayoutType.CUSTOM].
+ *
+ * @throws [VideoResponseException] If the layout could not be set.
+ */
fun setLayout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Unit =
@@ -117,23 +427,92 @@ class Video(private val client: VideoClient) {
.build()
)
- fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
+ /**
+ * Add a stream to the archive. This only applies if it's a composed archive that was started with
+ * the `streamMode` set to [StreamMode.MANUAL].
+ *
+ * @param streamId UUID of the stream to add.
+ * @param audio (OPTIONAL) Whether the composed archive should include the stream's audio.
+ * @param video (OPTIONAL) Whether the composed archive should include the stream's video.
+ *
+ * @throws [VideoResponseException] If the stream could not be added.
+ */
+ fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true): Unit =
client.addArchiveStream(id, streamId, audio, video)
+ /**
+ * Remove a stream from the archive. This only applies if it's a composed archive that was started with
+ * the `streamMode` set to [StreamMode.MANUAL].
+ *
+ * @param streamId UUID of the stream to remove.
+ *
+ * @throws [VideoResponseException] If the stream could not be removed.
+ */
fun removeStream(streamId: String): Unit = client.removeArchiveStream(id, streamId)
}
+ /**
+ * List all Broadcasts in the application.
+ *
+ * @param count (OPTIONAL) The number of Broadcasts to return (maximum 1000).
+ * @param offset (OPTIONAL) Index of the first Broadcast to return (used for pagination).
+ *
+ * @return A list of Broadcasts.
+ *
+ * @throws [VideoResponseException] If the broadcasts could not be retrieved.
+ */
fun listBroadcasts(count: Int = 1000, offset: Int = 0): List =
client.listBroadcasts(listCompositionsFilter(count, offset))
+ /**
+ * Call this method to work with an existing Broadcast.
+ *
+ * @param broadcastId UUID of the Broadcast to work with.
+ *
+ * @return An [ExistingBroadcast] object with methods to interact with the Broadcast.
+ */
fun broadcast(broadcastId: String): ExistingBroadcast = ExistingBroadcast(broadcastId)
+ /**
+ * Class for working with an existing broadcast.
+ *
+ * @property id The broadcast ID.
+ */
inner class ExistingBroadcast internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Retrieves information about the broadcast in progress.
+ *
+ * @return Details of the broadcast.
+ *
+ * @throws [VideoResponseException] If the broadcast details could not be retrieved.
+ */
fun info(): Broadcast = client.getBroadcast(id)
+ /**
+ * Stops the broadcast.
+ *
+ * @return Details of the broadcast.
+ *
+ * @throws [VideoResponseException] If the broadcast could not be stopped.
+ */
fun stop(): Broadcast = client.stopBroadcast(id)
+ /**
+ * Set the layout for the broadcast.
+ *
+ * @param initialLayout The layout type to use for the broadcast as an enum. If set to
+ * [ScreenLayoutType.CUSTOM], then you must also set the `stylesheet` property.
+ *
+ * @param screenshareType (OPTIONAL) The layout type to use when there is a screen-sharing stream in the
+ * session. Note if you set this property, then `initialLayout` must be set to [ScreenLayoutType.BEST_FIT]
+ * and you must leave the `stylesheet` property unset (null).
+ *
+ * @param stylesheet (OPTIONAL) The CSS stylesheet to use for the broadcast. If you set this property,
+ * then `initialLayout` must be set to [ScreenLayoutType.CUSTOM].
+ *
+ * @throws [VideoResponseException] If the layout could not be set.
+ */
fun setLayout(initialLayout: ScreenLayoutType,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Unit =
@@ -144,24 +523,71 @@ class Video(private val client: VideoClient) {
.build()
)
- fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true) =
+ /**
+ * Add a stream to the broadcast.
+ *
+ * @param streamId UUID of the stream to add.
+ * @param audio (OPTIONAL) Whether the broadcast should include the stream's audio.
+ * @param video (OPTIONAL) Whether the broadcast should include the stream's video.
+ *
+ * @throws [VideoResponseException] If the stream could not be added.
+ */
+ fun addStream(streamId: String, audio: Boolean = true, video: Boolean = true): Unit =
client.addBroadcastStream(id, streamId, audio, video)
+ /**
+ * Remove a stream from the broadcast.
+ *
+ * @param streamId UUID of the stream to remove.
+ *
+ * @throws [VideoResponseException] If the stream could not be removed.
+ */
fun removeStream(streamId: String): Unit = client.removeBroadcastStream(id, streamId)
}
- fun startRender(properties: RenderRequest.Builder.() -> Unit): RenderResponse =
- client.startRender(RenderRequest.builder().apply(properties).build())
-
+ /**
+ * List all Experience Composers in the application.
+ *
+ * @param count (OPTIONAL) The number of Experience Composers to return (maximum 1000).
+ * @param offset (OPTIONAL) Index of the first Experience Composer to return (used for pagination).
+ *
+ * @return A list of Experience Composers.
+ *
+ * @throws [VideoResponseException] If the Experience Composers could not be retrieved.
+ */
fun listRenders(count: Int = 1000, offset: Int = 0): List =
client.listRenders(ListStreamCompositionsRequest.builder().count(count).offset(offset).build())
+ /**
+ * Call this method to work with an existing Experience Composer.
+ *
+ * @param renderId UUID of the Experience Composer to work with.
+ *
+ * @return An [ExistingRender] object with methods to interact with the Experience Composer.
+ */
fun render(renderId: String): ExistingRender = ExistingRender(renderId)
+ /**
+ * Class for working with an existing Experience Composer.
+ *
+ * @property id The render ID.
+ */
inner class ExistingRender internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Retrieves the Experience Composer.
+ *
+ * @return Details of the Experience Composer.
+ *
+ * @throws [VideoResponseException] If the Experience Composer details could not be retrieved.
+ */
fun info(): RenderResponse = client.getRender(id)
+ /**
+ * Stops the Experience Composer.
+ *
+ * @throws [VideoResponseException] If the Experience Composer could not be stopped.
+ */
fun stop(): Unit = client.stopRender(id)
}
}
@@ -179,18 +605,64 @@ private fun streamCompositionLayout(initialLayout: ScreenLayoutType,
StreamCompositionLayout.builder(initialLayout)
.screenshareType(screenshareType).stylesheet(stylesheet).build()
+/**
+ * Adds an RTMP stream to the broadcast builder.
+ *
+ * @param properties A lambda function to set the parameters of the RTMP stream.
+ *
+ * @return The updated broadcast builder.
+ */
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())
+/**
+ * Adds an HTTP Live Stream to the broadcast builder.
+ *
+ * @param properties (OPTIONAL) A lambda function to set the parameters of the HLS stream.
+ *
+ * @return The updated broadcast builder.
+ */
+fun Broadcast.Builder.hls(properties: Hls.Builder.() -> Unit = {}): Broadcast.Builder =
+ hls(Hls.builder().apply(properties).build())
+/**
+ * Sets the layout for a composed archive. If this option is specified,
+ * [Archive.Builder.outputMode] must be set to [OutputMode.COMPOSED].
+ *
+ * @param initialLayout The layout type to use for the archive as an enum. If set to
+ * [ScreenLayoutType.CUSTOM], then you must also set the `stylesheet` property.
+ *
+ * @param screenshareType (OPTIONAL) The layout type to use when there is a screen-sharing stream in the
+ * session. Note if you set this property, then `initialLayout` must be set to [ScreenLayoutType.BEST_FIT]
+ * and you must leave the `stylesheet` property unset (null).
+ *
+ * @param stylesheet (OPTIONAL) The CSS stylesheet to use for the archive. If you set this property,
+ * then `initialLayout` must be set to [ScreenLayoutType.CUSTOM].
+ *
+ * @return The updated archive builder.
+ */
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,
+/**
+ * Specify this to assign the initial layout type for the broadcast. If you do not specify an initial layout type,
+ * the broadcast stream uses [ScreenLayoutType.BEST_FIT] as the default layout type.
+ *
+ * @param initialLayout The layout type to use for the broadcast as an enum. If set to
+ * [ScreenLayoutType.CUSTOM], then you must also set the `stylesheet` property.
+ *
+ * @param screenshareType (OPTIONAL) The layout type to use when there is a screen-sharing stream in the
+ * session. Note if you set this property, then `initialLayout` must be set to [ScreenLayoutType.BEST_FIT]
+ * and you must leave the `stylesheet` property unset (null).
+ *
+ * @param stylesheet (OPTIONAL) The CSS stylesheet to use for the broadcast. If you set this property,
+ * then `initialLayout` must be set to [ScreenLayoutType.CUSTOM].
+ *
+ * @return The updated broadcast builder.
+ */
+fun Broadcast.Builder.layout(initialLayout: ScreenLayoutType = ScreenLayoutType.BEST_FIT,
screenshareType: ScreenLayoutType? = null,
stylesheet: String? = null): Broadcast.Builder =
layout(streamCompositionLayout(initialLayout, screenshareType, stylesheet))
diff --git a/src/main/kotlin/com/vonage/client/kt/Voice.kt b/src/main/kotlin/com/vonage/client/kt/Voice.kt
index 6321dcb..61a9651 100644
--- a/src/main/kotlin/com/vonage/client/kt/Voice.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Voice.kt
@@ -15,103 +15,328 @@
*/
package com.vonage.client.kt
+import com.vonage.client.users.channels.Websocket
import com.vonage.client.voice.*
import com.vonage.client.voice.ncco.*
-import java.net.URI
import java.time.Instant
import java.util.*
+/**
+ * Implementation of the [Voice API](https://developer.vonage.com/en/api/voice).
+ *
+ * *Authentication method:* JWT.
+ */
class Voice internal constructor(private val client: VoiceClient) {
+ /**
+ * Call this method to work with an existing call.
+ *
+ * @param callId UUID of the call to work with.
+ */
fun call(callId: String): ExistingCall = ExistingCall(callId)
+ /**
+ * Class for working with an existing call.
+ *
+ * @property id The call ID.
+ */
inner class ExistingCall internal constructor(id: String): ExistingResource(id) {
+ /**
+ * Get information about the call.
+ *
+ * @return Details of the call.
+ */
fun info(): CallInfo = client.getCallDetails(id)
+ /**
+ * End the call.
+ */
fun hangup(): Unit = client.terminateCall(id)
+ /**
+ * Mute the call. The other party will not be able to hear this call.
+ */
fun mute(): Unit = client.muteCall(id)
+ /**
+ * Unmute the call. The other party will be able to hear this call again.
+ */
fun unmute(): Unit = client.unmuteCall(id)
+ /**
+ * Earmuff the call. This call will not be able to hear audio.
+ */
fun earmuff(): Unit = client.earmuffCall(id)
+ /**
+ * Unearmuff the call. This call will be able to hear audio again.
+ */
fun unearmuff(): Unit = client.unearmuffCall(id)
+ /**
+ * Transfer the call using an NCCO.
+ *
+ * @param actions The actions to perform after the transfer.
+ */
fun transfer(vararg actions: Action): Unit = client.transferCall(id, Ncco(actions.asList()))
+ /**
+ * Transfer the call using an answer URL.
+ *
+ * @param nccoUrl URL of the endpoint that will return the NCCO to execute after the transfer.
+ */
fun transfer(nccoUrl: String): Unit = client.transferCall(id, nccoUrl)
- fun transfer(nccoUrl: URI): Unit = transfer(nccoUrl.toString())
-
+ /**
+ * Play DTMF tones into the call.
+ *
+ * @param digits The digits to send. Valid characters are `0-9`, `#`, `*` and `p`,
+ * which indicates a short pause between tones.
+ *
+ * @return The DTMF status and call leg ID.
+ */
fun sendDtmf(digits: String): DtmfResponse = client.sendDtmf(id, digits)
+ /**
+ * Play an audio file to the call.
+ *
+ * @param url The publicly accessible URL of the audio file to play.
+ * @param loop The number of times to loop the audio file.
+ * @param level The volume level at which to play the audio file, between -1 (quietest) and 1 (loudest).
+ *
+ * @return The stream status and call leg ID.
+ */
fun streamAudio(streamUrl: String, loop: Int = 1, level: Double = 0.0): StreamResponse =
client.startStream(id, streamUrl, loop, level)
+ /**
+ * Stop playing audio into the call.
+ *
+ * @return The stream status and call leg ID.
+ */
fun stopStream(): StreamResponse = client.stopStream(id)
+ /**
+ * Speak text into the call.
+ *
+ * @param text The text to speak.
+ * @param properties (OPTIONAL) Additional properties for the talk action.
+ *
+ * @return The talk status and call leg ID.
+ */
fun startTalk(text: String, properties: (TalkPayload.Builder.() -> Unit) = {}): TalkResponse =
client.startTalk(id, TalkPayload.builder(text).apply(properties).build())
+ /**
+ * Stop the Text-to-Speech (TTS).
+ *
+ * @return The talk status and call leg ID.
+ */
fun stopTalk(): TalkResponse = client.stopTalk(id)
}
+ /**
+ * Retrieve details of your calls.
+ *
+ * @param filter (OPTIONAL) A lambda function for specifying the parameters to narrow down the results.
+ */
fun listCalls(filter: (CallsFilter.Builder.() -> Unit)? = null): CallInfoPage =
if (filter == null) client.listCalls()
else client.listCalls(CallsFilter.builder().apply(filter).build())
- fun createCall(call: Call.Builder.() -> Unit): CallEvent =
- client.createCall(Call.builder().apply(call).build())
+ /**
+ * Initiate an outbound call.
+ *
+ * @param properties A lambda function for specifying the call's parameters.
+ *
+ * @return Details of the created call.
+ */
+ fun createCall(properties: Call.Builder.() -> Unit): CallEvent =
+ client.createCall(Call.builder().apply(properties).build())
}
-fun CallsFilter.Builder.dateStart(dateStart: String): CallsFilter.Builder =
- dateStart(Date.from(Instant.parse(dateStart)))
+/**
+ * Sets the start date for the [Voice.listCalls] filter.
+ *
+ * @param dateStart ISO-8601 timestamp start retrieving calls from.
+ *
+ * @return The updated [CallsFilter.Builder].
+ */
+fun CallsFilter.Builder.dateStart(dateStart: Instant): CallsFilter.Builder = dateStart(Date.from(dateStart))
-fun CallsFilter.Builder.dateEnd(dateEnd: String): CallsFilter.Builder =
- dateEnd(Date.from(Instant.parse(dateEnd)))
+/**
+ * Sets the end date for the [Voice.listCalls] filter.
+ *
+ * @param dateStart ISO-8601 timestamp to stop retrieving calls from.
+ *
+ * @return The updated [CallsFilter.Builder].
+ */
+fun CallsFilter.Builder.dateEnd(dateEnd: Instant): CallsFilter.Builder = dateEnd(Date.from(dateEnd))
+/**
+ * Configure the behavior of Advanced Machine Detection. This overrides [Call.Builder#machineDetection] setting
+ * and is a premium feature, so you cannot set both.
+ *
+ * @param amd A lambda function for configuring the Advanced Machine Detection settings.
+ *
+ * @return The updated [Call.Builder].
+ */
fun Call.Builder.advancedMachineDetection(amd: AdvancedMachineDetection.Builder.() -> Unit = {}): Call.Builder =
advancedMachineDetection(AdvancedMachineDetection.builder().apply(amd).build())
+/**
+ * Configure the behavior of Automatic Speech Recognition to enable speech input. This setting is mutually
+ * exclusive with [Call.Builder#dtmf], so you must provide one or the other for receiving input from the callee.
+ *
+ * @param settings (OPTIONAL) A lambda function for configuring the speech recognition settings.
+ *
+ * @return The updated [InputAction.Builder].
+ */
fun InputAction.Builder.speech(settings: SpeechSettings.Builder.() -> Unit = {}): InputAction.Builder =
speech(SpeechSettings.builder().apply(settings).build())
+/**
+ * Configure the behavior of Dual-Tone Multi-Frequency (DTMF) input. This setting is mutually exclusive with
+ * [Call.Builder#speech], so you must provide one or the other for receiving input from the callee.
+ *
+ * @param settings (OPTIONAL) A lambda function for configuring the DTMF settings.
+ *
+ * @return The updated [InputAction.Builder].
+ */
fun InputAction.Builder.dtmf(settings: DtmfSettings.Builder.() -> Unit = {}): InputAction.Builder =
dtmf(DtmfSettings.builder().apply(settings).build())
+/**
+ * Configure the behaviour of call recording transcription. If present (even if all settings are default),
+ * transcription is activated. The [ConversationAction.Builder.record] parameter must also be set to `true`.
+ *
+ * @param settings (OPTIONAL) A lambda function for configuring the transcription settings.
+ *
+ * @return The updated [ConversationAction.Builder].
+ */
fun ConversationAction.Builder.transcription(settings: TranscriptionSettings.Builder.() -> Unit = {}):
ConversationAction.Builder = transcription(TranscriptionSettings.builder().apply(settings).build())
+/**
+ * Configure the behaviour of call recording transcription. Calling this method activates transcription.
+ *
+ * @param settings (OPTIONAL) A lambda function for configuring the transcription settings.
+ *
+ * @return The updated [RecordAction.Builder].
+ */
fun RecordAction.Builder.transcription(settings: TranscriptionSettings.Builder.() -> Unit = {}): RecordAction.Builder =
transcription(TranscriptionSettings.builder().apply(settings).build())
+/**
+ * Configure the behavior of Advanced Machine Detection. This overrides [ConnectAction.Builder#machineDetection]
+ * setting and is a premium feature, so you cannot set both.
+ *
+ * @param amd A lambda function for configuring the Advanced Machine Detection settings.
+ *
+ * @return The updated [ConnectAction.Builder].
+ */
fun ConnectAction.Builder.advancedMachineDetection(amd: AdvancedMachineDetection.Builder.() -> Unit = {}):
ConnectAction.Builder = advancedMachineDetection(AdvancedMachineDetection.builder().apply(amd).build())
+/**
+ * Builds an NCCO action to record the call.
+ *
+ * @param properties (OPTIONAL) A lambda function for configuring parameters of the record action.
+ *
+ * @return A new [RecordAction] with the specified properties.
+ */
fun recordAction(properties: RecordAction.Builder.() -> Unit = {}): RecordAction =
RecordAction.builder().apply(properties).build()
+/**
+ * Builds an NCCO action to play Text-to-Speech into the call.
+ *
+ * @param text A string of up to 1,500 characters (excluding SSML tags) containing the message to be synthesized in
+ * the Call or Conversation. A single comma in text adds a short pause to the synthesized speech. To add a longer
+ * pause a break tag needs to be used in SSML. To use SSML tags, you must enclose the text in a `speak` element.
+ *
+ * @param properties (OPTIONAL) A lambda function for configuring additional parameters of the TTS action.
+ *
+ * @return A new [TalkAction] with the specified properties.
+ */
fun talkAction(text: String, properties: TalkAction.Builder.() -> Unit = {}): TalkAction =
TalkAction.builder(text).apply(properties).build()
+/**
+ * Builds an NCCO action to play an audio stream into the call.
+ *
+ * @param streamUrl The URL of the audio stream to play.
+ *
+ * @param properties (OPTIONAL) A lambda function for configuring additional parameters of the stream action.
+ *
+ * @return A new [StreamAction] with the specified properties.
+ */
fun streamAction(streamUrl: String, properties: StreamAction.Builder.() -> Unit = {}): StreamAction =
StreamAction.builder(streamUrl).apply(properties).build()
+/**
+ * Builds an NCCO action to send custom events to a configured webhook.
+ *
+ * @param eventUrl The URL to send events to. If you return an NCCO when you receive a notification,
+ * it will replace the current NCCO.
+ *
+ * @param payload A map of key-value pairs to send as the event payload.
+ *
+ * @param eventMethod (OPTIONAL) The HTTP method to use when sending the event.
+ *
+ * @return A new [NotifyAction] with the specified properties.
+ */
fun notifyAction(eventUrl: String, payload: Map, eventMethod: EventMethod? = null): NotifyAction =
NotifyAction.builder(payload, eventUrl).eventMethod(eventMethod).build()
-fun inputAction(properties: InputAction.Builder.() -> Unit = {}): InputAction =
+/**
+ * Builds an NCCO action for gathering input from the call via DTMF or speech recognition.
+ *
+ * @param properties A lambda function for configuring the input action parameters.
+ *
+ * @return A new [InputAction] with the specified properties.
+ */
+fun inputAction(properties: InputAction.Builder.() -> Unit): InputAction =
InputAction.builder().apply(properties).build()
+/**
+ * Builds an NCCO action to enable hosting conference calls, while preserving the communication context.
+ *
+ * @param name The name of the conversation.
+ *
+ * @param properties (OPTIONAL) A lambda function for configuring the Conversation action parameters.
+ *
+ * @return A new [ConversationAction] with the specified properties.
+ */
fun conversationAction(name: String, properties: ConversationAction.Builder.() -> Unit = {}): ConversationAction =
ConversationAction.builder(name).apply(properties).build()
+/**
+ * Builds an NCCO action to connect the call to another destination. This is the underlying method that other
+ * `connectTo*` methods in [Voice] use to build their actions. It is recommended to use those instead.
+ *
+ * @param endpoint The endpoint to connect the call to.
+ *
+ * @param properties (OPTIONAL) A lambda function for configuring the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the specified properties.
+ */
fun connectAction(endpoint: com.vonage.client.voice.ncco.Endpoint,
properties: ConnectAction.Builder.() -> Unit = {}): ConnectAction =
ConnectAction.builder(endpoint).apply(properties).build()
+/**
+ * Builds an NCCO action to connect the call to a PSTN number.
+ *
+ * @param number The MSISDN to connect the call to, in E.164 format.
+ * @param dtmfAnswer (OPTIONAL) The DTMF digits to send when the call is answered.
+ * @param onAnswerUrl (OPTIONAL) The URL to fetch a new NCCO from when the call is answered.
+ * @param onAnswerRingback (OPTIONAL) The URL to fetch a ringback tone from when the call is answered.
+ * @param properties (OPTIONAL) A lambda function for additional configuration of the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the [com.vonage.client.voice.ncco.PhoneEndpoint] and specified properties.
+ */
fun connectToPstn(number: String, dtmfAnswer: String? = null,
onAnswerUrl: String? = null, onAnswerRingback: String? = null,
properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction =
@@ -119,18 +344,55 @@ fun connectToPstn(number: String, dtmfAnswer: String? = null,
.dtmfAnswer(dtmfAnswer).onAnswer(onAnswerUrl, onAnswerRingback).build(), properties
)
+/**
+ * Builds an NCCO action to connect the call to a Vonage Business Communications (VBC) extension.
+ *
+ * @param extension The VBC extension number to connect the call to.
+ *
+ * @param properties (OPTIONAL) A lambda function for additional configuration of the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the [com.vonage.client.voice.ncco.VbcEndpoint] and specified properties.
+ */
fun connectToVbc(extension: String, properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction =
connectAction(com.vonage.client.voice.ncco.VbcEndpoint.builder(extension).build(), properties)
+/**
+ * Builds an NCCO action to connect the call to an RTC capable application.
+ *
+ * @param user The username to connect the call to.
+ * @param properties (OPTIONAL) A lambda function for additional configuration of the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the [com.vonage.client.voice.ncco.AppEndpoint] and specified 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,
+/**
+ * Builds an NCCO action to connect the call to a WebSocket endpoint.
+ *
+ * @param uri The URI of the WebSocket to connect to.
+ * @param contentType The internet media type for the audio you are streaming.
+ * @param headers (OPTIONAL) A map of custom metadata to send with the WebSocket connection.
+ * @param properties (OPTIONAL) A lambda function for additional configuration of the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the [com.vonage.client.voice.ncco.WebSocketEndpoint] and specified properties.
+ */
+fun connectToWebsocket(uri: String, contentType: Websocket.ContentType, headers: Map? = null,
properties: ConnectAction.Builder.() -> Unit = {}) : ConnectAction =
- connectAction(com.vonage.client.voice.ncco.WebSocketEndpoint.builder(uri, contentType)
+ connectAction(com.vonage.client.voice.ncco.WebSocketEndpoint.builder(uri, contentType.toString())
.headers(headers).build(), properties
)
+/**
+ * Builds an NCCO action to connect the call to a SIP endpoint.
+ *
+ * @param uri The URI of the SIP endpoint to connect to.
+ * @param customHeaders (OPTIONAL) A map of custom metadata to send with the SIP connection.
+ * @param userToUserHeader (OPTIONAL) A string to send as the User-to-User header, as per RFC 7433.
+ * @param properties (OPTIONAL) A lambda function for additional configuration of the connect action parameters.
+ *
+ * @return A new [ConnectAction] with the [com.vonage.client.voice.ncco.SipEndpoint] and specified 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)
@@ -143,19 +405,57 @@ fun connectToSip(uri: String, customHeaders: Map? = null, userToUse
return connectAction(builder.build(), properties)
}
+/**
+ * Sets the call destination to a Public Switched Telephone Network (PSTN) or mobile number.
+ *
+ * @param number The MSISDN to call, in E.164 format.
+ * @param dtmfAnswer (OPTIONAL) The DTMF digits to send when the call is answered.
+ *
+ * @return The updated [Call.Builder].
+ */
fun Call.Builder.toPstn(number: String, dtmfAnswer: String? = null): Call.Builder =
to(com.vonage.client.voice.PhoneEndpoint(number, dtmfAnswer))
+/**
+ * Sets the call destination to a SIP URI.
+ *
+ * @param uri URI of the SIP endpoint to call.
+ * @param customHeaders (OPTIONAL) A map of custom metadata to send with the SIP connection.
+ * @param userToUserHeader (OPTIONAL) A string to send as the User-to-User header, as per RFC 7433.
+ *
+ * @return The updated [Call.Builder].
+ */
fun Call.Builder.toSip(uri: String, customHeaders: Map? = null,
userToUserHeader: String? = null): Call.Builder =
to(com.vonage.client.voice.SipEndpoint(uri, customHeaders, userToUserHeader))
-fun Call.Builder.toWebSocket(uri: String, contentType: String? = null,
+/**
+ * Sets the call destination to a WebSocket endpoint.
+ *
+ * @param uri The URI of the WebSocket to connect to.
+ * @param contentType The internet media type for the audio you are streaming.
+ * @param headers (OPTIONAL) A map of custom metadata to send with the WebSocket connection.
+ */
+fun Call.Builder.toWebSocket(uri: String, contentType: Websocket.ContentType,
headers: Map? = null): Call.Builder =
- to(com.vonage.client.voice.WebSocketEndpoint(uri, contentType, headers))
+ to(com.vonage.client.voice.WebSocketEndpoint(uri, contentType.toString(), headers))
+/**
+ * Sets the call destination to a Vonage Business Communications (VBC) extension.
+ *
+ * @param extension The VBC extension number to call.
+ *
+ * @return The updated [Call.Builder].
+ */
fun Call.Builder.toVbc(extension: String): Call.Builder =
to(com.vonage.client.voice.VbcEndpoint(extension))
+/**
+ * Sets the call destination to an RTC capable application user.
+ *
+ * @param user The username of the application user to call.
+ *
+ * @return The updated [Call.Builder].
+ */
fun Call.Builder.toApp(user: String): Call.Builder =
to(com.vonage.client.voice.AppEndpoint(user))
diff --git a/src/main/kotlin/com/vonage/client/kt/Vonage.kt b/src/main/kotlin/com/vonage/client/kt/Vonage.kt
index 5a5080a..5a38c78 100644
--- a/src/main/kotlin/com/vonage/client/kt/Vonage.kt
+++ b/src/main/kotlin/com/vonage/client/kt/Vonage.kt
@@ -18,32 +18,154 @@ package com.vonage.client.kt
import com.vonage.client.HttpConfig
import com.vonage.client.VonageClient
-class Vonage(init: VonageClient.Builder.() -> Unit) {
- private val client : VonageClient = VonageClient.builder().apply(init).build()
+/**
+ * Entry point for the SDK. This class provides access to all the Vonage APIs via its properties.
+ * The constructor takes a lambda that configures the client. At a minimum, you must provide
+ * your account credentials (API key and secret) and/or application ID and private key depending
+ * on which APIs you plan to use. It is recommended that you set the parameters for both
+ * authentication methods to maximise compatibility.
+ *
+ * Every sub-client for interacting with each API is exposed as a property of this class. Those classes
+ * document their accepted / preferred authentication type(s). For JWT authentication, you will need to
+ * provide the application ID and private key path. For API key and secret authentication, you will need
+ * to provide the API key and secret. These are specified on the [VonageClient.Builder] when initialising
+ * this class using the provided lambda function.
+ *
+ * @param config The configuration lambda, where you provide your Vonage account credentials.
+ */
+class Vonage(config: VonageClient.Builder.() -> Unit) {
+ private val client : VonageClient = VonageClient.builder().apply(config).build()
+
+ /**
+ * Access to the Vonage Account API.
+ *
+ * @return The [Account] client.
+ */
val account = Account(client.accountClient)
+
+ /**
+ * Access to the Vonage Application API.
+ *
+ * @return The [Application] client.
+ */
val application = Application(client.applicationClient)
+
+ /**
+ * Access to the Vonage Conversion API.
+ *
+ * @return The [Conversion] client.
+ */
val conversion = Conversion(client.conversionClient)
+
+ /**
+ * Access to the Vonage Messages API.
+ *
+ * @return The [Messages] client.
+ */
val messages = Messages(client.messagesClient)
+
+ /**
+ * Access to the Vonage Number Insight API.
+ *
+ * @return The [NumberInsight] client.
+ */
val numberInsight = NumberInsight(client.insightClient)
+
+ /**
+ * Access to the Vonage Numbers API.
+ *
+ * @return The [Numbers] client.
+ */
val numbers = Numbers(client.numbersClient)
+
+ /**
+ * Access to the CAMARA Number Verification API.
+ *
+ * @return The [NumberVerification] client.
+ */
val numberVerification = NumberVerification(client.numberVerificationClient)
+
+ /**
+ * Access to the Vonage Redact API.
+ *
+ * @return The [Redact] client.
+ */
val redact = Redact(client.redactClient)
+
+ /**
+ * Access to the CAMARA SIM Swap API.
+ *
+ * @return The [SimSwap] client.
+ */
val simSwap = SimSwap(client.simSwapClient)
+
+ /**
+ * Access to the Vonage SMS API. For more channels and message types,
+ * we recommend using the Messages API instead.
+ *
+ * @return The [Sms] client.
+ */
val sms = Sms(client.smsClient)
+
+ /**
+ * Access to the Vonage Subaccounts API. You must register explicitly to be able to use this.
+ *
+ * @return The [Subaccounts] client.
+ */
val subaccounts = Subaccounts(client.subaccountsClient)
+
+ /**
+ * Access to the Vonage Users API.
+ *
+ * @return The [Users] client.
+ */
val users = Users(client.usersClient)
+
+ /**
+ * Access to the Vonage Verify v2 API.
+ *
+ * @return The [Verify] client.
+ */
val verify = Verify(client.verify2Client)
+
+ /**
+ * Access to the Vonage Verify v1 API.
+ *
+ * @return The [VerifyLegacy] client.
+ */
val verifyLegacy = VerifyLegacy(client.verifyClient)
+
+ /**
+ * Access to the Vonage Video API.
+ *
+ * @return The [Video] client.
+ */
val video = Video(client.videoClient)
+
+ /**
+ * Access to the Vonage Voice API.
+ *
+ * @return The [Voice] client.
+ */
val voice = Voice(client.voiceClient)
}
+/**
+ * Use environment variables to populate authentication parameters.
+ * This method will look for the following environment variables and set them on the builder if present:
+ *
+ * - **VONAGE_API_KEY**: Vonage account API key.
+ * - **VONAGE_API_SECRET**: Vonage account API secret.
+ * - **VONAGE_SIGNATURE_SECRET**: Vonage account signature secret.
+ * - **VONAGE_APPLICATION_ID**: Vonage application ID.
+ * - **VONAGE_PRIVATE_KEY_PATH**: Path to the private key file of the application.
+ */
fun VonageClient.Builder.authFromEnv(): VonageClient.Builder {
- val apiKey = env("VONAGE_API_KEY")
- val apiSecret = env("VONAGE_API_SECRET")
- val signatureSecret = env("VONAGE_SIGNATURE_SECRET")
- val applicationId = env("VONAGE_APPLICATION_ID")
- val privateKeyPath = env("VONAGE_PRIVATE_KEY_PATH")
+ val apiKey = System.getenv("VONAGE_API_KEY")
+ val apiSecret = System.getenv("VONAGE_API_SECRET")
+ val signatureSecret = System.getenv("VONAGE_SIGNATURE_SECRET")
+ val applicationId = System.getenv("VONAGE_APPLICATION_ID")
+ val privateKeyPath = System.getenv("VONAGE_PRIVATE_KEY_PATH")
if (apiKey != null) apiKey(apiKey)
if (apiSecret != null) apiSecret(apiSecret)
if (signatureSecret != null) signatureSecret(signatureSecret)
@@ -52,7 +174,11 @@ fun VonageClient.Builder.authFromEnv(): VonageClient.Builder {
return this
}
+/**
+ * Optional HTTP configuration options for the client.
+ *
+ * @param init The config options lambda.
+ * @return The builder.
+ */
fun VonageClient.Builder.httpConfig(init: HttpConfig.Builder.() -> Unit): VonageClient.Builder =
httpConfig(HttpConfig.builder().apply(init).build())
-
-private fun env(variable: String) : String? = System.getenv(variable)
diff --git a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt
index ff3488a..763380b 100644
--- a/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/AbstractTest.kt
@@ -27,6 +27,7 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.assertThrows
import com.fasterxml.jackson.databind.ObjectMapper
+import com.vonage.client.users.channels.Websocket
import java.net.URI
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
@@ -66,7 +67,8 @@ abstract class AbstractTest {
protected val dtmf = "p*123#"
protected val sipUri = "sip:rebekka@sip.example.com"
protected val websocketUri = "wss://example.com/socket"
- protected val wsContentType = "audio/l16;rate=8000"
+ protected val wsContentTypeStr = "audio/l16;rate=8000"
+ protected val wsContentType = Websocket.ContentType.AUDIO_L16_8K
protected val clientRef = "my-personal-reference"
protected val textHexEncoded = "48656c6c6f2c20576f726c6421"
protected val entityId = "1101407360000017170"
@@ -104,11 +106,11 @@ abstract class AbstractTest {
)
val vonage = Vonage {
- apiKey(apiKey); apiSecret(apiSecret);
- applicationId(applicationId); privateKeyPath(privateKeyPath)
- httpConfig {
+ authFromEnv(); httpConfig {
baseUri("http://localhost:$port")
}
+ apiKey(apiKey); apiSecret(apiSecret); signatureSecret(null)
+ applicationId(applicationId); privateKeyPath(privateKeyPath)
}
@BeforeEach
diff --git a/src/test/kotlin/com/vonage/client/kt/AccountTest.kt b/src/test/kotlin/com/vonage/client/kt/AccountTest.kt
index 378a8c8..1914763 100644
--- a/src/test/kotlin/com/vonage/client/kt/AccountTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/AccountTest.kt
@@ -21,7 +21,7 @@ import org.junit.jupiter.api.assertThrows
import kotlin.test.*
class AccountTest : AbstractTest() {
- private val account = vonage.account
+ private val client = vonage.account
private val authType = AuthType.API_KEY_SECRET_HEADER
private val secretId = "ad6dc56f-07b5-46e1-a527-85530e625800"
private val trx = "8ef2447e69604f642ae59363aa5f781b"
@@ -30,8 +30,8 @@ class AccountTest : AbstractTest() {
private val secretsAltUrl = "${baseUrl}s/$apiKey2/secrets"
private val secretUrl = "$secretsUrl/$secretId"
private val altSecretUrl = "$secretsAltUrl/$secretId"
- private val secretsNoApiKey = account.secrets()
- private val secretsWithApiKey = account.secrets(apiKey2)
+ private val secretsNoApiKey = client.secrets()
+ private val secretsWithApiKey = client.secrets(apiKey2)
private val errorCode = 401
private val secretResponse = linksSelfHref(secretUrl) + mapOf(
"id" to secretId,
@@ -60,7 +60,7 @@ class AccountTest : AbstractTest() {
)
)
- val response = invocation(account)
+ val response = invocation(client)
assertNotNull(response)
assertEquals(moCallbackUrl, response.incomingSmsUrl)
assertEquals(drCallbackUrl, response.deliveryReceiptUrl)
@@ -159,7 +159,7 @@ class AccountTest : AbstractTest() {
)
)
- val response = account.getBalance()
+ val response = client.getBalance()
assertNotNull(response)
assertEquals(value, response.value)
assertEquals(autoReload, response.isAutoReload)
@@ -172,7 +172,7 @@ class AccountTest : AbstractTest() {
status = errorCode, authType = authType,
expectedResponseParams = errorResponse
)
- assertThrows { account.getBalance() }
+ assertThrows { client.getBalance() }
}
@Test
@@ -186,7 +186,7 @@ class AccountTest : AbstractTest() {
"error-code-label" to "success"
)
)
- account.topUp(trx)
+ client.topUp(trx)
}
@Test
@@ -198,7 +198,7 @@ class AccountTest : AbstractTest() {
expectedResponseParams = errorResponse
)
- assertThrows { account.topUp(trx) }
+ assertThrows { client.topUp(trx) }
}
@Test
diff --git a/src/test/kotlin/com/vonage/client/kt/ApplicationTest.kt b/src/test/kotlin/com/vonage/client/kt/ApplicationTest.kt
index 00a9a8e..eb5d98a 100644
--- a/src/test/kotlin/com/vonage/client/kt/ApplicationTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/ApplicationTest.kt
@@ -24,9 +24,9 @@ import com.vonage.client.common.Webhook
import kotlin.test.*
class ApplicationTest : AbstractTest() {
- private val ac = vonage.application
+ private val client = vonage.application
private val authType = AuthType.API_KEY_SECRET_HEADER
- private val existingApplication = ac.application(testUuid)
+ private val existingApplication = client.application(testUuid)
private val baseUrl = "/v2/applications"
private val appUrl = "$baseUrl/$testUuid"
private val answerUrl = "$exampleUrlBase/answer"
@@ -266,18 +266,18 @@ class ApplicationTest : AbstractTest() {
@Test
fun `list all applications`() {
- assertListApplications { ac.listAll() }
+ assertListApplications { client.listAll() }
}
@Test
fun `list applications no filter`() {
- assertListApplications { ac.list() }
+ assertListApplications { client.list() }
}
@Test
fun `list applications all filters`() {
assertListApplications(mapOf("page" to page, "page_size" to pageSize)) {
- ac.list(page, pageSize)
+ client.list(page, pageSize)
}
}
@@ -316,7 +316,7 @@ class ApplicationTest : AbstractTest() {
expectedRequestParams = advancedApplicationRequest,
expectedResponseParams = fullApplicationResponse
)
- assertEqualsFullApplication(ac.create {
+ assertEqualsFullApplication(client.create {
name(name)
publicKey(publicKey)
voice {
@@ -369,7 +369,7 @@ class ApplicationTest : AbstractTest() {
})
assert401ApiResponseException(baseUrl, HttpMethod.POST) {
- ac.create { name(name) }
+ client.create { name(name) }
}
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/com/vonage/client/kt/ConversionTest.kt b/src/test/kotlin/com/vonage/client/kt/ConversionTest.kt
index b4596a4..3876bf8 100644
--- a/src/test/kotlin/com/vonage/client/kt/ConversionTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/ConversionTest.kt
@@ -18,7 +18,7 @@ package com.vonage.client.kt
import kotlin.test.*
class ConversionTest : AbstractTest() {
- private val conversionClient = vonage.conversion
+ private val client = vonage.conversion
private val smsEndpoint = "sms"
private val voiceEndpoint = "voice"
@@ -33,27 +33,27 @@ class ConversionTest : AbstractTest() {
fun `submit sms conversion with timestamp`() {
val delivered = true
mockSuccess(smsMessageId, smsEndpoint, delivered, true)
- conversionClient.convertSms(smsMessageId, delivered, timestampDate.toInstant())
+ client.convertSms(smsMessageId, delivered, timestampDate.toInstant())
}
@Test
fun `submit sms conversion without timestamp`() {
val delivered = false
mockSuccess(smsMessageId, smsEndpoint, delivered, false)
- conversionClient.convertSms(smsMessageId, delivered)
+ client.convertSms(smsMessageId, delivered)
}
@Test
fun `submit voice conversion with timestamp`() {
val delivered = false
mockSuccess(callIdStr, voiceEndpoint, delivered, true)
- conversionClient.convertVoice(callIdStr, delivered, timestamp)
+ client.convertVoice(callIdStr, delivered, timestamp)
}
@Test
fun `submit voice conversion without timestamp`() {
val delivered = true
mockSuccess(callIdStr, voiceEndpoint, delivered, false)
- conversionClient.convertVoice(callIdStr, delivered)
+ client.convertVoice(callIdStr, delivered)
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt
index 50cb21d..76c1321 100644
--- a/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/MessagesTest.kt
@@ -23,13 +23,12 @@ import com.vonage.client.messages.whatsapp.Policy
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.net.URI
-import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class MessagesTest : AbstractTest() {
- private val messagesClient = vonage.messages
+ private val client = vonage.messages
private val sendUrl = "/v1/messages"
private val messageUuid = testUuid
private val mmsChannel = "mms"
@@ -48,7 +47,7 @@ class MessagesTest : AbstractTest() {
expectedUrl = sendUrl, expectedRequestParams = expectedBodyParams,
status = status, expectedResponseParams = expectedResponseParams
)
- assertEquals(messageUuid, messagesClient.send(req))
+ assertEquals(messageUuid, client.send(req))
}
private fun baseBody(messageType: String, channel: String): Map =
@@ -84,7 +83,7 @@ class MessagesTest : AbstractTest() {
@Test
fun `send message exception responses`() {
assertApiResponseException(sendUrl, HttpMethod.POST) {
- messagesClient.send(smsText {
+ client.send(smsText {
from(altNumber); to(toNumber); text(text)
})
}
diff --git a/src/test/kotlin/com/vonage/client/kt/NumberInsightTest.kt b/src/test/kotlin/com/vonage/client/kt/NumberInsightTest.kt
index 7841f09..a1e828d 100644
--- a/src/test/kotlin/com/vonage/client/kt/NumberInsightTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/NumberInsightTest.kt
@@ -22,7 +22,7 @@ import java.math.BigDecimal
import kotlin.test.*
class NumberInsightTest : AbstractTest() {
- private val niClient = vonage.numberInsight
+ private val client = vonage.numberInsight
private val cnam = true
private val realTimeData = true
private val statusMessage = "Success"
@@ -230,48 +230,48 @@ class NumberInsightTest : AbstractTest() {
@Test
fun `basic insight required params`() {
mockInsight(InsightType.BASIC, false)
- assertBasicResponse(niClient.basic(toNumber))
+ assertBasicResponse(client.basic(toNumber))
}
@Test
fun `basic insight all params`() {
mockInsight(InsightType.BASIC, true)
- assertBasicResponse(niClient.basic(toNumber, countryCode))
+ assertBasicResponse(client.basic(toNumber, countryCode))
}
@Test
fun `standard insight required params`() {
mockInsight(InsightType.STANDARD, false)
- assertStandardResponse(niClient.standard(toNumber))
+ assertStandardResponse(client.standard(toNumber))
}
@Test
fun `standard insight all params`() {
mockInsight(InsightType.STANDARD, true)
- assertStandardResponse(niClient.standard(toNumber, countryCode, cnam))
+ assertStandardResponse(client.standard(toNumber, countryCode, cnam))
}
@Test
fun `advanced insight required params`() {
mockInsight(InsightType.ADVANCED, false)
- assertAdvancedResponse(niClient.advanced(toNumber))
+ assertAdvancedResponse(client.advanced(toNumber))
}
@Test
fun `advanced insight all params`() {
mockInsight(InsightType.ADVANCED, true)
- assertAdvancedResponse(niClient.advanced(toNumber, countryCode, cnam, realTimeData))
+ assertAdvancedResponse(client.advanced(toNumber, countryCode, cnam, realTimeData))
}
@Test
fun `advanced async insight required params`() {
mockInsight(InsightType.ADVANCED_ASYNC, false)
- niClient.advancedAsync(toNumber, callbackUrl)
+ client.advancedAsync(toNumber, callbackUrl)
}
@Test
fun `advanced async insight all params`() {
mockInsight(InsightType.ADVANCED_ASYNC, true)
- niClient.advancedAsync(toNumber, callbackUrl, countryCode, cnam)
+ client.advancedAsync(toNumber, callbackUrl, countryCode, cnam)
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/com/vonage/client/kt/NumberVerificationTest.kt b/src/test/kotlin/com/vonage/client/kt/NumberVerificationTest.kt
index 7ed0e7f..2b486ff 100644
--- a/src/test/kotlin/com/vonage/client/kt/NumberVerificationTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/NumberVerificationTest.kt
@@ -20,7 +20,7 @@ import java.net.URLEncoder
import kotlin.test.*
class NumberVerificationTest : AbstractTest() {
- private val nvClient = vonage.numberVerification
+ private val client = vonage.numberVerification
private val nvCheckUrl = "/camara/number-verification/v031/verify"
private val clientAuthUrl = "https://oidc.idp.vonage.com/oauth2/auth"
private val redirectUrl = "$exampleUrlBase/nv/redirect"
@@ -50,7 +50,7 @@ class NumberVerificationTest : AbstractTest() {
expectedResponseParams = if (result != null)
mapOf("devicePhoneNumberVerified" to result) else mapOf()
)
- assertEquals(result ?: false, invocation(nvClient))
+ assertEquals(result ?: false, invocation(client))
}
}
@@ -62,10 +62,10 @@ class NumberVerificationTest : AbstractTest() {
URLEncoder.encode(redirectUrl, "UTF-8")
}&response_type=code"
- assertEquals(URI.create(expectedUrlStr), nvClient.createVerificationUrl(toNumber, redirectUrl))
+ assertEquals(URI.create(expectedUrlStr), client.createVerificationUrl(toNumber, redirectUrl))
val expectedUrlWithState = URI.create("$expectedUrlStr&state=$state")
- assertEquals(expectedUrlWithState, nvClient.createVerificationUrl(toNumber, redirectUrl, state))
+ assertEquals(expectedUrlWithState, client.createVerificationUrl(toNumber, redirectUrl, state))
}
@Test
@@ -84,7 +84,7 @@ class NumberVerificationTest : AbstractTest() {
@Test
fun `verify number relying on cached redirect url`() {
- nvClient.createVerificationUrl(altNumber, redirectUrl)
+ client.createVerificationUrl(altNumber, redirectUrl)
assertVerifyNumber {
verifyNumberWithCode(toNumber, code)
diff --git a/src/test/kotlin/com/vonage/client/kt/SimSwapTest.kt b/src/test/kotlin/com/vonage/client/kt/SimSwapTest.kt
index 2d651a0..55a9da4 100644
--- a/src/test/kotlin/com/vonage/client/kt/SimSwapTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/SimSwapTest.kt
@@ -19,7 +19,7 @@ import com.vonage.client.auth.camara.FraudPreventionDetectionScope
import kotlin.test.*
class SimSwapTest : AbstractTest() {
- private val simSwapClient = vonage.simSwap
+ private val client = vonage.simSwap
private val baseSimSwapUrl = "/camara/sim-swap/v040"
private val checkSimSwapUrl = "$baseSimSwapUrl/check"
private val retrieveSimSwapDateUrl = "$baseSimSwapUrl/retrieve-date"
@@ -66,7 +66,7 @@ class SimSwapTest : AbstractTest() {
expectedRequestParams = phoneNumberMap + mapOf("maxAge" to maxAge),
expectedResponseParams = if (result != null) mapOf("swapped" to result) else mapOf()
)
- assertEquals(result == true, invocation(simSwapClient))
+ assertEquals(result == true, invocation(client))
}
}
@@ -78,7 +78,7 @@ class SimSwapTest : AbstractTest() {
expectedRequestParams = phoneNumberMap,
expectedResponseParams = if (includeResponse) mapOf("latestSimChange" to timestampStr) else mapOf()
)
- assertEquals(if (includeResponse) timestamp else null, simSwapClient.retrieveSimSwapDate(simSwapNumber))
+ assertEquals(if (includeResponse) timestamp else null, client.retrieveSimSwapDate(simSwapNumber))
}
@Test
diff --git a/src/test/kotlin/com/vonage/client/kt/SmsTest.kt b/src/test/kotlin/com/vonage/client/kt/SmsTest.kt
index 04c9514..01fb1d8 100644
--- a/src/test/kotlin/com/vonage/client/kt/SmsTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/SmsTest.kt
@@ -22,7 +22,7 @@ import java.math.BigDecimal
import kotlin.test.*
class SmsTest : AbstractTest() {
- private val smsClient = vonage.sms
+ private val client = vonage.sms
private val sendUrl = "/sms/json"
private val from = brand
private val accountRef = "customer1234"
@@ -70,7 +70,7 @@ class SmsTest : AbstractTest() {
assertEquals(network, first.network)
assertEquals(clientRef, first.clientRef)
assertEquals(accountRef, first.accountRef)
- assertTrue(smsClient.wasSuccessfullySent(response))
+ assertTrue(client.wasSuccessfullySent(response))
}
private fun errorStatus(code: Int, text: String) = mapOf("status" to code, "error-text" to text)
@@ -78,14 +78,14 @@ class SmsTest : AbstractTest() {
@Test
fun `send regular text message success required parameters`() {
testSuccessSingleMessage(mapOf("from" to from, "to" to toNumber, "text" to text, "type" to "unicode")) {
- smsClient.sendText(from, toNumber, text, unicode = true)
+ client.sendText(from, toNumber, text, unicode = true)
}
}
@Test
fun `send unicode text message success required parameters`() {
testSuccessSingleMessage(mapOf("from" to from, "to" to toNumber, "text" to text, "type" to "text")) {
- smsClient.sendText(from, toNumber, text)
+ client.sendText(from, toNumber, text)
}
}
@@ -104,7 +104,7 @@ class SmsTest : AbstractTest() {
"entity-id" to entityId,
"content-id" to contentId
)) {
- smsClient.sendText(from, toNumber, text,
+ client.sendText(from, toNumber, text,
unicode = false, statusReport = statusReport,
ttl = ttl, messageClass = Message.MessageClass.CLASS_1,
clientRef = clientRef, contentId = contentId, entityId = entityId,
@@ -119,7 +119,7 @@ class SmsTest : AbstractTest() {
"from" to from, "to" to toNumber, "type" to "binary",
"body" to textHexEncoded, "udh" to udhHex.lowercase()
)) {
- smsClient.sendBinary(from, toNumber, text.encodeToByteArray(), udhBinary)
+ client.sendBinary(from, toNumber, text.encodeToByteArray(), udhBinary)
}
}
@@ -140,7 +140,7 @@ class SmsTest : AbstractTest() {
"entity-id" to entityId,
"content-id" to contentId
)) {
- smsClient.sendBinary(from, toNumber, text.encodeToByteArray(), udh = udhBinary,
+ client.sendBinary(from, toNumber, text.encodeToByteArray(), udh = udhBinary,
protocolId = protocolId, statusReport = statusReport, ttl = ttl,
messageClass = Message.MessageClass.CLASS_2, clientRef = clientRef,
contentId = contentId, entityId = entityId, callbackUrl = moCallbackUrl
@@ -183,9 +183,9 @@ class SmsTest : AbstractTest() {
)
)
)
- val response = smsClient.sendText(from, toNumber, text)
+ val response = client.sendText(from, toNumber, text)
assertNotNull(response)
- assertFalse(smsClient.wasSuccessfullySent(response))
+ assertFalse(client.wasSuccessfullySent(response))
assertEquals(24, response.size)
var offset = 0
diff --git a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt
index a9659ca..0bc6960 100644
--- a/src/test/kotlin/com/vonage/client/kt/UsersTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/UsersTest.kt
@@ -60,7 +60,7 @@ class UsersTest : AbstractTest() {
)),
"websocket" to listOf(mapOf(
"uri" to websocketUri,
- "content-type" to wsContentType,
+ "content-type" to wsContentTypeStr,
"headers" to customData
)),
"mms" to listOf(mapOf(
@@ -120,7 +120,7 @@ class UsersTest : AbstractTest() {
assertNotNull(websocket)
assertEquals(1, websocket.size)
assertEquals(URI.create(websocketUri), websocket[0].uri)
- assertEquals(Websocket.ContentType.fromString(wsContentType), websocket[0].contentType)
+ assertEquals(Websocket.ContentType.fromString(wsContentTypeStr), websocket[0].contentType)
assertEquals(customData, websocket[0].headers)
val sip = channels.sip
@@ -273,7 +273,7 @@ class UsersTest : AbstractTest() {
customData(customData)
channels(
Pstn(toNumber), Sip(sipUri, sipUser, sipPassword),
- Websocket(websocketUri, Websocket.ContentType.fromString(wsContentType), customData),
+ Websocket(websocketUri, Websocket.ContentType.fromString(wsContentTypeStr), customData),
Vbc(vbcExt.toInt()), Mms(toNumber), Whatsapp(altNumber),
Viber(altNumber), Messenger(messengerId)
)
diff --git a/src/test/kotlin/com/vonage/client/kt/VerifyLegacyTest.kt b/src/test/kotlin/com/vonage/client/kt/VerifyLegacyTest.kt
index aa7adbc..86aa761 100644
--- a/src/test/kotlin/com/vonage/client/kt/VerifyLegacyTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/VerifyLegacyTest.kt
@@ -21,9 +21,9 @@ import java.util.*
import kotlin.test.*
class VerifyLegacyTest : AbstractTest() {
- private val verifyClient = vonage.verifyLegacy
+ private val client = vonage.verifyLegacy
private val requestId = "abcdef0123456789abcdef0123456789"
- private val existingRequest = verifyClient.request(requestId)
+ private val existingRequest = client.request(requestId)
private val eventId = "0A00000012345678"
private val accountId = "abcdef01"
private val payee = "Acme Inc"
@@ -45,11 +45,11 @@ class VerifyLegacyTest : AbstractTest() {
"request_id" to requestId,
"status" to "0"
))
- val successParsed = invocation(verifyClient)
+ val successParsed = invocation(client)
assertNotNull(successParsed)
assertEquals(requestId, successParsed.requestId)
assertEquals(VerifyStatus.OK, successParsed.status)
- assertEquals(existingRequest, verifyClient.request(successParsed))
+ assertEquals(existingRequest, client.request(successParsed))
val errorText = "Your request is incomplete and missing the mandatory parameter `number`"
mockPostQueryParams(expectedUrl, params, expectedResponseParams = mapOf(
@@ -58,7 +58,7 @@ class VerifyLegacyTest : AbstractTest() {
"error_text" to errorText,
"network" to networkCode
))
- val failureParsed = invocation(verifyClient)
+ val failureParsed = invocation(client)
assertNotNull(failureParsed)
assertEquals(requestId, failureParsed.requestId)
assertEquals(VerifyStatus.MISSING_PARAMS, failureParsed.status)
@@ -137,7 +137,7 @@ class VerifyLegacyTest : AbstractTest() {
)
)
)
- val response = if (single) existingRequest.search() else verifyClient.search(*requestIds)
+ val response = if (single) existingRequest.info() else client.search(*requestIds)
assertNotNull(response)
assertNull(response.errorText)
assertEquals(3, response.verificationRequests.size)
@@ -162,7 +162,7 @@ class VerifyLegacyTest : AbstractTest() {
@Test
fun `existing request hashCode is based on the requestId`() {
assertEquals(requestId.hashCode(), existingRequest.hashCode())
- assertEquals(existingRequest, verifyClient.request(requestId))
+ assertEquals(existingRequest, client.request(requestId))
assertEquals(existingRequest, existingRequest)
assertFalse(existingRequest.equals(requestId))
}
diff --git a/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt b/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt
index 04c4dd2..401a3cb 100644
--- a/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/VerifyTest.kt
@@ -23,12 +23,12 @@ import java.util.UUID
import kotlin.test.*
class VerifyTest : AbstractTest() {
- private val verifyClient = vonage.verify
+ private val client = vonage.verify
private val baseUrl = "/v2/verify"
private val requestIdStr = "c11236f4-00bf-4b89-84ba-88b25df97315"
private val requestId = UUID.fromString(requestIdStr)
private val requestIdUrl = "$baseUrl/$requestIdStr"
- private val existingRequest = verifyClient.request(requestIdStr)
+ private val existingRequest = client.request(requestIdStr)
private val timeout = 60
private val fraudCheck = false
private val sandbox = true
@@ -80,7 +80,7 @@ class VerifyTest : AbstractTest() {
if (channel == Channel.SILENT_AUTH) mapOf("check_url" to checkUrl) else mapOf()
)
- val response = verifyClient.sendVerification(brand) {
+ val response = client.sendVerification(brand) {
when (channel) {
Channel.VOICE -> voice(toNumber)
Channel.SMS -> sms(toNumber)
@@ -154,7 +154,7 @@ class VerifyTest : AbstractTest() {
status = 202
)
- val response = verifyClient.sendVerification {
+ val response = client.sendVerification {
brand(brand); clientRef(clientRef); channelTimeout(timeout)
fraudCheck(fraudCheck); codeLength(codeLength); locale(locale)
silentAuth(toNumber, sandbox, redirectUrl); voice(altNumber); sms(toNumber) {
diff --git a/src/test/kotlin/com/vonage/client/kt/VideoTest.kt b/src/test/kotlin/com/vonage/client/kt/VideoTest.kt
index 5fc5ef5..1c4ad37 100644
--- a/src/test/kotlin/com/vonage/client/kt/VideoTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/VideoTest.kt
@@ -460,10 +460,9 @@ class VideoTest : AbstractTest() {
)
)
- val response = client.connectToWebsocket {
+ val response = existingSession.connectToWebsocket {
uri(websocketUri); headers(headers)
- sessionId(sessionId); token(token)
- streams(streamId, randomUuidStr)
+ streams(streamId, randomUuidStr); token(token)
audioRate(Websocket.AudioRate.L16_16K)
}
assertNotNull(response)
@@ -480,8 +479,8 @@ class VideoTest : AbstractTest() {
expectedResponseParams = mapOf("id" to audioConnectorId)
)
- val invocation = { client.connectToWebsocket {
- uri(websocketUri); sessionId(sessionId); token(token)
+ val invocation = { existingSession.connectToWebsocket {
+ uri(websocketUri); token(token)
}}
val response = invocation()
assertNotNull(response)
@@ -576,11 +575,10 @@ class VideoTest : AbstractTest() {
"streamId" to streamId
)
)
- val response = client.sipDial {
- sessionId(sessionId); token(token)
+ val response = existingSession.sipDial {
uri(URI.create(sipUri), true)
addHeaders(headers); secure(secure)
- from(from); video(video)
+ from(from); video(video); token(token)
observeForceMute(observeForceMute)
username(userName); password(password)
}
@@ -599,7 +597,7 @@ class VideoTest : AbstractTest() {
),
expectedResponseParams = mapOf("id" to sipCallId)
)
- val invocation = { client.sipDial {
+ val invocation = { existingSession.sipDial {
sessionId(sessionId); token(token); uri(URI.create(sipUri), false)
} }
val response = invocation()
@@ -914,7 +912,7 @@ class VideoTest : AbstractTest() {
multiBroadcastTag(multiBroadcastTag)
maxDuration(maxDuration); maxBitrate(maxBitrate)
resolution(broadcastResolution); streamMode(broadcastStreamMode)
- layout(ScreenLayoutType.VERTICAL) // This is to get 100% coverage; override below
+ layout(); layout(ScreenLayoutType.VERTICAL) // This is to get 100% coverage; override below
layout(ScreenLayoutType.BEST_FIT, ScreenLayoutType.PIP)
hls {
dvr(dvr); lowLatency(lowLatency)
@@ -1139,9 +1137,8 @@ class VideoTest : AbstractTest() {
expectedRequestParams = renderRequestMap,
expectedResponseParams = mapOf()
)
- val invocation = { client.startRender {
- sessionId(sessionId); token(token)
- url(mediaUrl); name(renderName)
+ val invocation = { existingSession.startRender {
+ token(token); url(mediaUrl); name(renderName)
} }
assertEqualsEmptyRender(invocation())
assertApiResponseException(renderBaseUrl, HttpMethod.POST, invocation)
@@ -1156,7 +1153,7 @@ class VideoTest : AbstractTest() {
),
expectedResponseParams = renderResponseMap
)
- assertEqualsSampleRender(client.startRender {
+ assertEqualsSampleRender(existingSession.startRender {
url(mediaUrl); maxDuration(maxDuration)
resolution(Resolution.HD_LANDSCAPE)
sessionId(sessionId); token(token); name(renderName)
diff --git a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt
index d020586..2989418 100644
--- a/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/VoiceTest.kt
@@ -18,7 +18,6 @@ package com.vonage.client.kt
import com.vonage.client.common.HttpMethod
import com.vonage.client.voice.*
import com.vonage.client.voice.ncco.*
-import java.net.URI
import java.util.*
import kotlin.test.*
@@ -204,7 +203,7 @@ class VoiceTest : AbstractTest() {
)
)
- val callEvent = this@VoiceTest.client.createCall(call)
+ val callEvent = client.createCall(call)
assertNotNull(callEvent)
assertEquals(callIdStr, callEvent.uuid)
assertEquals(callStatus, callEvent.status)
@@ -300,7 +299,6 @@ class VoiceTest : AbstractTest() {
@Test
fun `transfer call with answer url`() {
testModifyCall(nccoUrl = onAnswerUrl, invocation = {
- existingCall.transfer(URI.create(onAnswerUrl))
existingCall.transfer(onAnswerUrl)
})
}
@@ -384,17 +382,17 @@ class VoiceTest : AbstractTest() {
fun `list calls all filter parameters`() {
mockGet(callsBaseUrl, expectedQueryParams = mapOf(
"status" to "unanswered",
- "date_start" to startTime,
- "date_end" to endTime,
+ "date_start" to startTimeStr,
+ "date_end" to endTimeStr,
"page_size" to pageSize,
"record_index" to recordIndex,
"order" to "desc",
"conversation_uuid" to conversationId
), expectedResponseParams = listCallsResponse)
- val callsPage = this@VoiceTest.client.listCalls {
+ val callsPage = client.listCalls {
status(CallStatus.UNANSWERED)
- dateStart(startTimeStr); dateEnd(endTimeStr)
+ dateStart(startTime); dateEnd(endTime)
pageSize(pageSize); recordIndex(recordIndex)
order(CallOrder.DESCENDING); conversationUuid(conversationId)
}
@@ -405,7 +403,7 @@ class VoiceTest : AbstractTest() {
@Test
fun `list calls no filter`() {
mockGet(callsBaseUrl, expectedResponseParams = listCallsResponse)
- assertEqualsSampleCallsPage(this@VoiceTest.client.listCalls())
+ assertEqualsSampleCallsPage(client.listCalls())
}
@Test
@@ -469,13 +467,13 @@ class VoiceTest : AbstractTest() {
@Test
fun `create call to WebSocket`() {
- val baseMap = mapOf("type" to "websocket", "uri" to websocketUri)
+ val baseMap = mapOf("type" to "websocket", "uri" to websocketUri, "content-type" to wsContentTypeStr)
testCreateCallToSingleEndpoint(baseMap) {
- toWebSocket(websocketUri)
+ toWebSocket(websocketUri, wsContentType)
}
testCreateCallToSingleEndpoint(baseMap + mapOf(
- "content-type" to wsContentType, "headers" to customHeaders
+ "content-type" to wsContentTypeStr, "headers" to customHeaders
)) {
toWebSocket(websocketUri, wsContentType, customHeaders)
}
@@ -512,7 +510,7 @@ class VoiceTest : AbstractTest() {
mapOf(
"type" to "websocket",
"uri" to websocketUri,
- "content-type" to wsContentType,
+ "content-type" to wsContentTypeStr,
"headers" to customHeaders
),
mapOf(
@@ -548,7 +546,7 @@ class VoiceTest : AbstractTest() {
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),
+ com.vonage.client.voice.WebSocketEndpoint(websocketUri, wsContentTypeStr, customHeaders),
com.vonage.client.voice.SipEndpoint(sipUri, customHeaders, userToUserHeader)
)
}
@@ -571,7 +569,11 @@ class VoiceTest : AbstractTest() {
@Test
fun `create call with input action required parameters only`() {
- testSingleNcco(ncco = inputAction())
+ val types = listOf("dtmf", "speech")
+ testSingleNcco(
+ additionalParams = mapOf("type" to types),
+ ncco = inputAction { type(types) }
+ )
}
@Test
@@ -645,12 +647,12 @@ class VoiceTest : AbstractTest() {
@Test
fun `create call with connect to WebSocket ncco`() {
testSingleNccoConnect(
- mapOf("uri" to websocketUri, "content-type" to wsContentType),
+ mapOf("uri" to websocketUri, "content-type" to wsContentTypeStr),
connectToWebsocket(websocketUri, wsContentType)
)
testSingleNccoConnect(
- mapOf("uri" to websocketUri, "content-type" to wsContentType, "headers" to customHeaders),
+ mapOf("uri" to websocketUri, "content-type" to wsContentTypeStr, "headers" to customHeaders),
connectToWebsocket(websocketUri, wsContentType, customHeaders)
)
}
diff --git a/src/test/kotlin/com/vonage/client/kt/VonageTest.kt b/src/test/kotlin/com/vonage/client/kt/VonageTest.kt
index c07797d..58317d8 100644
--- a/src/test/kotlin/com/vonage/client/kt/VonageTest.kt
+++ b/src/test/kotlin/com/vonage/client/kt/VonageTest.kt
@@ -15,14 +15,13 @@
*/
package com.vonage.client.kt
-import org.junit.jupiter.api.Test
import kotlin.test.*
class VonageTest {
@Test
- fun `auth from env`() {
- val vonage = Vonage { authFromEnv() }
- assertNotNull(vonage)
+ fun `live testing placeholder`() {
+ val client = Vonage { authFromEnv(); signatureSecret(null) }
+ println("Finished") // Place debug breakpoint here
}
}
\ No newline at end of file