
commonMain.com.bselzer.gw2.v2.client.instance.BaseClient.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of v2-client Show documentation
Show all versions of v2-client Show documentation
Ktor client for v2 endpoints of the Guild Wars 2 API.
The newest version!
package com.bselzer.gw2.v2.client.instance
import com.bselzer.gw2.v2.client.instance.ExceptionRecoveryMode.DEFAULT
import com.bselzer.gw2.v2.client.instance.ExceptionRecoveryMode.NONE
import com.bselzer.ktx.value.identifier.Identifiable
import com.bselzer.ktx.value.identifier.Identifier
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
sealed class BaseClient(
protected val httpClient: HttpClient,
protected val configuration: Gw2ClientConfiguration
) {
/**
* @return the ids as a comma separated string or "-1" if there are no ids
*/
private fun Collection<*>.asIdsParameter(): String = if (isEmpty()) {
// {"text": "all ids provided are invalid"} is returned when no ids are provided
"-1"
} else {
joinToString(separator = ",")
}
/**
* Adds the id.
*/
protected fun HttpRequestBuilder.idParameter(id: Any?) = parameter("id", id)
/**
* Adds the ids as a comma separated string.
*/
protected fun HttpRequestBuilder.idsParameter(ids: Collection<*>, parameterName: String = "ids") =
parameter(parameterName, ids.asIdsParameter())
/**
* Adds the ids as "all".
*/
protected fun HttpRequestBuilder.allIdsParameter(parameterName: String = "ids") = parameter(parameterName, "all")
/**
* Gets the object without recovery.
*
* @return the object
*/
protected suspend inline fun forceGetSingle(path: String, block: HttpRequestBuilder.() -> Unit = {}): T = request(path, block)
/**
* Gets the object with recovery.
*
* @return the object
*/
protected suspend inline fun getSingle(path: String, instance: () -> T, block: HttpRequestBuilder.() -> Unit = {}): T =
tryOrRecover(instance) { forceGetSingle(path, block) }
/**
* Gets the identifiable object with recovery.
* @return the identifiable object
*/
protected suspend inline fun , Id : Identifier, Value> getIdentifiableSingle(
id: Id,
path: String,
instance: (Id) -> T,
block: HttpRequestBuilder.() -> Unit = {}
): T = tryOrRecover(default = { instance(id) }) { forceGetSingle(path, block) }
/**
* Gets the ids with recovery.
*/
protected suspend inline fun , Value> getIds(path: String, block: HttpRequestBuilder.() -> Unit = {}): List = getList(path, block)
/**
* Gets the identifiable objects with recovery.
*/
protected suspend inline fun , Id : Identifier, Value> getIdentifiableList(
path: String,
block: HttpRequestBuilder.() -> Unit = {}
): List =
getList(path, block)
/**
* Gets the objects with recovery.
*
* @return the objects
*/
protected suspend inline fun getList(path: String, block: HttpRequestBuilder.() -> Unit = {}): List = tryOrRecover(
{ emptyList() }
) {
request(path, block)
}
/**
* Chunks the ids into requests small enough for the API to accept, if there are more ids than the configuration page size.
*
* @return the collection of objects represented by the ids
*/
protected suspend inline fun , Id : Identifier, Value> chunkedIds(
ids: Collection,
path: String,
instance: (Id) -> T,
block: HttpRequestBuilder.() -> Unit = {}
): List = chunked(ids, path, "ids", instance, block)
/**
* Chunks the ids into requests small enough for the API to accept, if there are more ids than the configuration page size.
*
* @return the collection of objects represented by the ids
*/
protected suspend inline fun , Id : Identifier, Value> chunkedTabs(
ids: Collection,
path: String,
instance: (Id) -> T,
block: HttpRequestBuilder.() -> Unit = {}
): List = chunked(ids, path, "tabs", instance, block)
/**
* @return all the objects
*/
protected suspend inline fun , Id : Identifier, Value> allIds(
path: String,
block: HttpRequestBuilder.() -> Unit = {}
): List =
all(path, "ids", block)
/**
* @return all the objects
*/
protected suspend inline fun , Id : Identifier, Value> allTabs(
path: String,
block: HttpRequestBuilder.() -> Unit = {}
): List =
all(path, "tabs", block)
/**
* Chunks the ids into requests small enough for the API to accept, if there are more ids than the configuration page size.
*
* @return the collection of objects represented by the ids
*/
protected suspend inline fun , Id : Identifier, Value> chunked(
ids: Collection,
path: String,
idsParameterName: String,
instance: (Id) -> T,
block: HttpRequestBuilder.() -> Unit = {}
): List = tryOrRecover(
{ ids.map { id -> instance(id) } }
) {
val responses = mutableListOf()
for (chunk in ids.toHashSet().chunked(configuration.pageSize)) {
responses.addAll(request(path) {
idsParameter(chunk, idsParameterName)
apply(block)
})
}
responses
}
/**
* @return all the objects
*/
protected suspend inline fun all(
path: String,
idParameterName: String,
block: HttpRequestBuilder.() -> Unit = {}
): List = tryOrRecover(
// Don't know the associated ids so can't do proper defaulting.
{ emptyList() }
) {
request(path) {
allIdsParameter(idParameterName)
apply(block)
}
}
/**
* Gets the identifiable object with recovery.
*
* @return a single object
*/
protected suspend inline fun , Id : Identifier, Value> getSingleById(
id: Id,
path: String,
instance: (Id) -> T,
block: HttpRequestBuilder.() -> Unit = {}
): T = tryOrRecover(
default = { instance(id) }
) {
request(path) {
idParameter(id)
apply(block)
}
}
/**
* Attempts to call the [block]. If the [block] fails and the recovery mode is [DEFAULT], then the [default] is called.
*
* @return the result of the [block] or the recovery result
*/
protected inline fun tryOrRecover(default: () -> T, block: () -> T): T = try {
block()
} catch (exception: Exception) {
when (configuration.exceptionRecoveryMode) {
NONE -> throw exception
DEFAULT -> default()
}
}
/**
* Makes the request for the object and verifies the status code.
*/
protected suspend inline fun request(path: String, block: HttpRequestBuilder.() -> Unit): T {
val response = httpClient.get(path, block)
// In the case where we are getting a 404, an empty default may be deserialized.
// Consequently, throw and potentially let a tryOrRecover catch to properly construct a default.
return if (!response.status.isSuccess()) {
throw ResponseException(response, response.bodyAsText())
} else {
response.body()
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy