diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 67e8abeb..d25f4e1d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,14 +65,18 @@ jobs:
run: ./scripts/build
- name: Get GitHub OIDC Token
- if: github.repository == 'stainless-sdks/sent-dm-java'
+ if: |-
+ github.repository == 'stainless-sdks/sent-dm-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());
- name: Build and upload Maven artifacts
- if: github.repository == 'stainless-sdks/sent-dm-java'
+ if: |-
+ github.repository == 'stainless-sdks/sent-dm-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 4208b5cb..ac031714 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.6.0"
+ ".": "0.6.1"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 85a351b6..f982b6ed 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 44
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sent%2Fsent-dm-433bfd8c688a6b6d2d4f964bb59121d692798f4e2bb6cb47f6110c4f0e1f638d.yml
-openapi_spec_hash: 5378295d401c8c1152c1946cc7dbd69f
-config_hash: 43a0daa5b05d44a1620e3da0ea6f4fdc
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sent%2Fsent-dm-c1e54655c045f25bd08ecc70c87f938e429911c413c09957d83d3eb9eaab2372.yml
+openapi_spec_hash: 44b7f99e2660bde83eff178b9d4ec00c
+config_hash: d475a61f5b59375bf562f85f19b80409
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6efd531..7800b53f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## 0.6.1 (2026-03-11)
+
+Full Changelog: [v0.6.0...v0.6.1](https://github.com/sentdm/sent-dm-java/compare/v0.6.0...v0.6.1)
+
+### Bug Fixes
+
+* **client:** incorrect `Retry-After` parsing ([6d4e87d](https://github.com/sentdm/sent-dm-java/commit/6d4e87dae24fe9c5775022473d10d1bf67feb3fd))
+* **client:** method name conflict ([1a6889d](https://github.com/sentdm/sent-dm-java/commit/1a6889dbe4a2b34055b50cec925f452f2c2695ac))
+
+
+### Chores
+
+* **ci:** skip uploading artifacts on stainless-internal branches ([888ded0](https://github.com/sentdm/sent-dm-java/commit/888ded06e1c2a5ffc7e7dc93055ec0dc92953a07))
+* **internal:** bump palantir-java-format ([cbd4f1b](https://github.com/sentdm/sent-dm-java/commit/cbd4f1bacdc8260d342b842ff18b830dd75d11b8))
+* **internal:** codegen related update ([0076455](https://github.com/sentdm/sent-dm-java/commit/0076455451ea946dfdfb2be1ed3e57275af0d13e))
+* **internal:** codegen related update ([3a09c77](https://github.com/sentdm/sent-dm-java/commit/3a09c773610e5227b9131c2941e049e3832b6d1a))
+* **internal:** expand imports ([47e0e22](https://github.com/sentdm/sent-dm-java/commit/47e0e221dc542bd54edfffdccdec5f55db08f352))
+
## 0.6.0 (2026-02-18)
Full Changelog: [v0.5.1...v0.6.0](https://github.com/sentdm/sent-dm-java/compare/v0.5.1...v0.6.0)
diff --git a/README.md b/README.md
index 1f286c1a..500018f9 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/dm.sent/sent-dm-java/0.6.0)
-[](https://javadoc.io/doc/dm.sent/sent-dm-java/0.6.0)
+[](https://central.sonatype.com/artifact/dm.sent/sent-dm-java/0.6.1)
+[](https://javadoc.io/doc/dm.sent/sent-dm-java/0.6.1)
@@ -11,9 +11,18 @@ The Sent Dm Java SDK provides convenient access to the [Sent Dm REST API](https:
It is generated with [Stainless](https://www.stainless.com/).
+## MCP Server
+
+Use the Sent Dm MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
+
+[](https://cursor.com/en-US/install-mcp?name=%40sentdm%2Fsentdm-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzZW50ZG0vc2VudGRtLW1jcCJdLCJlbnYiOnsiU0VOVF9ETV9BUElfS0VZIjoiTXkgQVBJIEtleSJ9fQ)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40sentdm%2Fsentdm-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40sentdm%2Fsentdm-mcp%22%5D%2C%22env%22%3A%7B%22SENT_DM_API_KEY%22%3A%22My%20API%20Key%22%7D%7D)
+
+> Note: You may need to set environment variables in your MCP client.
+
-The REST API documentation can be found on [docs.sent.dm](https://docs.sent.dm). Javadocs are available on [javadoc.io](https://javadoc.io/doc/dm.sent/sent-dm-java/0.6.0).
+The REST API documentation can be found on [docs.sent.dm](https://docs.sent.dm). Javadocs are available on [javadoc.io](https://javadoc.io/doc/dm.sent/sent-dm-java/0.6.1).
@@ -24,7 +33,7 @@ The REST API documentation can be found on [docs.sent.dm](https://docs.sent.dm).
### Gradle
```kotlin
-implementation("dm.sent:sent-dm-java:0.6.0")
+implementation("dm.sent:sent-dm-java:0.6.1")
```
### Maven
@@ -33,7 +42,7 @@ implementation("dm.sent:sent-dm-java:0.6.0")
dm.sent
sent-dm-java
- 0.6.0
+ 0.6.1
```
@@ -391,6 +400,25 @@ SentDmClient client = SentDmOkHttpClient.builder()
.build();
```
+### Connection pooling
+
+To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:
+
+```java
+import dm.sent.client.SentDmClient;
+import dm.sent.client.okhttp.SentDmOkHttpClient;
+import java.time.Duration;
+
+SentDmClient client = SentDmOkHttpClient.builder()
+ .fromEnv()
+ // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.
+ .maxIdleConnections(10)
+ .keepAliveDuration(Duration.ofMinutes(2))
+ .build();
+```
+
+If both options are unset, OkHttp's default connection pool settings are used.
+
### HTTPS
> [!NOTE]
diff --git a/build.gradle.kts b/build.gradle.kts
index 061efe6b..06ac9f34 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "dm.sent"
- version = "0.6.0" // x-release-please-version
+ version = "0.6.1" // x-release-please-version
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/sent-dm.java.gradle.kts b/buildSrc/src/main/kotlin/sent-dm.java.gradle.kts
index 70fc33f4..8f4f902a 100644
--- a/buildSrc/src/main/kotlin/sent-dm.java.gradle.kts
+++ b/buildSrc/src/main/kotlin/sent-dm.java.gradle.kts
@@ -45,7 +45,7 @@ tasks.withType().configureEach {
val palantir by configurations.creating
dependencies {
- palantir("com.palantir.javaformat:palantir-java-format:2.73.0")
+ palantir("com.palantir.javaformat:palantir-java-format:2.89.0")
}
fun registerPalantir(
diff --git a/scripts/mock b/scripts/mock
deleted file mode 100755
index 0b28f6ea..00000000
--- a/scripts/mock
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-cd "$(dirname "$0")/.."
-
-if [[ -n "$1" && "$1" != '--'* ]]; then
- URL="$1"
- shift
-else
- URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)"
-fi
-
-# Check if the URL is empty
-if [ -z "$URL" ]; then
- echo "Error: No OpenAPI spec path/url provided or found in .stats.yml"
- exit 1
-fi
-
-echo "==> Starting mock server with URL ${URL}"
-
-# Run prism mock on the given spec
-if [ "$1" == "--daemon" ]; then
- npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log &
-
- # Wait for server to come online
- echo -n "Waiting for server"
- while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do
- echo -n "."
- sleep 0.1
- done
-
- if grep -q "✖ fatal" ".prism.log"; then
- cat .prism.log
- exit 1
- fi
-
- echo
-else
- npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL"
-fi
diff --git a/scripts/test b/scripts/test
index 047bc1db..904aea60 100755
--- a/scripts/test
+++ b/scripts/test
@@ -4,53 +4,7 @@ set -e
cd "$(dirname "$0")/.."
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[0;33m'
-NC='\033[0m' # No Color
-function prism_is_running() {
- curl --silent "http://localhost:4010" >/dev/null 2>&1
-}
-
-kill_server_on_port() {
- pids=$(lsof -t -i tcp:"$1" || echo "")
- if [ "$pids" != "" ]; then
- kill "$pids"
- echo "Stopped $pids."
- fi
-}
-
-function is_overriding_api_base_url() {
- [ -n "$TEST_API_BASE_URL" ]
-}
-
-if ! is_overriding_api_base_url && ! prism_is_running ; then
- # When we exit this script, make sure to kill the background mock server process
- trap 'kill_server_on_port 4010' EXIT
-
- # Start the dev server
- ./scripts/mock --daemon
-fi
-
-if is_overriding_api_base_url ; then
- echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}"
- echo
-elif ! prism_is_running ; then
- echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server"
- echo -e "running against your OpenAPI spec."
- echo
- echo -e "To run the server, pass in the path or url of your OpenAPI"
- echo -e "spec to the prism command:"
- echo
- echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}"
- echo
-
- exit 1
-else
- echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}"
- echo
-fi
echo "==> Running tests"
./gradlew test "$@"
diff --git a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/OkHttpClient.kt b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/OkHttpClient.kt
index d82584f0..273334a6 100644
--- a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/OkHttpClient.kt
+++ b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/OkHttpClient.kt
@@ -16,11 +16,13 @@ import java.time.Duration
import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
+import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Call
import okhttp3.Callback
+import okhttp3.ConnectionPool
import okhttp3.Dispatcher
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType
@@ -33,7 +35,7 @@ import okhttp3.logging.HttpLoggingInterceptor
import okio.BufferedSink
class OkHttpClient
-private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
+internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
val call = newCall(request, requestOptions)
@@ -200,6 +202,8 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var dispatcherExecutorService: ExecutorService? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
@@ -211,6 +215,28 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+ /**
+ * Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Sets the keep-alive duration for idle connections in the underlying [ConnectionPool].
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}
@@ -240,6 +266,22 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
.apply {
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
+ val maxIdleConnections = maxIdleConnections
+ val keepAliveDuration = keepAliveDuration
+ if (maxIdleConnections != null && keepAliveDuration != null) {
+ connectionPool(
+ ConnectionPool(
+ maxIdleConnections,
+ keepAliveDuration.toNanos(),
+ TimeUnit.NANOSECONDS,
+ )
+ )
+ } else {
+ check((maxIdleConnections != null) == (keepAliveDuration != null)) {
+ "Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set"
+ }
+ }
+
val sslSocketFactory = sslSocketFactory
val trustManager = trustManager
if (sslSocketFactory != null && trustManager != null) {
diff --git a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClient.kt b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClient.kt
index c863f2e0..f7acb5fb 100644
--- a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClient.kt
+++ b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClient.kt
@@ -47,6 +47,8 @@ class SentDmOkHttpClient private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -75,6 +77,46 @@ class SentDmOkHttpClient private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -321,6 +363,8 @@ class SentDmOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClientAsync.kt b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClientAsync.kt
index b6b13078..f19121f3 100644
--- a/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClientAsync.kt
+++ b/sent-dm-java-client-okhttp/src/main/kotlin/dm/sent/client/okhttp/SentDmOkHttpClientAsync.kt
@@ -47,6 +47,8 @@ class SentDmOkHttpClientAsync private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -75,6 +77,46 @@ class SentDmOkHttpClientAsync private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -321,6 +363,8 @@ class SentDmOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/sent-dm-java-core/build.gradle.kts b/sent-dm-java-core/build.gradle.kts
index f76bf3fb..4f6bd70d 100644
--- a/sent-dm-java-core/build.gradle.kts
+++ b/sent-dm-java-core/build.gradle.kts
@@ -27,8 +27,6 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
- implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
- implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
testImplementation(kotlin("test"))
testImplementation(project(":sent-dm-java-client-okhttp"))
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClient.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClient.kt
index 22bca60b..8182b34b 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClient.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClient.kt
@@ -50,22 +50,30 @@ interface SentDmClient {
*/
fun withOptions(modifier: Consumer): SentDmClient
+ /** Configure webhook endpoints for real-time event delivery */
fun webhooks(): WebhookService
+ /** Invite, update, and manage organization users and roles */
fun users(): UserService
+ /** Manage message templates with variable substitution */
fun templates(): TemplateService
+ /** Manage organization profiles */
fun profiles(): ProfileService
+ /** Send and track SMS and WhatsApp messages */
fun messages(): MessageService
fun lookup(): LookupService
+ /** Create, update, and manage customer contact lists */
fun contacts(): ContactService
+ /** Register and manage 10DLC brands for SMS compliance */
fun brands(): BrandService
+ /** Retrieve account details */
fun me(): MeService
/**
@@ -91,22 +99,30 @@ interface SentDmClient {
*/
fun withOptions(modifier: Consumer): SentDmClient.WithRawResponse
+ /** Configure webhook endpoints for real-time event delivery */
fun webhooks(): WebhookService.WithRawResponse
+ /** Invite, update, and manage organization users and roles */
fun users(): UserService.WithRawResponse
+ /** Manage message templates with variable substitution */
fun templates(): TemplateService.WithRawResponse
+ /** Manage organization profiles */
fun profiles(): ProfileService.WithRawResponse
+ /** Send and track SMS and WhatsApp messages */
fun messages(): MessageService.WithRawResponse
fun lookup(): LookupService.WithRawResponse
+ /** Create, update, and manage customer contact lists */
fun contacts(): ContactService.WithRawResponse
+ /** Register and manage 10DLC brands for SMS compliance */
fun brands(): BrandService.WithRawResponse
+ /** Retrieve account details */
fun me(): MeService.WithRawResponse
}
}
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsync.kt
index 1640e791..db17d7d9 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsync.kt
@@ -50,22 +50,30 @@ interface SentDmClientAsync {
*/
fun withOptions(modifier: Consumer): SentDmClientAsync
+ /** Configure webhook endpoints for real-time event delivery */
fun webhooks(): WebhookServiceAsync
+ /** Invite, update, and manage organization users and roles */
fun users(): UserServiceAsync
+ /** Manage message templates with variable substitution */
fun templates(): TemplateServiceAsync
+ /** Manage organization profiles */
fun profiles(): ProfileServiceAsync
+ /** Send and track SMS and WhatsApp messages */
fun messages(): MessageServiceAsync
fun lookup(): LookupServiceAsync
+ /** Create, update, and manage customer contact lists */
fun contacts(): ContactServiceAsync
+ /** Register and manage 10DLC brands for SMS compliance */
fun brands(): BrandServiceAsync
+ /** Retrieve account details */
fun me(): MeServiceAsync
/**
@@ -93,22 +101,30 @@ interface SentDmClientAsync {
modifier: Consumer
): SentDmClientAsync.WithRawResponse
+ /** Configure webhook endpoints for real-time event delivery */
fun webhooks(): WebhookServiceAsync.WithRawResponse
+ /** Invite, update, and manage organization users and roles */
fun users(): UserServiceAsync.WithRawResponse
+ /** Manage message templates with variable substitution */
fun templates(): TemplateServiceAsync.WithRawResponse
+ /** Manage organization profiles */
fun profiles(): ProfileServiceAsync.WithRawResponse
+ /** Send and track SMS and WhatsApp messages */
fun messages(): MessageServiceAsync.WithRawResponse
fun lookup(): LookupServiceAsync.WithRawResponse
+ /** Create, update, and manage customer contact lists */
fun contacts(): ContactServiceAsync.WithRawResponse
+ /** Register and manage 10DLC brands for SMS compliance */
fun brands(): BrandServiceAsync.WithRawResponse
+ /** Retrieve account details */
fun me(): MeServiceAsync.WithRawResponse
}
}
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsyncImpl.kt
index 251b283c..69dd8e1d 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientAsyncImpl.kt
@@ -80,22 +80,30 @@ class SentDmClientAsyncImpl(private val clientOptions: ClientOptions) : SentDmCl
override fun withOptions(modifier: Consumer): SentDmClientAsync =
SentDmClientAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /** Configure webhook endpoints for real-time event delivery */
override fun webhooks(): WebhookServiceAsync = webhooks
+ /** Invite, update, and manage organization users and roles */
override fun users(): UserServiceAsync = users
+ /** Manage message templates with variable substitution */
override fun templates(): TemplateServiceAsync = templates
+ /** Manage organization profiles */
override fun profiles(): ProfileServiceAsync = profiles
+ /** Send and track SMS and WhatsApp messages */
override fun messages(): MessageServiceAsync = messages
override fun lookup(): LookupServiceAsync = lookup
+ /** Create, update, and manage customer contact lists */
override fun contacts(): ContactServiceAsync = contacts
+ /** Register and manage 10DLC brands for SMS compliance */
override fun brands(): BrandServiceAsync = brands
+ /** Retrieve account details */
override fun me(): MeServiceAsync = me
override fun close() = clientOptions.close()
@@ -146,22 +154,30 @@ class SentDmClientAsyncImpl(private val clientOptions: ClientOptions) : SentDmCl
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /** Configure webhook endpoints for real-time event delivery */
override fun webhooks(): WebhookServiceAsync.WithRawResponse = webhooks
+ /** Invite, update, and manage organization users and roles */
override fun users(): UserServiceAsync.WithRawResponse = users
+ /** Manage message templates with variable substitution */
override fun templates(): TemplateServiceAsync.WithRawResponse = templates
+ /** Manage organization profiles */
override fun profiles(): ProfileServiceAsync.WithRawResponse = profiles
+ /** Send and track SMS and WhatsApp messages */
override fun messages(): MessageServiceAsync.WithRawResponse = messages
override fun lookup(): LookupServiceAsync.WithRawResponse = lookup
+ /** Create, update, and manage customer contact lists */
override fun contacts(): ContactServiceAsync.WithRawResponse = contacts
+ /** Register and manage 10DLC brands for SMS compliance */
override fun brands(): BrandServiceAsync.WithRawResponse = brands
+ /** Retrieve account details */
override fun me(): MeServiceAsync.WithRawResponse = me
}
}
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientImpl.kt
index f143baea..b6bcefb0 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/client/SentDmClientImpl.kt
@@ -68,22 +68,30 @@ class SentDmClientImpl(private val clientOptions: ClientOptions) : SentDmClient
override fun withOptions(modifier: Consumer): SentDmClient =
SentDmClientImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /** Configure webhook endpoints for real-time event delivery */
override fun webhooks(): WebhookService = webhooks
+ /** Invite, update, and manage organization users and roles */
override fun users(): UserService = users
+ /** Manage message templates with variable substitution */
override fun templates(): TemplateService = templates
+ /** Manage organization profiles */
override fun profiles(): ProfileService = profiles
+ /** Send and track SMS and WhatsApp messages */
override fun messages(): MessageService = messages
override fun lookup(): LookupService = lookup
+ /** Create, update, and manage customer contact lists */
override fun contacts(): ContactService = contacts
+ /** Register and manage 10DLC brands for SMS compliance */
override fun brands(): BrandService = brands
+ /** Retrieve account details */
override fun me(): MeService = me
override fun close() = clientOptions.close()
@@ -134,22 +142,30 @@ class SentDmClientImpl(private val clientOptions: ClientOptions) : SentDmClient
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /** Configure webhook endpoints for real-time event delivery */
override fun webhooks(): WebhookService.WithRawResponse = webhooks
+ /** Invite, update, and manage organization users and roles */
override fun users(): UserService.WithRawResponse = users
+ /** Manage message templates with variable substitution */
override fun templates(): TemplateService.WithRawResponse = templates
+ /** Manage organization profiles */
override fun profiles(): ProfileService.WithRawResponse = profiles
+ /** Send and track SMS and WhatsApp messages */
override fun messages(): MessageService.WithRawResponse = messages
override fun lookup(): LookupService.WithRawResponse = lookup
+ /** Create, update, and manage customer contact lists */
override fun contacts(): ContactService.WithRawResponse = contacts
+ /** Register and manage 10DLC brands for SMS compliance */
override fun brands(): BrandService.WithRawResponse = brands
+ /** Retrieve account details */
override fun me(): MeService.WithRawResponse = me
}
}
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/core/Properties.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/core/Properties.kt
index b6bd00b8..1bb1896c 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/core/Properties.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/core/Properties.kt
@@ -34,9 +34,9 @@ fun getOsName(): String {
}
}
-fun getOsVersion(): String = System.getProperty("os.version", "unknown")
+fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown"
fun getPackageVersion(): String =
- SentDmClient::class.java.`package`.implementationVersion ?: "unknown"
+ SentDmClient::class.java.`package`?.implementationVersion ?: "unknown"
-fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
+fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown"
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/HttpRequestBodies.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/HttpRequestBodies.kt
index 1d00c8c4..217c1398 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/HttpRequestBodies.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/HttpRequestBodies.kt
@@ -8,13 +8,13 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
import dm.sent.core.MultipartField
+import dm.sent.core.toImmutable
import dm.sent.errors.SentDmInvalidDataException
+import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.OutputStream
+import java.util.UUID
import kotlin.jvm.optionals.getOrNull
-import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
-import org.apache.hc.core5.http.ContentType
-import org.apache.hc.core5.http.HttpEntity
@JvmSynthetic
internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody =
@@ -37,92 +37,231 @@ internal fun multipartFormData(
jsonMapper: JsonMapper,
fields: Map>,
): HttpRequestBody =
- object : HttpRequestBody {
- private val entity: HttpEntity by lazy {
- MultipartEntityBuilder.create()
- .apply {
- fields.forEach { (name, field) ->
- val knownValue = field.value.asKnown().getOrNull()
- val parts =
- if (knownValue is InputStream) {
- // Read directly from the `InputStream` instead of reading it all
- // into memory due to the `jsonMapper` serialization below.
- sequenceOf(name to knownValue)
- } else {
- val node = jsonMapper.valueToTree(field.value)
- serializePart(name, node)
+ MultipartBody.Builder()
+ .apply {
+ fields.forEach { (name, field) ->
+ val knownValue = field.value.asKnown().getOrNull()
+ val parts =
+ if (knownValue is InputStream) {
+ // Read directly from the `InputStream` instead of reading it all
+ // into memory due to the `jsonMapper` serialization below.
+ sequenceOf(name to knownValue)
+ } else {
+ val node = jsonMapper.valueToTree(field.value)
+ serializePart(name, node)
+ }
+
+ parts.forEach { (name, bytes) ->
+ val partBody =
+ if (bytes is ByteArrayInputStream) {
+ val byteArray = bytes.readBytes()
+
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ outputStream.write(byteArray)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = byteArray.size.toLong()
+
+ override fun repeatable(): Boolean = true
+
+ override fun close() {}
}
+ } else {
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ bytes.copyTo(outputStream)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = -1L
- parts.forEach { (name, bytes) ->
- addBinaryBody(
- name,
- bytes,
- ContentType.parseLenient(field.contentType),
- field.filename().getOrNull(),
- )
+ override fun repeatable(): Boolean = false
+
+ override fun close() = bytes.close()
+ }
}
- }
+
+ addPart(
+ MultipartBody.Part.create(
+ name,
+ field.filename().getOrNull(),
+ field.contentType,
+ partBody,
+ )
+ )
}
- .build()
+ }
}
+ .build()
- private fun serializePart(
- name: String,
- node: JsonNode,
- ): Sequence> =
- when (node.nodeType) {
- JsonNodeType.MISSING,
- JsonNodeType.NULL -> emptySequence()
- JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
- JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream())
- JsonNodeType.BOOLEAN ->
- sequenceOf(name to node.booleanValue().toString().inputStream())
- JsonNodeType.NUMBER ->
- sequenceOf(name to node.numberValue().toString().inputStream())
- JsonNodeType.ARRAY ->
- sequenceOf(
- name to
- node
- .elements()
- .asSequence()
- .mapNotNull { element ->
- when (element.nodeType) {
- JsonNodeType.MISSING,
- JsonNodeType.NULL -> null
- JsonNodeType.STRING -> node.textValue()
- JsonNodeType.BOOLEAN -> node.booleanValue().toString()
- JsonNodeType.NUMBER -> node.numberValue().toString()
- null,
- JsonNodeType.BINARY,
- JsonNodeType.ARRAY,
- JsonNodeType.OBJECT,
- JsonNodeType.POJO ->
- throw SentDmInvalidDataException(
- "Unexpected JsonNode type in array: ${node.nodeType}"
- )
- }
- }
- .joinToString(",")
- .inputStream()
- )
- JsonNodeType.OBJECT ->
- node.fields().asSequence().flatMap { (key, value) ->
- serializePart("$name[$key]", value)
- }
- JsonNodeType.POJO,
- null ->
- throw SentDmInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+private fun serializePart(name: String, node: JsonNode): Sequence> =
+ when (node.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> emptySequence()
+ JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
+ JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream())
+ JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream())
+ JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream())
+ JsonNodeType.ARRAY ->
+ sequenceOf(
+ name to
+ node
+ .elements()
+ .asSequence()
+ .mapNotNull { element ->
+ when (element.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> null
+ JsonNodeType.STRING -> element.textValue()
+ JsonNodeType.BOOLEAN -> element.booleanValue().toString()
+ JsonNodeType.NUMBER -> element.numberValue().toString()
+ null,
+ JsonNodeType.BINARY,
+ JsonNodeType.ARRAY,
+ JsonNodeType.OBJECT,
+ JsonNodeType.POJO ->
+ throw SentDmInvalidDataException(
+ "Unexpected JsonNode type in array: ${element.nodeType}"
+ )
+ }
+ }
+ .joinToString(",")
+ .byteInputStream()
+ )
+ JsonNodeType.OBJECT ->
+ node.fields().asSequence().flatMap { (key, value) ->
+ serializePart("$name[$key]", value)
+ }
+ JsonNodeType.POJO,
+ null -> throw SentDmInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+ }
+
+private class MultipartBody
+private constructor(private val boundary: String, private val parts: List) : HttpRequestBody {
+ private val boundaryBytes: ByteArray = boundary.toByteArray()
+ private val contentType = "multipart/form-data; boundary=$boundary"
+
+ // This must remain in sync with `contentLength`.
+ override fun writeTo(outputStream: OutputStream) {
+ parts.forEach { part ->
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_DISPOSITION)
+ outputStream.write(part.contentDisposition.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_TYPE)
+ outputStream.write(part.contentType.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CRLF)
+ part.body.writeTo(outputStream)
+ outputStream.write(CRLF)
+ }
+
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(DASHDASH)
+ outputStream.write(CRLF)
+ }
+
+ override fun contentType(): String = contentType
+
+ // This must remain in sync with `writeTo`.
+ override fun contentLength(): Long {
+ var byteCount = 0L
+
+ parts.forEach { part ->
+ val contentLength = part.body.contentLength()
+ if (contentLength == -1L) {
+ return -1L
}
- private fun String.inputStream(): InputStream = toByteArray().inputStream()
+ byteCount +=
+ DASHDASH.size +
+ boundaryBytes.size +
+ CRLF.size +
+ CONTENT_DISPOSITION.size +
+ part.contentDisposition.toByteArray().size +
+ CRLF.size +
+ CONTENT_TYPE.size +
+ part.contentType.toByteArray().size +
+ CRLF.size +
+ CRLF.size +
+ contentLength +
+ CRLF.size
+ }
- override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)
+ byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size
+ return byteCount
+ }
- override fun contentType(): String = entity.contentType
+ override fun repeatable(): Boolean = parts.all { it.body.repeatable() }
- override fun contentLength(): Long = entity.contentLength
+ override fun close() {
+ parts.forEach { it.body.close() }
+ }
- override fun repeatable(): Boolean = entity.isRepeatable
+ class Builder {
+ private val boundary = UUID.randomUUID().toString()
+ private val parts: MutableList = mutableListOf()
- override fun close() = entity.close()
+ fun addPart(part: Part) = apply { parts.add(part) }
+
+ fun build() = MultipartBody(boundary, parts.toImmutable())
+ }
+
+ class Part
+ private constructor(
+ val contentDisposition: String,
+ val contentType: String,
+ val body: HttpRequestBody,
+ ) {
+ companion object {
+ fun create(
+ name: String,
+ filename: String?,
+ contentType: String,
+ body: HttpRequestBody,
+ ): Part {
+ val disposition = buildString {
+ append("form-data; name=")
+ appendQuotedString(name)
+ if (filename != null) {
+ append("; filename=")
+ appendQuotedString(filename)
+ }
+ }
+ return Part(disposition, contentType, body)
+ }
+ }
+ }
+
+ companion object {
+ private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
+ private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())
+ private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray()
+ private val CONTENT_TYPE = "Content-Type: ".toByteArray()
+
+ private fun StringBuilder.appendQuotedString(key: String) {
+ append('"')
+ for (ch in key) {
+ when (ch) {
+ '\n' -> append("%0A")
+ '\r' -> append("%0D")
+ '"' -> append("%22")
+ else -> append(ch)
+ }
+ }
+ append('"')
+ }
}
+}
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/RetryingHttpClient.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/RetryingHttpClient.kt
index 6a3fdc46..e1108abd 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/RetryingHttpClient.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/core/http/RetryingHttpClient.kt
@@ -1,3 +1,5 @@
+// File generated from our OpenAPI spec by Stainless.
+
package dm.sent.core.http
import dm.sent.core.DefaultSleeper
@@ -199,7 +201,7 @@ private constructor(
?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter ->
retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1))
?: try {
- ChronoUnit.MILLIS.between(
+ ChronoUnit.NANOS.between(
OffsetDateTime.now(clock),
OffsetDateTime.parse(
retryAfter,
@@ -212,13 +214,8 @@ private constructor(
}
}
?.let { retryAfterNanos ->
- // If the API asks us to wait a certain amount of time (and it's a reasonable
- // amount), just
- // do what it says.
- val retryAfter = Duration.ofNanos(retryAfterNanos.toLong())
- if (retryAfter in Duration.ofNanos(0)..Duration.ofMinutes(1)) {
- return retryAfter
- }
+ // If the API asks us to wait a certain amount of time, do what it says.
+ return Duration.ofNanos(retryAfterNanos.toLong())
}
// Apply exponential backoff, but not more than the max.
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/models/lookup/LookupRetrievePhoneInfoResponse.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/models/lookup/LookupRetrievePhoneInfoResponse.kt
index f6576a88..619a89ac 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/models/lookup/LookupRetrievePhoneInfoResponse.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/models/lookup/LookupRetrievePhoneInfoResponse.kt
@@ -262,7 +262,7 @@ private constructor(
private constructor(
private val carrierName: JsonField,
private val isPorted: JsonField,
- private val isValid: JsonField,
+ private val isValid_: JsonField,
private val isVoip: JsonField,
private val lineType: JsonField,
private val mobileCountryCode: JsonField,
@@ -280,7 +280,9 @@ private constructor(
@JsonProperty("isPorted")
@ExcludeMissing
isPorted: JsonField = JsonMissing.of(),
- @JsonProperty("isValid") @ExcludeMissing isValid: JsonField = JsonMissing.of(),
+ @JsonProperty("isValid")
+ @ExcludeMissing
+ isValid_: JsonField = JsonMissing.of(),
@JsonProperty("isVoip") @ExcludeMissing isVoip: JsonField = JsonMissing.of(),
@JsonProperty("lineType")
@ExcludeMissing
@@ -298,7 +300,7 @@ private constructor(
) : this(
carrierName,
isPorted,
- isValid,
+ isValid_,
isVoip,
lineType,
mobileCountryCode,
@@ -324,7 +326,7 @@ private constructor(
* @throws SentDmInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
- fun isValid(): Optional = isValid.getOptional("isValid")
+ fun isValid_(): Optional = isValid_.getOptional("isValid")
/**
* @throws SentDmInvalidDataException if the JSON field has an unexpected type (e.g. if the
@@ -381,11 +383,11 @@ private constructor(
@JsonProperty("isPorted") @ExcludeMissing fun _isPorted(): JsonField = isPorted
/**
- * Returns the raw JSON value of [isValid].
+ * Returns the raw JSON value of [isValid_].
*
- * Unlike [isValid], this method doesn't throw if the JSON field has an unexpected type.
+ * Unlike [isValid_], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("isValid") @ExcludeMissing fun _isValid(): JsonField = isValid
+ @JsonProperty("isValid") @ExcludeMissing fun _isValid_(): JsonField = isValid_
/**
* Returns the raw JSON value of [isVoip].
@@ -460,7 +462,7 @@ private constructor(
private var carrierName: JsonField = JsonMissing.of()
private var isPorted: JsonField = JsonMissing.of()
- private var isValid: JsonField = JsonMissing.of()
+ private var isValid_: JsonField = JsonMissing.of()
private var isVoip: JsonField = JsonMissing.of()
private var lineType: JsonField = JsonMissing.of()
private var mobileCountryCode: JsonField = JsonMissing.of()
@@ -473,7 +475,7 @@ private constructor(
internal fun from(data: Data) = apply {
carrierName = data.carrierName
isPorted = data.isPorted
- isValid = data.isValid
+ isValid_ = data.isValid_
isVoip = data.isVoip
lineType = data.lineType
mobileCountryCode = data.mobileCountryCode
@@ -520,16 +522,16 @@ private constructor(
*/
fun isPorted(isPorted: JsonField) = apply { this.isPorted = isPorted }
- fun isValid(isValid: Boolean) = isValid(JsonField.of(isValid))
+ fun isValid_(isValid_: Boolean) = isValid_(JsonField.of(isValid_))
/**
- * Sets [Builder.isValid] to an arbitrary JSON value.
+ * Sets [Builder.isValid_] to an arbitrary JSON value.
*
- * You should usually call [Builder.isValid] with a well-typed [Boolean] value instead.
+ * You should usually call [Builder.isValid_] with a well-typed [Boolean] value instead.
* This method is primarily for setting the field to an undocumented or not yet
* supported value.
*/
- fun isValid(isValid: JsonField) = apply { this.isValid = isValid }
+ fun isValid_(isValid_: JsonField) = apply { this.isValid_ = isValid_ }
fun isVoip(isVoip: Boolean?) = isVoip(JsonField.ofNullable(isVoip))
@@ -658,7 +660,7 @@ private constructor(
Data(
carrierName,
isPorted,
- isValid,
+ isValid_,
isVoip,
lineType,
mobileCountryCode,
@@ -678,7 +680,7 @@ private constructor(
carrierName()
isPorted()
- isValid()
+ isValid_()
isVoip()
lineType()
mobileCountryCode()
@@ -706,7 +708,7 @@ private constructor(
internal fun validity(): Int =
(if (carrierName.asKnown().isPresent) 1 else 0) +
(if (isPorted.asKnown().isPresent) 1 else 0) +
- (if (isValid.asKnown().isPresent) 1 else 0) +
+ (if (isValid_.asKnown().isPresent) 1 else 0) +
(if (isVoip.asKnown().isPresent) 1 else 0) +
(if (lineType.asKnown().isPresent) 1 else 0) +
(if (mobileCountryCode.asKnown().isPresent) 1 else 0) +
@@ -722,7 +724,7 @@ private constructor(
return other is Data &&
carrierName == other.carrierName &&
isPorted == other.isPorted &&
- isValid == other.isValid &&
+ isValid_ == other.isValid_ &&
isVoip == other.isVoip &&
lineType == other.lineType &&
mobileCountryCode == other.mobileCountryCode &&
@@ -736,7 +738,7 @@ private constructor(
Objects.hash(
carrierName,
isPorted,
- isValid,
+ isValid_,
isVoip,
lineType,
mobileCountryCode,
@@ -750,7 +752,7 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "Data{carrierName=$carrierName, isPorted=$isPorted, isValid=$isValid, isVoip=$isVoip, lineType=$lineType, mobileCountryCode=$mobileCountryCode, mobileNetworkCode=$mobileNetworkCode, phoneNumber=$phoneNumber, provider=$provider, additionalProperties=$additionalProperties}"
+ "Data{carrierName=$carrierName, isPorted=$isPorted, isValid_=$isValid_, isVoip=$isVoip, lineType=$lineType, mobileCountryCode=$mobileCountryCode, mobileNetworkCode=$mobileNetworkCode, phoneNumber=$phoneNumber, provider=$provider, additionalProperties=$additionalProperties}"
}
override fun equals(other: Any?): Boolean {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsync.kt
index 70aa4300..19a6024c 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsync.kt
@@ -16,6 +16,7 @@ import dm.sent.services.async.brands.CampaignServiceAsync
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Register and manage 10DLC brands for SMS compliance */
interface BrandServiceAsync {
/**
@@ -30,6 +31,7 @@ interface BrandServiceAsync {
*/
fun withOptions(modifier: Consumer): BrandServiceAsync
+ /** Register and manage 10DLC brands for SMS compliance */
fun campaigns(): CampaignServiceAsync
/**
@@ -137,6 +139,7 @@ interface BrandServiceAsync {
modifier: Consumer
): BrandServiceAsync.WithRawResponse
+ /** Register and manage 10DLC brands for SMS compliance */
fun campaigns(): CampaignServiceAsync.WithRawResponse
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsyncImpl.kt
index 1f9fe260..93c17b6e 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/BrandServiceAsyncImpl.kt
@@ -29,6 +29,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Register and manage 10DLC brands for SMS compliance */
class BrandServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
BrandServiceAsync {
@@ -43,6 +44,7 @@ class BrandServiceAsyncImpl internal constructor(private val clientOptions: Clie
override fun withOptions(modifier: Consumer): BrandServiceAsync =
BrandServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /** Register and manage 10DLC brands for SMS compliance */
override fun campaigns(): CampaignServiceAsync = campaigns
override fun create(
@@ -90,6 +92,7 @@ class BrandServiceAsyncImpl internal constructor(private val clientOptions: Clie
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /** Register and manage 10DLC brands for SMS compliance */
override fun campaigns(): CampaignServiceAsync.WithRawResponse = campaigns
private val createHandler: Handler =
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsync.kt
index 0b81bbc9..24069244 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsync.kt
@@ -16,6 +16,7 @@ import dm.sent.models.contacts.ContactUpdateParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Create, update, and manage customer contact lists */
interface ContactServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsyncImpl.kt
index 588214ee..c2fdef33 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ContactServiceAsyncImpl.kt
@@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Create, update, and manage customer contact lists */
class ContactServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
ContactServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsync.kt
index c9be9bc9..fd1ba9e8 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsync.kt
@@ -10,6 +10,7 @@ import dm.sent.models.me.MeRetrieveResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Retrieve account details */
interface MeServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsyncImpl.kt
index adfe06db..773a2dd1 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MeServiceAsyncImpl.kt
@@ -19,6 +19,7 @@ import dm.sent.models.me.MeRetrieveResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Retrieve account details */
class MeServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
MeServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsync.kt
index ae47c86e..2576b628 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsync.kt
@@ -14,6 +14,7 @@ import dm.sent.models.messages.MessageSendResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Send and track SMS and WhatsApp messages */
interface MessageServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsyncImpl.kt
index 5d73261d..4d58b4c0 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/MessageServiceAsyncImpl.kt
@@ -26,6 +26,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Send and track SMS and WhatsApp messages */
class MessageServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
MessageServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsync.kt
index 27c4a5a0..015cc343 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsync.kt
@@ -18,6 +18,7 @@ import dm.sent.models.profiles.ProfileUpdateParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Manage organization profiles */
interface ProfileServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsyncImpl.kt
index d56ea86a..7db9d847 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/ProfileServiceAsyncImpl.kt
@@ -30,6 +30,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Manage organization profiles */
class ProfileServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
ProfileServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsync.kt
index 898cfc2c..4f3ba61c 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsync.kt
@@ -16,6 +16,7 @@ import dm.sent.models.templates.TemplateUpdateParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Manage message templates with variable substitution */
interface TemplateServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsyncImpl.kt
index f89dbe45..71b781c6 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/TemplateServiceAsyncImpl.kt
@@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Manage message templates with variable substitution */
class TemplateServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
TemplateServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsync.kt
index 08973b28..12f4c210 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsync.kt
@@ -16,6 +16,7 @@ import dm.sent.models.users.UserUpdateRoleParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Invite, update, and manage organization users and roles */
interface UserServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsyncImpl.kt
index 5f6d1d6f..e7f09c89 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/UserServiceAsyncImpl.kt
@@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Invite, update, and manage organization users and roles */
class UserServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
UserServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsync.kt
index 63b3f3ad..e9163338 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsync.kt
@@ -25,6 +25,7 @@ import dm.sent.models.webhooks.WebhookUpdateParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Configure webhook endpoints for real-time event delivery */
interface WebhookServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsyncImpl.kt
index 5168b7d3..7730677c 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/WebhookServiceAsyncImpl.kt
@@ -37,6 +37,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Configure webhook endpoints for real-time event delivery */
class WebhookServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
WebhookServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsync.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsync.kt
index eeb21766..927985bd 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsync.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsync.kt
@@ -15,6 +15,7 @@ import dm.sent.models.brands.campaigns.CampaignUpdateParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Register and manage 10DLC brands for SMS compliance */
interface CampaignServiceAsync {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsyncImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsyncImpl.kt
index 74bf07c4..49a07519 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsyncImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/async/brands/CampaignServiceAsyncImpl.kt
@@ -27,6 +27,7 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Register and manage 10DLC brands for SMS compliance */
class CampaignServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
CampaignServiceAsync {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandService.kt
index 54a6d3b4..6242a96e 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandService.kt
@@ -16,6 +16,7 @@ import dm.sent.models.brands.BrandUpdateParams
import dm.sent.services.blocking.brands.CampaignService
import java.util.function.Consumer
+/** Register and manage 10DLC brands for SMS compliance */
interface BrandService {
/**
@@ -30,6 +31,7 @@ interface BrandService {
*/
fun withOptions(modifier: Consumer): BrandService
+ /** Register and manage 10DLC brands for SMS compliance */
fun campaigns(): CampaignService
/**
@@ -123,6 +125,7 @@ interface BrandService {
*/
fun withOptions(modifier: Consumer): BrandService.WithRawResponse
+ /** Register and manage 10DLC brands for SMS compliance */
fun campaigns(): CampaignService.WithRawResponse
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandServiceImpl.kt
index dd020433..b62e19e0 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/BrandServiceImpl.kt
@@ -28,6 +28,7 @@ import dm.sent.services.blocking.brands.CampaignServiceImpl
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Register and manage 10DLC brands for SMS compliance */
class BrandServiceImpl internal constructor(private val clientOptions: ClientOptions) :
BrandService {
@@ -42,6 +43,7 @@ class BrandServiceImpl internal constructor(private val clientOptions: ClientOpt
override fun withOptions(modifier: Consumer): BrandService =
BrandServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /** Register and manage 10DLC brands for SMS compliance */
override fun campaigns(): CampaignService = campaigns
override fun create(
@@ -84,6 +86,7 @@ class BrandServiceImpl internal constructor(private val clientOptions: ClientOpt
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /** Register and manage 10DLC brands for SMS compliance */
override fun campaigns(): CampaignService.WithRawResponse = campaigns
private val createHandler: Handler =
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactService.kt
index c3151db9..970ddfe2 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactService.kt
@@ -16,6 +16,7 @@ import dm.sent.models.contacts.ContactRetrieveParams
import dm.sent.models.contacts.ContactUpdateParams
import java.util.function.Consumer
+/** Create, update, and manage customer contact lists */
interface ContactService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactServiceImpl.kt
index caf5758e..f790b5dd 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ContactServiceImpl.kt
@@ -27,6 +27,7 @@ import dm.sent.models.contacts.ContactUpdateParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Create, update, and manage customer contact lists */
class ContactServiceImpl internal constructor(private val clientOptions: ClientOptions) :
ContactService {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeService.kt
index 9ac4bf34..6428de30 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeService.kt
@@ -10,6 +10,7 @@ import dm.sent.models.me.MeRetrieveParams
import dm.sent.models.me.MeRetrieveResponse
import java.util.function.Consumer
+/** Retrieve account details */
interface MeService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeServiceImpl.kt
index b974022d..09f066eb 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MeServiceImpl.kt
@@ -18,6 +18,7 @@ import dm.sent.models.me.MeRetrieveParams
import dm.sent.models.me.MeRetrieveResponse
import java.util.function.Consumer
+/** Retrieve account details */
class MeServiceImpl internal constructor(private val clientOptions: ClientOptions) : MeService {
private val withRawResponse: MeService.WithRawResponse by lazy {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageService.kt
index 8390fc38..7584db9f 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageService.kt
@@ -14,6 +14,7 @@ import dm.sent.models.messages.MessageSendParams
import dm.sent.models.messages.MessageSendResponse
import java.util.function.Consumer
+/** Send and track SMS and WhatsApp messages */
interface MessageService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageServiceImpl.kt
index 4b8bad57..658a9e11 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/MessageServiceImpl.kt
@@ -25,6 +25,7 @@ import dm.sent.models.messages.MessageSendResponse
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Send and track SMS and WhatsApp messages */
class MessageServiceImpl internal constructor(private val clientOptions: ClientOptions) :
MessageService {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileService.kt
index 08f4231f..c729e750 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileService.kt
@@ -18,6 +18,7 @@ import dm.sent.models.profiles.ProfileRetrieveParams
import dm.sent.models.profiles.ProfileUpdateParams
import java.util.function.Consumer
+/** Manage organization profiles */
interface ProfileService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileServiceImpl.kt
index 18db3201..26d8f150 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/ProfileServiceImpl.kt
@@ -29,6 +29,7 @@ import dm.sent.models.profiles.ProfileUpdateParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Manage organization profiles */
class ProfileServiceImpl internal constructor(private val clientOptions: ClientOptions) :
ProfileService {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateService.kt
index 36941dab..c319506b 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateService.kt
@@ -16,6 +16,7 @@ import dm.sent.models.templates.TemplateRetrieveParams
import dm.sent.models.templates.TemplateUpdateParams
import java.util.function.Consumer
+/** Manage message templates with variable substitution */
interface TemplateService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateServiceImpl.kt
index 1eac8393..d5e055b3 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/TemplateServiceImpl.kt
@@ -27,6 +27,7 @@ import dm.sent.models.templates.TemplateUpdateParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Manage message templates with variable substitution */
class TemplateServiceImpl internal constructor(private val clientOptions: ClientOptions) :
TemplateService {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserService.kt
index 683678bf..630b4a7f 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserService.kt
@@ -16,6 +16,7 @@ import dm.sent.models.users.UserRetrieveParams
import dm.sent.models.users.UserUpdateRoleParams
import java.util.function.Consumer
+/** Invite, update, and manage organization users and roles */
interface UserService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserServiceImpl.kt
index 864a46db..aa9d1caf 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/UserServiceImpl.kt
@@ -27,6 +27,7 @@ import dm.sent.models.users.UserUpdateRoleParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Invite, update, and manage organization users and roles */
class UserServiceImpl internal constructor(private val clientOptions: ClientOptions) : UserService {
private val withRawResponse: UserService.WithRawResponse by lazy {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookService.kt
index 02bf8ebf..38637d9a 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookService.kt
@@ -25,6 +25,7 @@ import dm.sent.models.webhooks.WebhookToggleStatusParams
import dm.sent.models.webhooks.WebhookUpdateParams
import java.util.function.Consumer
+/** Configure webhook endpoints for real-time event delivery */
interface WebhookService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookServiceImpl.kt
index 8497cda4..a68aa9ba 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/WebhookServiceImpl.kt
@@ -36,6 +36,7 @@ import dm.sent.models.webhooks.WebhookUpdateParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Configure webhook endpoints for real-time event delivery */
class WebhookServiceImpl internal constructor(private val clientOptions: ClientOptions) :
WebhookService {
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignService.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignService.kt
index b20e8919..12f10166 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignService.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignService.kt
@@ -15,6 +15,7 @@ import dm.sent.models.brands.campaigns.CampaignListResponse
import dm.sent.models.brands.campaigns.CampaignUpdateParams
import java.util.function.Consumer
+/** Register and manage 10DLC brands for SMS compliance */
interface CampaignService {
/**
diff --git a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignServiceImpl.kt b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignServiceImpl.kt
index 925094d7..6fb1aecc 100644
--- a/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignServiceImpl.kt
+++ b/sent-dm-java-core/src/main/kotlin/dm/sent/services/blocking/brands/CampaignServiceImpl.kt
@@ -26,6 +26,7 @@ import dm.sent.models.brands.campaigns.CampaignUpdateParams
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/** Register and manage 10DLC brands for SMS compliance */
class CampaignServiceImpl internal constructor(private val clientOptions: ClientOptions) :
CampaignService {
diff --git a/sent-dm-java-core/src/test/kotlin/dm/sent/TestServerExtension.kt b/sent-dm-java-core/src/test/kotlin/dm/sent/TestServerExtension.kt
deleted file mode 100644
index 07df7758..00000000
--- a/sent-dm-java-core/src/test/kotlin/dm/sent/TestServerExtension.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package dm.sent
-
-import java.lang.RuntimeException
-import java.net.URL
-import org.junit.jupiter.api.extension.BeforeAllCallback
-import org.junit.jupiter.api.extension.ConditionEvaluationResult
-import org.junit.jupiter.api.extension.ExecutionCondition
-import org.junit.jupiter.api.extension.ExtensionContext
-
-class TestServerExtension : BeforeAllCallback, ExecutionCondition {
-
- override fun beforeAll(context: ExtensionContext?) {
- try {
- URL(BASE_URL).openConnection().connect()
- } catch (e: Exception) {
- throw RuntimeException(
- """
- The test suite will not run without a mock server running against your OpenAPI spec.
-
- You can set the environment variable `SKIP_MOCK_TESTS` to `true` to skip running any tests
- that require the mock server.
-
- To fix run `./scripts/mock` in a separate terminal.
- """
- .trimIndent(),
- e,
- )
- }
- }
-
- override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult {
- return if (System.getenv(SKIP_TESTS_ENV).toBoolean()) {
- ConditionEvaluationResult.disabled(
- "Environment variable $SKIP_TESTS_ENV is set to true"
- )
- } else {
- ConditionEvaluationResult.enabled(
- "Environment variable $SKIP_TESTS_ENV is not set to true"
- )
- }
- }
-
- companion object {
-
- val BASE_URL = System.getenv("TEST_API_BASE_URL") ?: "http://localhost:4010"
-
- const val SKIP_TESTS_ENV: String = "SKIP_MOCK_TESTS"
- }
-}
diff --git a/sent-dm-java-core/src/test/kotlin/dm/sent/core/http/HttpRequestBodiesTest.kt b/sent-dm-java-core/src/test/kotlin/dm/sent/core/http/HttpRequestBodiesTest.kt
new file mode 100644
index 00000000..a3ffe24b
--- /dev/null
+++ b/sent-dm-java-core/src/test/kotlin/dm/sent/core/http/HttpRequestBodiesTest.kt
@@ -0,0 +1,729 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package dm.sent.core.http
+
+import dm.sent.core.MultipartField
+import dm.sent.core.jsonMapper
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class HttpRequestBodiesTest {
+
+ @Test
+ fun multipartFormData_serializesFieldWithFilename() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "file" to
+ MultipartField.builder()
+ .value("hello")
+ .filename("hello.txt")
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(output.size().toLong()).isEqualTo(body.contentLength())
+ val boundary = body.contentType()!!.substringAfter("multipart/form-data; boundary=")
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="file"; filename="hello.txt"
+ |Content-Type: text/plain
+ |
+ |hello
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesFieldWithoutFilename() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "field" to
+ MultipartField.builder()
+ .value("value")
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(output.size().toLong()).isEqualTo(body.contentLength())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="field"
+ |Content-Type: text/plain
+ |
+ |value
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesInputStream() {
+ // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code
+ // path.
+ val inputStream = "stream content".byteInputStream().buffered()
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "data" to
+ MultipartField.builder()
+ .value(inputStream)
+ .contentType("application/octet-stream")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isFalse()
+ assertThat(body.contentLength()).isEqualTo(-1L)
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="data"
+ |Content-Type: application/octet-stream
+ |
+ |stream content
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesByteArray() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "binary" to
+ MultipartField.builder()
+ .value("abc".toByteArray())
+ .contentType("application/octet-stream")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="binary"
+ |Content-Type: application/octet-stream
+ |
+ |abc
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesBooleanValue() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "flag" to
+ MultipartField.builder()
+ .value(true)
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="flag"
+ |Content-Type: text/plain
+ |
+ |true
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesNumberValue() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "count" to
+ MultipartField.builder().value(42).contentType("text/plain").build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="count"
+ |Content-Type: text/plain
+ |
+ |42
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesNullValueAsNoParts() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "present" to
+ MultipartField.builder()
+ .value("yes")
+ .contentType("text/plain")
+ .build(),
+ "absent" to
+ MultipartField.builder()
+ .value(null as String?)
+ .contentType("text/plain")
+ .build(),
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="present"
+ |Content-Type: text/plain
+ |
+ |yes
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesArray() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "items" to
+ MultipartField.builder>()
+ .value(listOf("alpha", "beta", "gamma"))
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="items"
+ |Content-Type: text/plain
+ |
+ |alpha,beta,gamma
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesObjectAsNestedParts() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "meta" to
+ MultipartField.builder