Skip to content

Commit b3bcf0f

Browse files
committed
Refactor Api extensions
1 parent 93c1bb4 commit b3bcf0f

File tree

4 files changed

+83
-38
lines changed

4 files changed

+83
-38
lines changed

src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/GradleEnterpriseApiExtensions.kt

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.gabrielfeo.gradle.enterprise.api
22

3+
import com.gabrielfeo.gradle.enterprise.api.internal.API_MAX_BUILDS
4+
import com.gabrielfeo.gradle.enterprise.api.internal.operator.pagedUntilLastBuild
5+
import com.gabrielfeo.gradle.enterprise.api.internal.operator.withGradleAttributes
36
import com.gabrielfeo.gradle.enterprise.api.model.*
47
import kotlinx.coroutines.CoroutineScope
8+
import kotlinx.coroutines.DelicateCoroutinesApi
59
import kotlinx.coroutines.GlobalScope
6-
import kotlinx.coroutines.async
710
import kotlinx.coroutines.flow.*
811
import retrofit2.await
912

10-
private const val API_MAX_BUILDS = 1000
11-
1213
/**
1314
* Gets builds on demand from the API, in as many requests as necessary. It allows
1415
* for queries of any size, as opposed to [GradleEnterpriseApi.getBuilds] which is limited by the
@@ -24,43 +25,17 @@ fun GradleEnterpriseApi.getBuildsFlow(
2425
fromInstant: Long? = null,
2526
fromBuild: String? = null,
2627
): Flow<Build> = flow {
27-
var lastBuildId: String? = null
28-
while (true) {
29-
val call = when (lastBuildId) {
30-
null -> getBuilds(
31-
since = since, sinceBuild = sinceBuild,
32-
fromInstant = fromInstant, fromBuild = fromBuild,
33-
maxBuilds = API_MAX_BUILDS,
34-
)
35-
else -> getBuilds(fromBuild = lastBuildId, maxBuilds = API_MAX_BUILDS)
36-
}
37-
val builds = call.await()
38-
emitAll(builds.asFlow())
39-
when {
40-
builds.isEmpty() || builds.size < API_MAX_BUILDS -> break
41-
else -> lastBuildId = builds.last().id
42-
}
43-
}
28+
val firstBuilds = getBuilds(
29+
since = since,
30+
sinceBuild = sinceBuild,
31+
fromInstant = fromInstant,
32+
fromBuild = fromBuild,
33+
maxBuilds = API_MAX_BUILDS,
34+
).await()
35+
val pagedBuilds = firstBuilds.asFlow().pagedUntilLastBuild(maxPerRequest = API_MAX_BUILDS)
36+
emitAll(pagedBuilds)
4437
}
4538

46-
/**
47-
* Joins builds with their [GradleAttributes], which comes from a different endpoint
48-
* ([GradleEnterpriseApi.getGradleAttributes]).
49-
*
50-
* Don't expect client-side filtering to be efficient. Does as many concurrent calls
51-
* as it can, requesting attributes in an eager coroutine, in [scope].
52-
*/
53-
fun Flow<Build>.withGradleAttributes(
54-
scope: CoroutineScope = GlobalScope,
55-
): Flow<Pair<Build, GradleAttributes>> =
56-
map { build ->
57-
build to scope.async {
58-
api.getGradleAttributes(build.id).await()
59-
}
60-
}.buffer(Int.MAX_VALUE).map { (build, attrs) ->
61-
build to attrs.await()
62-
}
63-
6439
/**
6540
* Gets [GradleAttributes] of all builds from a given date. Queries [GradleEnterpriseApi.getBuilds]
6641
* first, since it's the only endpoint providing a timeline of builds, then maps each to
@@ -69,7 +44,10 @@ fun Flow<Build>.withGradleAttributes(
6944
* Don't expect client-side filtering to be efficient. Does as many concurrent calls
7045
* as it can, requesting attributes in an eager coroutine, in [scope]. For other params,
7146
* see [getBuildsFlow] and [GradleEnterpriseApi.getBuilds].
47+
*
48+
* @param scope CoroutineScope in which to create coroutines. Defaults to [GlobalScope].
7249
*/
50+
@OptIn(DelicateCoroutinesApi::class)
7351
fun GradleEnterpriseApi.getGradleAttributesFlow(
7452
since: Long = 0,
7553
sinceBuild: String? = null,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal
2+
3+
import com.gabrielfeo.gradle.enterprise.api.GradleEnterpriseApi
4+
5+
/**
6+
* Undocumented max value of `/api/builds?maxBuilds`. Last checked in 2022.4.
7+
*/
8+
internal const val API_MAX_BUILDS = 1000
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal.operator
2+
3+
import com.gabrielfeo.gradle.enterprise.api.*
4+
import com.gabrielfeo.gradle.enterprise.api.internal.API_MAX_BUILDS
5+
import com.gabrielfeo.gradle.enterprise.api.model.*
6+
import kotlinx.coroutines.flow.*
7+
import retrofit2.await
8+
9+
/**
10+
* Emits all available builds starting from the upstream Flow builds until the last build available.
11+
* Makes paged requests to the API using `fromBuild`, [maxPerRequest] at a time.
12+
*/
13+
internal fun Flow<Build>.pagedUntilLastBuild(
14+
maxPerRequest: Int,
15+
): Flow<Build> {
16+
val firstBuilds = this
17+
return flow {
18+
var lastBuildId = ""
19+
firstBuilds.collect {
20+
lastBuildId = it.id
21+
emit(it)
22+
}
23+
if (lastBuildId.isEmpty()) {
24+
return@flow
25+
} else while (true) {
26+
val builds = api.getBuilds(fromBuild = lastBuildId, maxBuilds = maxPerRequest).await()
27+
emitAll(builds.asFlow())
28+
when {
29+
builds.isEmpty() || builds.size < API_MAX_BUILDS -> break
30+
else -> lastBuildId = builds.last().id
31+
}
32+
}
33+
}
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal.operator
2+
3+
import com.gabrielfeo.gradle.enterprise.api.*
4+
import com.gabrielfeo.gradle.enterprise.api.model.*
5+
import kotlinx.coroutines.*
6+
import kotlinx.coroutines.flow.*
7+
import retrofit2.await
8+
9+
/**
10+
* Joins builds with their [GradleAttributes], which comes from a different endpoint
11+
* ([GradleEnterpriseApi.getGradleAttributes]).
12+
*
13+
* Don't expect client-side filtering to be efficient. Does as many concurrent calls
14+
* as it can, requesting attributes in an eager coroutine, in [scope].
15+
*/
16+
internal fun Flow<Build>.withGradleAttributes(
17+
scope: CoroutineScope,
18+
): Flow<Pair<Build, GradleAttributes>> =
19+
map { build ->
20+
build to scope.async {
21+
api.getGradleAttributes(build.id).await()
22+
}
23+
}.buffer(Int.MAX_VALUE).map { (build, attrs) ->
24+
build to attrs.await()
25+
}

0 commit comments

Comments
 (0)