Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sdk-core/api/sdk-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public abstract interface class org/dexpace/sdk/core/generics/Builder {
public abstract fun build ()Ljava/lang/Object;
}

public final class org/dexpace/sdk/core/generics/BuilderChecksKt {
public static final fun checkRequired (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
}

public final class org/dexpace/sdk/core/http/auth/AuthChallengeParser {
public static final field INSTANCE Lorg/dexpace/sdk/core/http/auth/AuthChallengeParser;
public static final fun parse (Ljava/lang/String;)Ljava/util/List;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/

package org.dexpace.sdk.core.generics

/**
* Asserts that a required builder field [value] has been set, returning it non-null.
*
* Builders that materialize an immutable value in [Builder.build] use this to validate
* mandatory inputs in one uniform place. When [value] is `null` it throws
* [IllegalStateException] with the message `"<name> is required"`, where [name] is the
* caller-supplied field name (typically the builder property, e.g. `"method"`); when
* [value] is present it is returned as a non-null reference so the result can be assigned
* directly:
*
* ```
* override fun build(): Request =
* Request(
* method = checkRequired("method", method),
* url = checkRequired("url", url),
* // ...
* )
* ```
*
* Replacing the ad-hoc `checkNotNull(field) { "Field is required." }` calls scattered
* across builders with this helper keeps the missing-field diagnostics identical, which
* matters most once builders are emitted by codegen.
*
* @param name The field name to report in the failure message.
* @param value The field value to validate.
* @return [value], guaranteed non-null.
* @throws IllegalStateException If [value] is `null`.
*/
public fun <T : Any> checkRequired(
name: String,
value: T?,
): T = checkNotNull(value) { "$name is required" }
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package org.dexpace.sdk.core.http.request

import org.dexpace.sdk.core.generics.Builder
import org.dexpace.sdk.core.generics.checkRequired
import org.dexpace.sdk.core.http.common.Headers
import org.dexpace.sdk.core.http.common.HttpHeaderName
import java.net.MalformedURLException
Expand Down Expand Up @@ -251,8 +252,8 @@ public data class Request private constructor(
*/
override fun build(): Request =
Request(
method = checkNotNull(method) { "Method is required." },
url = checkNotNull(url) { "URL is required." },
method = checkRequired("method", method),
url = checkRequired("url", url),
headers = headersBuilder.build(),
body = body,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package org.dexpace.sdk.core.http.response

import org.dexpace.sdk.core.generics.Builder
import org.dexpace.sdk.core.generics.checkRequired
import org.dexpace.sdk.core.http.common.Headers
import org.dexpace.sdk.core.http.common.HttpHeaderName
import org.dexpace.sdk.core.http.common.Protocol
Expand Down Expand Up @@ -252,9 +253,9 @@ public data class Response private constructor(
*/
override fun build(): Response =
Response(
request = checkNotNull(request) { "request is required" },
protocol = checkNotNull(protocol) { "protocol is required" },
status = checkNotNull(status) { "status is required" },
request = checkRequired("request", request),
protocol = checkRequired("protocol", protocol),
status = checkRequired("status", status),
message = message,
headers = headersBuilder.build(),
body = body,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/

package org.dexpace.sdk.core.generics

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertSame

class BuilderChecksTest {
@Test
fun `returns the value when present`() {
val value = "GET"
assertEquals(value, checkRequired("method", value))
}

@Test
fun `returns the same instance when present`() {
val value = listOf(1, 2, 3)
assertSame(value, checkRequired("items", value))
}

@Test
fun `throws IllegalStateException naming the field when value is null`() {
val ex =
assertFailsWith<IllegalStateException> {
checkRequired<String>("method", null)
}
assertEquals("method is required", ex.message)
}

@Test
fun `message uses the supplied field name verbatim`() {
val ex =
assertFailsWith<IllegalStateException> {
checkRequired<Int>("status", null)
}
assertEquals("status is required", ex.message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class RequestTest {
assertFailsWith<IllegalStateException> {
Request.builder().url("https://example.test").build()
}
assertEquals("Method is required.", ex.message)
assertEquals("method is required", ex.message)
}

@Test
Expand All @@ -216,7 +216,7 @@ class RequestTest {
assertFailsWith<IllegalStateException> {
Request.builder().method(Method.GET).build()
}
assertEquals("URL is required.", ex.message)
assertEquals("url is required", ex.message)
}

// ---------------------------------------------------------------------
Expand Down
Loading