elements.events.kt Maven / Gradle / Ivy
package elements
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.pusher.platform.network.parseAs
import com.pusher.platform.network.DataParser
import com.pusher.util.Result
import com.pusher.util.Result.Companion.failuresOf
import com.pusher.util.asFailure
import com.pusher.util.asSuccess
import com.pusher.util.orElse
internal fun String.toSubscriptionMessage(
bodyParser: DataParser
): Result, Error> =
parseAs>().flatMap {
when (it.messageType) {
0 -> it.createControlEvent()
1 -> it.createSubscriptionEvent(bodyParser)
255 -> it.createEosEvent()
else -> Errors.other("Unknown message type: $this").asFailure()
}
}
@Suppress("unused")
sealed class SubscriptionMessage
data class SubscriptionEvent(val eventId: String, val headers: Headers, val body: A) : SubscriptionMessage()
data class EOSEvent(val statusCode: Int, val headers: Headers, val error: EosError) : SubscriptionMessage()
data class ControlEvent(val body: String) : SubscriptionMessage()
private fun Array.createControlEvent(): Result, Error> =
getOrElse(1) { JsonPrimitive("") }.asString.let { ControlEvent(body = it) }.asSuccess()
private fun Array.createEosEvent(): Result, Error> =
validate(statusCode) { statusCode ->
EOSEvent(
statusCode = statusCode,
headers = headers,
error = eosError
)
}
private fun Array.createSubscriptionEvent(
bodyParser: DataParser
): Result, Error> =
validate(eventId, messageBody(bodyParser)) { eventId, body: A ->
SubscriptionEvent(eventId, headers, body)
}
private val Array.statusCode: Result
get() = valueAt(1, "statusCode") { asInt }
private val Array.eosError: EosError
get() = valueAt(3, "error") {
asJsonObject.apply {
if (!has("type")) addProperty("type", "unknown")
if (!has("reason")) addProperty("reason", "unknown")
}
}
.flatMap { it.parseAs() }
.recover { EosError("unknown", "unknown") }
private val Array.headers: Headers
get() = valueAt(2, "headers").recover { emptyHeaders() }
private val Array.eventId: Result
get() = valueAt(1, "eventId")
private val Array.messageType
get() = valueAt(0, "messageType") { asJsonPrimitive.takeIf { it.isNumber }?.asInt }.recover { -1 }
private fun Array.messageBody(
bodyParser: DataParser
): Result = jsonBody.flatMap { bodyParser(it.toString()) }
private val Array.jsonBody
get() = valueAt(3, "body")
private fun validate(a: Result, b: Result, block: (A, B) -> C): Result = when {
a is Result.Success && b is Result.Success -> block(a.value, b.value).asSuccess()
else -> Errors.compose(failuresOf(a, b)).asFailure()
}
private fun validate(a: Result, block: (A) -> C): Result = when (a) {
is Result.Success -> block(a.value).asSuccess()
else -> Errors.compose(failuresOf(a)).asFailure()
}
private fun Array?.valueAt(index: Int, name: String, block: JsonElement.() -> A?): Result =
this?.getOrNull(index)?.block().orElse { fieldNotFoundError(name) }
private inline fun Array.valueAt(index: Int, name: String): Result =
getOrNull(index)?.parseAs()
?:fieldNotFoundError(name).asFailure()
private fun fieldNotFoundError(name: String) =
Errors.other("Field '$name' not found in subscription message")