Skip to content

Commit

Permalink
Fixes some minor bugs and add tests for all PaginatedRequests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Bram-- committed Feb 16, 2024
1 parent 790b020 commit fd83007
Show file tree
Hide file tree
Showing 31 changed files with 15,782 additions and 179 deletions.
17 changes: 1 addition & 16 deletions src/main/kotlin/org/audux/bgg/BggClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.HttpRequestRetry
import java.io.File
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.Locale
Expand All @@ -36,7 +37,6 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.future.future
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.audux.bgg.common.Domains
import org.audux.bgg.common.FamilyType
Expand Down Expand Up @@ -82,21 +82,6 @@ object BggClient {

var engine = { CIO.create() }

@JvmStatic
fun main(vararg args: String) {
setLoggerSeverity(Severity.Verbose)
val things = runBlocking {
things(ids = arrayOf(224517, 342942), comments = true).paginate().call()
}

println("Things:: ${things.data?.things?.size}")
val firstComment = things.data?.things!![0].comments!!
println("${firstComment.comments.size} / ${firstComment.totalItems}")

val secondComments = things.data.things[1].comments!!
println("${secondComments.comments.size} / ${secondComments.totalItems}")
}

/**
* Request details about a user's collection.
*
Expand Down
62 changes: 39 additions & 23 deletions src/main/kotlin/org/audux/bgg/request/PaginatedRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ package org.audux.bgg.request
import co.touchlab.kermit.Logger
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.jvm.Throws
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.audux.bgg.BggClient
Expand Down Expand Up @@ -64,7 +64,11 @@ internal constructor(client: BggClient.InternalBggClient, request: suspend () ->
* ```
* Plays(total = 3, ...., page = 3, plays = List<Play>( /* 300 items in here */)
* ```
*
* @param toPage Paginate from the initial request to the `toPage`
* @throws BggRequestException Thrown when something went wrong in the initial request
*/
@Throws(BggRequestException::class)
abstract suspend fun paginate(toPage: Int = Int.MAX_VALUE): Request<T>
}

Expand All @@ -75,8 +79,12 @@ internal constructor(
private val currentPage: Int,
private val request: suspend () -> Response<Forum>
) : PaginatedRequest<Forum>(client, request) {
/** The pageSize for forum requests i.e. the number of [ThreadSummary]s returned per request. */
private val pageSize = 50
companion object {
/**
* The pageSize for forum requests i.e. the number of [ThreadSummary]s returned per request.
*/
const val PAGE_SIZE = 50
}

override suspend fun paginate(toPage: Int) =
Request(client) {
Expand All @@ -85,7 +93,8 @@ internal constructor(
if (forum.data == null) return@Request forum
val allThreads =
CopyOnWriteArrayList<ThreadSummary>().apply { addAllAbsent(forum.data.threads) }
val lastPage = ceil(forum.data.numThreads.toDouble() / pageSize).toInt()
val lastPage =
min(toPage, ceil(forum.data.numThreads.toDouble() / PAGE_SIZE).toInt())

// Start pagination concurrently.
concurrentRequests((currentPage + 1)..lastPage) { page ->
Expand Down Expand Up @@ -113,13 +122,17 @@ internal constructor(
private val members: Inclusion? = null,
private val request: suspend () -> Response<Guild>
) : PaginatedRequest<Guild>(client, request) {
/** The pageSize for guild requests i.e. the number of [GuildMember]s returned per request. */
private val pageSize = 25
companion object {
/**
* The pageSize for guild requests i.e. the number of [GuildMember]s returned per request.
*/
const val PAGE_SIZE = 25
}

override suspend fun paginate(toPage: Int) =
Request(client) {
if (members != Inclusion.INCLUDE) {
throw BggRequestException("Nothing to paginate without the members parameter set.")
throw BggRequestException("Nothing to paginate without the members parameter set")
}

// Run the initial request
Expand All @@ -134,7 +147,7 @@ internal constructor(

// Number of pages to paginate: (CurrentPage + 1)..lastPage.
val currentPage = guildMembers.page.toInt()
lastPage = min(ceil(guildMembers.count.toDouble() / pageSize).toInt(), toPage)
lastPage = min(ceil(guildMembers.count.toDouble() / PAGE_SIZE).toInt(), toPage)

// Start pagination concurrently.
concurrentRequests((currentPage + 1)..lastPage) { page ->
Expand Down Expand Up @@ -169,8 +182,10 @@ internal constructor(
private val client: BggClient.InternalBggClient,
private val request: suspend () -> Response<Plays>
) : PaginatedRequest<Plays>(client, request) {
/** The pageSize for plays requests i.e. the number of [Play]s returned per request. */
private val pageSize = 100
companion object {
/** The pageSize for plays requests i.e. the number of [Play]s returned per request. */
const val PAGE_SIZE = 100
}

override suspend fun paginate(toPage: Int) =
Request(client) {
Expand All @@ -181,7 +196,7 @@ internal constructor(

// Number of pages to paginate: (CurrentPage + 1)..lastPage.
val currentPage = plays.data.page.toInt()
val lastPage = min(ceil(plays.data.total.toDouble() / pageSize).toInt(), toPage)
val lastPage = min(ceil(plays.data.total.toDouble() / PAGE_SIZE).toInt(), toPage)

// Start pagination concurrently.
concurrentRequests((currentPage + 1)..lastPage) { page ->
Expand Down Expand Up @@ -216,7 +231,7 @@ internal constructor(
Request(client) {
if (!comments && !ratingComments) {
throw BggRequestException(
"Nothing to paginate without the either the comments or ratingComments parameter set."
"Nothing to paginate without either the comments or ratingComments parameter set."
)
}

Expand All @@ -234,7 +249,7 @@ internal constructor(
// Number of pages to paginate: (CurrentPage + 1)..lastPage.
val maxComments =
things.data.things.maxOfOrNull { it.comments?.totalItems ?: 0 } ?: 0
val lastPage = ceil(maxComments.toDouble() / pageSize).toInt()
val lastPage = min(toPage, ceil(maxComments.toDouble() / pageSize).toInt())

// Start pagination concurrently.
concurrentRequests((currentPage + 1)..lastPage) { page ->
Expand Down Expand Up @@ -292,22 +307,25 @@ internal constructor(
private val guilds: Inclusion?,
private val request: suspend () -> Response<User>,
) : PaginatedRequest<User>(client, request) {
/**
* The pageSize for plays requests i.e. the number of [Buddy]s and [GuildReference]s returned
* per request.
*/
private val pageSize = 1_000
companion object {
/**
* The pageSize for user requests i.e. the number of [Buddy]s and [GuildReference]s returned
* per request.
*/
const val PAGE_SIZE = 1_000
}

override suspend fun paginate(toPage: Int) =
Request(client) {
if (buddies != Inclusion.INCLUDE && guilds != Inclusion.INCLUDE) {
throw BggRequestException(
"Nothing to paginate without the either the buddies or guilds parameter set."
"Nothing to paginate without either the buddies or guilds parameter set."
)
}
request().let { user ->
if (user.data == null) return@Request user
if (user.data.buddies == null && user.data.guilds == null) return@Request user

val allGuilds =
CopyOnWriteArrayList<GuildReference>().apply {
user.data.guilds?.let { addAllAbsent(it.guilds) }
Expand All @@ -327,7 +345,7 @@ internal constructor(
user.data.guilds?.total?.toInt() ?: 1,
user.data.buddies?.total?.toInt() ?: 1
)
val lastPage = min(ceil(maxPage.toDouble() / pageSize).toInt(), toPage)
val lastPage = min(ceil(maxPage.toDouble() / PAGE_SIZE).toInt(), toPage)

// Retrieve all pages
concurrentRequests((currentPage + 1)..lastPage) { page ->
Expand Down Expand Up @@ -383,9 +401,7 @@ private suspend inline fun <T> concurrentRequests(
) {
val jobs = CopyOnWriteArrayList<Job>()
runBlocking {
pages.forEach {
jobs.add(launch { request(it) })
}
pages.forEach { jobs.add(launch { request(it) }) }

// Wait for all requests to complete.
jobs.forEach() { it.join() }
Expand Down
Loading

0 comments on commit fd83007

Please sign in to comment.