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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ package org.onebusaway.core
fun <T : Any> checkRequired(name: String, value: T?): T =
checkNotNull(value) { "`$name` is required, but was not set" }

internal fun <T : Any> checkKnown(name: String, value: JsonField<T>): T =
value.asKnown()
?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}")

internal fun <T : Any> checkKnown(name: String, value: MultipartField<T>): T =
value.value.asKnown()
?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}")

internal fun checkLength(name: String, value: String, length: Int): String =
value.also {
check(it.length == length) { "`$name` must have length $length, but was ${it.length}" }
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ import com.fasterxml.jackson.databind.node.JsonNodeType.POJO
import com.fasterxml.jackson.databind.node.JsonNodeType.STRING
import com.fasterxml.jackson.databind.ser.std.NullSerializer
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import java.nio.charset.Charset
import java.util.Objects
import kotlin.reflect.KClass
import org.apache.hc.core5.http.ContentType
import org.onebusaway.errors.OnebusawaySdkInvalidDataException

@JsonDeserialize(using = JsonField.Deserializer::class)
Expand Down Expand Up @@ -281,12 +279,12 @@ private constructor(
return true
}

return other is KnownValue<*> && value == other.value
return other is KnownValue<*> && value contentEquals other.value
}

override fun hashCode() = value.hashCode()
override fun hashCode() = contentHash(value)

override fun toString() = value.toString()
override fun toString() = value.contentToString()

companion object {
@JsonCreator fun <T : Any> of(value: T) = KnownValue(value)
Expand Down Expand Up @@ -454,15 +452,54 @@ annotation class ExcludeMissing
)
annotation class NoAutoDetect

class MultipartFormValue<T>
internal constructor(
val name: String,
val value: T,
val contentType: ContentType,
val filename: String? = null,
) {
class MultipartField<T : Any>
private constructor(val value: JsonField<T>, val contentType: String, val filename: String?) {

companion object {

fun <T : Any> of(value: T?) = builder<T>().value(value).build()

fun <T : Any> of(value: JsonField<T>) = builder<T>().value(value).build()

fun <T : Any> builder() = Builder<T>()
}

internal fun <R : Any> map(transform: (T) -> R): MultipartField<R> =
MultipartField.builder<R>()
.value(value.map(transform))
.contentType(contentType)
.filename(filename)
.build()

/** A builder for [MultipartField]. */
class Builder<T : Any> internal constructor() {

private var value: JsonField<T>? = null
private var contentType: String? = null
private var filename: String? = null

fun value(value: JsonField<T>) = apply { this.value = value }

fun value(value: T?) = value(JsonField.ofNullable(value))

private val hashCode: Int by lazy { contentHash(name, value, contentType, filename) }
fun contentType(contentType: String) = apply { this.contentType = contentType }

fun filename(filename: String?) = apply { this.filename = filename }

fun build(): MultipartField<T> {
val value = checkRequired("value", value)
return MultipartField(
value,
contentType
?: if (value is KnownValue && value.value is ByteArray)
"application/octet-stream"
else "text/plain; charset=utf-8",
filename,
)
}
}

private val hashCode: Int by lazy { contentHash(value, contentType, filename) }

override fun hashCode(): Int = hashCode

Expand All @@ -471,63 +508,12 @@ internal constructor(
return true
}

return other is MultipartFormValue<*> &&
name == other.name &&
value contentEquals other.value &&
return other is MultipartField<*> &&
value == other.value &&
contentType == other.contentType &&
filename == other.filename
}

override fun toString(): String =
"MultipartFormValue{name=$name, contentType=$contentType, filename=$filename, value=${valueToString()}}"

private fun valueToString(): String =
when (value) {
is ByteArray -> "ByteArray of size ${value.size}"
else -> value.toString()
}

companion object {
internal fun fromString(
name: String,
value: String,
contentType: ContentType,
): MultipartFormValue<String> = MultipartFormValue(name, value, contentType)

internal fun fromBoolean(
name: String,
value: Boolean,
contentType: ContentType,
): MultipartFormValue<Boolean> = MultipartFormValue(name, value, contentType)

internal fun fromLong(
name: String,
value: Long,
contentType: ContentType,
): MultipartFormValue<Long> = MultipartFormValue(name, value, contentType)

internal fun fromDouble(
name: String,
value: Double,
contentType: ContentType,
): MultipartFormValue<Double> = MultipartFormValue(name, value, contentType)

internal fun <T : Enum> fromEnum(
name: String,
value: T,
contentType: ContentType,
): MultipartFormValue<T> = MultipartFormValue(name, value, contentType)

internal fun fromByteArray(
name: String,
value: ByteArray,
contentType: ContentType,
filename: String? = null,
): MultipartFormValue<ByteArray> = MultipartFormValue(name, value, contentType, filename)
}
}

internal object ContentTypes {
val DefaultText = ContentType.create(ContentType.TEXT_PLAIN.mimeType, Charset.forName("UTF-8"))
val DefaultBinary = ContentType.DEFAULT_BINARY
"MultipartField{value=$value, contentType=$contentType, filename=$filename}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// File generated from our OpenAPI spec by Stainless.

package org.onebusaway.core.http

import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
import java.io.OutputStream
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
import org.apache.hc.core5.http.ContentType
import org.apache.hc.core5.http.HttpEntity
import org.onebusaway.core.MultipartField
import org.onebusaway.errors.OnebusawaySdkInvalidDataException

internal inline fun <reified T> json(jsonMapper: JsonMapper, value: T): HttpRequestBody =
object : HttpRequestBody {
private val bytes: ByteArray by lazy { jsonMapper.writeValueAsBytes(value) }

override fun writeTo(outputStream: OutputStream) = outputStream.write(bytes)

override fun contentType(): String = "application/json"

override fun contentLength(): Long = bytes.size.toLong()

override fun repeatable(): Boolean = true

override fun close() {}
}

internal fun multipartFormData(
jsonMapper: JsonMapper,
fields: Map<String, MultipartField<*>>,
): HttpRequestBody =
object : HttpRequestBody {
private val entity: HttpEntity by lazy {
MultipartEntityBuilder.create()
.apply {
fields.forEach { (name, field) ->
val node = jsonMapper.valueToTree<JsonNode>(field.value)
serializePart(name, node).forEach { (name, bytes) ->
addBinaryBody(
name,
bytes,
ContentType.parseLenient(field.contentType),
field.filename,
)
}
}
}
.build()
}

private fun serializePart(name: String, node: JsonNode): Sequence<Pair<String, ByteArray>> =
when (node.nodeType) {
JsonNodeType.MISSING,
JsonNodeType.NULL -> emptySequence()
JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue())
JsonNodeType.STRING -> sequenceOf(name to node.textValue().toByteArray())
JsonNodeType.BOOLEAN ->
sequenceOf(name to node.booleanValue().toString().toByteArray())
JsonNodeType.NUMBER ->
sequenceOf(name to node.numberValue().toString().toByteArray())
JsonNodeType.ARRAY ->
node.elements().asSequence().flatMap { element -> serializePart(name, element) }
JsonNodeType.OBJECT ->
node.fields().asSequence().flatMap { (key, value) ->
serializePart("$name[$key]", value)
}
JsonNodeType.POJO,
null ->
throw OnebusawaySdkInvalidDataException(
"Unexpected JsonNode type: ${node.nodeType}"
)
}

override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)

override fun contentType(): String = entity.contentType

override fun contentLength(): Long = entity.contentLength

override fun repeatable(): Boolean = entity.isRepeatable

override fun close() = entity.close()
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.onebusaway.core.JsonField
import org.onebusaway.core.JsonMissing
import org.onebusaway.core.JsonValue
import org.onebusaway.core.NoAutoDetect
import org.onebusaway.core.checkKnown
import org.onebusaway.core.checkRequired
import org.onebusaway.core.immutableEmptyMap
import org.onebusaway.core.toImmutable
Expand Down Expand Up @@ -243,12 +244,8 @@ private constructor(

fun addList(list: List) = apply {
this.list =
(this.list ?: JsonField.of(mutableListOf())).apply {
(asKnown()
?: throw IllegalStateException(
"Field was set to non-list type: ${javaClass.simpleName}"
))
.add(list)
(this.list ?: JsonField.of(mutableListOf())).also {
checkKnown("list", it).add(list)
}
}

Expand Down
Loading