diff --git a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/info/AvailableReleasesResource.kt b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/info/AvailableReleasesResource.kt index b8a34253..2f3a8a16 100644 --- a/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/info/AvailableReleasesResource.kt +++ b/adoptopenjdk-api-v3-frontend/src/main/kotlin/net/adoptopenjdk/api/v3/routes/info/AvailableReleasesResource.kt @@ -2,7 +2,6 @@ package net.adoptopenjdk.api.v3.routes.info import net.adoptopenjdk.api.v3.dataSources.APIDataStore import net.adoptopenjdk.api.v3.models.ReleaseInfo -import net.adoptopenjdk.api.v3.models.ReleaseType import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.tags.Tag import javax.ws.rs.GET @@ -14,54 +13,10 @@ import javax.ws.rs.core.MediaType @Path("/v3/info/") @Produces(MediaType.APPLICATION_JSON) class AvailableReleasesResource { - - private var releaseInfo: ReleaseInfo = formReleaseInfo() - @GET @Path("/available_releases/") @Operation(summary = "Returns information about available releases") fun get(): ReleaseInfo { - return releaseInfo - } - - final fun formReleaseInfo(): ReleaseInfo { - val gaReleases = APIDataStore.getAdoptRepos() - .allReleases - .getReleases() - .filter { it.release_type == ReleaseType.ga } - .toList() - - val availableReleases = gaReleases - .map { it.version_data.major } - .distinct() - .sorted() - .toList() - .toTypedArray() - val mostRecentFeatureRelease: Int = availableReleases.last() - - val availableLtsReleases: Array = gaReleases - .filter { APIDataStore.variants.ltsVersions.contains(it.version_data.major) } - .map { it.version_data.major } - .distinct() - .sorted() - .toList() - .toTypedArray() - val mostRecentLts = availableLtsReleases.last() - - val mostRecentFeatureVersion: Int = APIDataStore.getAdoptRepos() - .allReleases - .getReleases() - .map { it.version_data.major } - .distinct() - .sorted() - .last() - - return ReleaseInfo( - availableReleases, - availableLtsReleases, - mostRecentLts, - mostRecentFeatureRelease, - mostRecentFeatureVersion - ) + return APIDataStore.getReleaseInfo() } } diff --git a/adoptopenjdk-api-v3-frontend/src/test/kotlin/net/adoptopenjdk/api/AvailableReleasesPathTest.kt b/adoptopenjdk-api-v3-frontend/src/test/kotlin/net/adoptopenjdk/api/AvailableReleasesPathTest.kt index 98843709..c76bcea5 100644 --- a/adoptopenjdk-api-v3-frontend/src/test/kotlin/net/adoptopenjdk/api/AvailableReleasesPathTest.kt +++ b/adoptopenjdk-api-v3-frontend/src/test/kotlin/net/adoptopenjdk/api/AvailableReleasesPathTest.kt @@ -3,15 +3,10 @@ package net.adoptopenjdk.api import io.quarkus.test.junit.QuarkusTest import io.restassured.RestAssured import kotlinx.coroutines.runBlocking -import net.adoptopenjdk.api.v3.AdoptReposBuilder import net.adoptopenjdk.api.v3.JsonMapper import net.adoptopenjdk.api.v3.dataSources.APIDataStore import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory -import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos -import net.adoptopenjdk.api.v3.dataSources.models.FeatureRelease -import net.adoptopenjdk.api.v3.dataSources.models.Releases import net.adoptopenjdk.api.v3.models.ReleaseInfo -import net.adoptopenjdk.api.v3.models.ReleaseType import org.hamcrest.Description import org.hamcrest.TypeSafeMatcher import org.junit.jupiter.api.BeforeEach @@ -23,18 +18,18 @@ class AvailableReleasesPathTest : BaseTest() { @BeforeEach fun initDB() { runBlocking { - val repo = AdoptReposBuilder.build(APIDataStore.variants.versions) - - val releases = repo.allReleases.getReleases() - .filter { it.version_data.major < 13 || it.version_data.major == 13 && it.release_type == ReleaseType.ea } - .groupBy { it.version_data.major } - .toMap() - .map { FeatureRelease(it.key, Releases(it.value)) } - // Reset connection ApiPersistenceFactory.set(null) - ApiPersistenceFactory.get().updateAllRepos(AdoptRepos(releases)) - APIDataStore.loadDataFromDb() + ApiPersistenceFactory.get().setReleaseInfo(ReleaseInfo( + arrayOf(8, 9, 10, 11, 12), + arrayOf(8, 11), + 11, + 12, + 13, + 15 + ) + ) + APIDataStore.loadReleaseInfo() } } diff --git a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/ReleaseInfo.kt b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/ReleaseInfo.kt index 6c701db5..70e2e487 100644 --- a/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/ReleaseInfo.kt +++ b/adoptopenjdk-api-v3-models/src/main/kotlin/net/adoptopenjdk/api/v3/models/ReleaseInfo.kt @@ -19,17 +19,22 @@ class ReleaseInfo { @Schema(example = "15", description = "The highest version (LTS or not) for which we have produced a build, this may be a version that has not yet produced a ga release") val most_recent_feature_version: Int + @Schema(example = "15", description = "The version that is currently in development at openjdk") + val tip_version: Int + constructor( available_releases: Array, available_lts_releases: Array, most_recent_lts: Int, most_recent_feature_release: Int, - most_recent_feature_version: Int + most_recent_feature_version: Int, + tip_version: Int ) { this.available_releases = available_releases this.available_lts_releases = available_lts_releases this.most_recent_lts = most_recent_lts this.most_recent_feature_release = most_recent_feature_release this.most_recent_feature_version = most_recent_feature_version + this.tip_version = tip_version } } diff --git a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/APIDataStore.kt b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/APIDataStore.kt index f5261aa6..7379287d 100644 --- a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/APIDataStore.kt +++ b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/APIDataStore.kt @@ -1,18 +1,20 @@ package net.adoptopenjdk.api.v3.dataSources import com.google.common.annotations.VisibleForTesting -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit -import kotlin.concurrent.timerTask import kotlinx.coroutines.runBlocking import net.adoptopenjdk.api.v3.JsonMapper import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos import net.adoptopenjdk.api.v3.models.Platforms +import net.adoptopenjdk.api.v3.models.ReleaseInfo import net.adoptopenjdk.api.v3.models.Variants import org.slf4j.LoggerFactory +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import kotlin.concurrent.timerTask object APIDataStore { private var binaryRepos: AdoptRepos + private var releaseInfo: ReleaseInfo @JvmStatic private val LOGGER = LoggerFactory.getLogger(this::class.java) @@ -35,23 +37,47 @@ object APIDataStore { binaryRepos = AdoptRepos(listOf()) } + releaseInfo = loadReleaseInfo() + Executors - .newSingleThreadScheduledExecutor() - .scheduleWithFixedDelay(timerTask { - periodicUpdate() - }, 0, 15, TimeUnit.MINUTES) + .newSingleThreadScheduledExecutor() + .scheduleWithFixedDelay(timerTask { + periodicUpdate() + }, 0, 15, TimeUnit.MINUTES) + } + + fun loadReleaseInfo(): ReleaseInfo { + releaseInfo = runBlocking { + val releaseInfo = try { + ApiPersistenceFactory.get().getReleaseInfo() + } catch (e: Exception) { + LOGGER.error("Failed to read db", e) + null + } + + // Default for first time when DB is still being populated + releaseInfo ?: ReleaseInfo( + arrayOf(8, 9, 10, 11, 12, 13, 14), + arrayOf(8, 11), + 11, + 14, + 15, + 15 + ) + } + return releaseInfo } @VisibleForTesting fun loadDataFromDb(): AdoptRepos { binaryRepos = runBlocking { val data = variants - .versions - .map { version -> - ApiPersistenceFactory.get().readReleaseData(version) - } - .filter { it.releases.nodes.isNotEmpty() } - .toList() + .versions + .map { version -> + ApiPersistenceFactory.get().readReleaseData(version) + } + .filter { it.releases.nodes.isNotEmpty() } + .toList() AdoptRepos(data) } @@ -71,8 +97,13 @@ object APIDataStore { // Must catch errors or may kill the scheduler try { binaryRepos = loadDataFromDb() + releaseInfo = loadReleaseInfo() } catch (e: Exception) { LOGGER.error("Failed to load db", e) } } + + fun getReleaseInfo(): ReleaseInfo { + return releaseInfo + } } diff --git a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/ApiPersistence.kt b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/ApiPersistence.kt index baad2f03..5a616b3e 100644 --- a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/ApiPersistence.kt +++ b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/ApiPersistence.kt @@ -1,10 +1,11 @@ package net.adoptopenjdk.api.v3.dataSources.persitence -import java.time.ZonedDateTime import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos import net.adoptopenjdk.api.v3.dataSources.models.FeatureRelease import net.adoptopenjdk.api.v3.models.DockerDownloadStatsDbEntry import net.adoptopenjdk.api.v3.models.GithubDownloadStatsDbEntry +import net.adoptopenjdk.api.v3.models.ReleaseInfo +import java.time.ZonedDateTime interface ApiPersistence { suspend fun updateAllRepos(repos: AdoptRepos) @@ -18,4 +19,6 @@ interface ApiPersistence { suspend fun addDockerDownloadStatsEntries(stats: List) suspend fun getLatestAllDockerStats(): List suspend fun removeStatsBetween(start: ZonedDateTime, end: ZonedDateTime) + suspend fun setReleaseInfo(version: ReleaseInfo) + suspend fun getReleaseInfo(): ReleaseInfo? } diff --git a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt index 4619258b..a4ebc41e 100644 --- a/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt +++ b/adoptopenjdk-api-v3-persistance/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt @@ -1,8 +1,8 @@ package net.adoptopenjdk.api.v3.dataSources.persitence.mongo import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.UpdateOptions import com.mongodb.reactivestreams.client.ClientSession -import java.time.ZonedDateTime import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos import net.adoptopenjdk.api.v3.dataSources.models.FeatureRelease import net.adoptopenjdk.api.v3.dataSources.models.Releases @@ -10,17 +10,21 @@ import net.adoptopenjdk.api.v3.dataSources.persitence.ApiPersistence import net.adoptopenjdk.api.v3.models.DockerDownloadStatsDbEntry import net.adoptopenjdk.api.v3.models.GithubDownloadStatsDbEntry import net.adoptopenjdk.api.v3.models.Release +import net.adoptopenjdk.api.v3.models.ReleaseInfo import org.bson.BsonArray +import org.bson.BsonBoolean import org.bson.BsonDateTime import org.bson.BsonDocument import org.bson.Document import org.litote.kmongo.coroutine.CoroutineCollection import org.slf4j.LoggerFactory +import java.time.ZonedDateTime class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient), ApiPersistence { private val releasesCollection: CoroutineCollection = createCollection(database, RELEASE_DB) private val githubStatsCollection: CoroutineCollection = createCollection(database, GITHUB_STATS_DB) private val dockerStatsCollection: CoroutineCollection = createCollection(database, DOCKER_STATS_DB) + private val releaseInfoCollection: CoroutineCollection = createCollection(database, RELEASE_INFO_DB) companion object { @JvmStatic @@ -28,6 +32,7 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient const val RELEASE_DB = "release" const val GITHUB_STATS_DB = "githubStats" const val DOCKER_STATS_DB = "dockerStats" + const val RELEASE_INFO_DB = "releaseInfo" } override suspend fun updateAllRepos(repos: AdoptRepos) { @@ -42,10 +47,10 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient try { session?.startTransaction() repos - .repos - .forEach { repo -> - writeReleases(session, repo.key, repo.value) - } + .repos + .forEach { repo -> + writeReleases(session, repo.key, repo.value) + } } finally { session?.commitTransaction() session?.close() @@ -67,8 +72,8 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient override suspend fun readReleaseData(featureVersion: Int): FeatureRelease { val releases = releasesCollection - .find(majorVersionMatcher(featureVersion)) - .toList() + .find(majorVersionMatcher(featureVersion)) + .toList() return FeatureRelease(featureVersion, Releases(releases)) } @@ -79,29 +84,29 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient override suspend fun getStatsForFeatureVersion(featureVersion: Int): List { return githubStatsCollection.find(Document("version.major", featureVersion)) - .toList() + .toList() } override suspend fun getLatestGithubStatsForFeatureVersion(featureVersion: Int): GithubDownloadStatsDbEntry? { return githubStatsCollection - .find(Document("feature_version", featureVersion)) - .sort(Document("date", -1)) - .limit(1) - .first() + .find(Document("feature_version", featureVersion)) + .sort(Document("date", -1)) + .limit(1) + .first() } override suspend fun getGithubStats(start: ZonedDateTime, end: ZonedDateTime): List { return githubStatsCollection - .find(betweenDates(start, end)) - .sort(Document("date", 1)) - .toList() + .find(betweenDates(start, end)) + .sort(Document("date", 1)) + .toList() } override suspend fun getDockerStats(start: ZonedDateTime, end: ZonedDateTime): List { return dockerStatsCollection - .find(betweenDates(start, end)) - .sort(Document("date", 1)) - .toList() + .find(betweenDates(start, end)) + .sort(Document("date", 1)) + .toList() } override suspend fun addDockerDownloadStatsEntries(stats: List) { @@ -113,14 +118,14 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient val repoNames = dockerStatsCollection.distinct("repo").toList() return repoNames - .mapNotNull { - dockerStatsCollection - .find(Document("repo", it)) - .sort(Document("date", -1)) - .limit(1) - .first() - } - .toList() + .mapNotNull { + dockerStatsCollection + .find(Document("repo", it)) + .sort(Document("date", -1)) + .limit(1) + .first() + } + .toList() } override suspend fun removeStatsBetween(start: ZonedDateTime, end: ZonedDateTime) { @@ -129,12 +134,28 @@ class MongoApiPersistence(mongoClient: MongoClient) : MongoInterface(mongoClient githubStatsCollection.deleteMany(deleteQuery) } + override suspend fun setReleaseInfo(version: ReleaseInfo) { + releaseInfoCollection.deleteMany(releaseVersionDbEntryMatcher()) + releaseInfoCollection.updateOne( + releaseVersionDbEntryMatcher(), + version, + UpdateOptions().upsert(true) + ) + } + + override suspend fun getReleaseInfo(): ReleaseInfo? { + return releaseInfoCollection.findOne(releaseVersionDbEntryMatcher()) + } + + private fun releaseVersionDbEntryMatcher() = Document("tip_version", BsonDocument("\$exists", BsonBoolean(true))) + private fun betweenDates(start: ZonedDateTime, end: ZonedDateTime): Document { return Document("\$and", - BsonArray(listOf( - BsonDocument("date", BsonDocument("\$gt", BsonDateTime(start.toInstant().toEpochMilli()))), - BsonDocument("date", BsonDocument("\$lt", BsonDateTime(end.toInstant().toEpochMilli()))) - )) + BsonArray(listOf( + BsonDocument("date", BsonDocument("\$gt", BsonDateTime(start.toInstant().toEpochMilli()))), + BsonDocument("date", BsonDocument("\$lt", BsonDateTime(end.toInstant().toEpochMilli()))) + ) + ) ) } diff --git a/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/V3Updater.kt b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/V3Updater.kt index 70eceea3..8b83cc0d 100644 --- a/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/V3Updater.kt +++ b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/V3Updater.kt @@ -6,6 +6,7 @@ import kotlin.concurrent.timerTask import kotlinx.coroutines.runBlocking import net.adoptopenjdk.api.v3.dataSources.APIDataStore import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory +import net.adoptopenjdk.api.v3.dataSources.ReleaseVersionResolver import net.adoptopenjdk.api.v3.dataSources.UpdaterJsonMapper import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos import net.adoptopenjdk.api.v3.dataSources.persitence.ApiPersistence @@ -63,11 +64,11 @@ class V3Updater { // Must catch errors or may kill the scheduler try { runBlocking { - LOGGER.info("Starting Full update") repo = AdoptReposBuilder.build(variants.versions) database.updateAllRepos(repo) statsInterface.update(repo) + ReleaseVersionResolver.updateDbVersion(repo) LOGGER.info("Full update done") } } catch (e: Exception) { @@ -85,6 +86,7 @@ class V3Updater { if (updatedRepo != repo) { repo = updatedRepo database.updateAllRepos(repo) + ReleaseVersionResolver.updateDbVersion(repo) LOGGER.info("Incremental update done") } } diff --git a/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/ReleaseVersionResolver.kt b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/ReleaseVersionResolver.kt new file mode 100644 index 00000000..c1ce5ecd --- /dev/null +++ b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/ReleaseVersionResolver.kt @@ -0,0 +1,70 @@ +package net.adoptopenjdk.api.v3.dataSources + +import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos +import net.adoptopenjdk.api.v3.models.ReleaseInfo +import net.adoptopenjdk.api.v3.models.ReleaseType + +object ReleaseVersionResolver { + + private val VERSION_FILE_URL = "https://raw.githubusercontent.com/openjdk/jdk/master/make/autoconf/version-numbers" + + private suspend fun getTipVersion(): Int? { + val versionFile = UpdaterHtmlClientFactory.client.get(VERSION_FILE_URL) + + return if (versionFile != null) { + Regex(""".*DEFAULT_VERSION_FEATURE=(?\d+).*""", setOf(RegexOption.MULTILINE, RegexOption.DOT_MATCHES_ALL)) + .matchEntire(versionFile)?.groups?.get("num")?.value?.toInt() + } else { + null + } + } + + suspend fun updateDbVersion(repo: AdoptRepos) { + ApiPersistenceFactory.get().setReleaseInfo(formReleaseInfo(repo)) + } + + suspend fun formReleaseInfo(repo: AdoptRepos): ReleaseInfo { + val gaReleases = repo + .allReleases + .getReleases() + .filter { it.release_type == ReleaseType.ga } + .toList() + + val availableReleases = gaReleases + .map { it.version_data.major } + .distinct() + .sorted() + .toList() + .toTypedArray() + val mostRecentFeatureRelease: Int = availableReleases.last() + + val availableLtsReleases: Array = gaReleases + .asSequence() + .filter { APIDataStore.variants.ltsVersions.contains(it.version_data.major) } + .map { it.version_data.major } + .distinct() + .sorted() + .toList() + .toTypedArray() + val mostRecentLts = availableLtsReleases.last() + + val mostRecentFeatureVersion: Int = repo + .allReleases + .getReleases() + .map { it.version_data.major } + .distinct() + .sorted() + .last() + + val tip = getTipVersion() ?: mostRecentFeatureVersion + + return ReleaseInfo( + availableReleases, + availableLtsReleases, + mostRecentLts, + mostRecentFeatureRelease, + mostRecentFeatureVersion, + tip + ) + } +} diff --git a/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/UpdaterHtmlClient.kt b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/UpdaterHtmlClient.kt index 2114db76..12c11272 100644 --- a/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/UpdaterHtmlClient.kt +++ b/adoptopenjdk-api-v3-updater/src/main/kotlin/net/adoptopenjdk/api/v3/dataSources/UpdaterHtmlClient.kt @@ -38,6 +38,7 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient { private val LOGGER = LoggerFactory.getLogger(this::class.java) private val TOKEN: String? = GithubAuth.readToken() private const val REQUEST_TIMEOUT = 12000L + private val GITHUB_DOMAINS = listOf("api.github.com", "github.com") fun extractBody(response: HttpResponse?): String? { if (response == null) { @@ -84,9 +85,9 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient { private fun isARedirect(response: HttpResponse): Boolean { return response.statusLine.statusCode == 307 || - response.statusLine.statusCode == 301 || - response.statusLine.statusCode == 302 || - response.statusLine.statusCode == 303 + response.statusLine.statusCode == 301 || + response.statusLine.statusCode == 302 || + response.statusLine.statusCode == 303 } override fun failed(e: java.lang.Exception?) { @@ -102,24 +103,24 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient { try { val url = URL(urlRequest.url) val request = RequestBuilder - .get(url.toURI()) - .setConfig(HttpClientFactory.REQUEST_CONFIG) - .build() + .get(url.toURI()) + .setConfig(HttpClientFactory.REQUEST_CONFIG) + .build() if (urlRequest.lastModified != null) { request.addHeader("If-Modified-Since", urlRequest.lastModified) } - if (url.host.endsWith("github.com") && TOKEN != null) { + if (GITHUB_DOMAINS.contains(url.host) && TOKEN != null) { request.setHeader("Authorization", "token $TOKEN") } val client = - if (url.host.endsWith("github.com")) { - HttpClientFactory.getNonRedirectHttpClient() - } else { - HttpClientFactory.getHttpClient() - } + if (url.host.endsWith("github.com")) { + HttpClientFactory.getNonRedirectHttpClient() + } else { + HttpClientFactory.getHttpClient() + } client.execute(request, ResponseHandler(this, continuation, urlRequest)) } catch (e: Exception) { diff --git a/adoptopenjdk-api-v3-updater/src/test/kotlin/net/adoptopenjdk/api/ReleaseVersionResolverTest.kt b/adoptopenjdk-api-v3-updater/src/test/kotlin/net/adoptopenjdk/api/ReleaseVersionResolverTest.kt new file mode 100644 index 00000000..37befa90 --- /dev/null +++ b/adoptopenjdk-api-v3-updater/src/test/kotlin/net/adoptopenjdk/api/ReleaseVersionResolverTest.kt @@ -0,0 +1,121 @@ +package net.adoptopenjdk.api + +import kotlinx.coroutines.runBlocking +import net.adoptopenjdk.api.v3.AdoptReposBuilder +import net.adoptopenjdk.api.v3.JsonMapper +import net.adoptopenjdk.api.v3.dataSources.APIDataStore +import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory +import net.adoptopenjdk.api.v3.dataSources.ReleaseVersionResolver +import net.adoptopenjdk.api.v3.dataSources.UpdaterHtmlClient +import net.adoptopenjdk.api.v3.dataSources.UpdaterHtmlClientFactory +import net.adoptopenjdk.api.v3.dataSources.UrlRequest +import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos +import net.adoptopenjdk.api.v3.dataSources.models.FeatureRelease +import net.adoptopenjdk.api.v3.dataSources.models.Releases +import net.adoptopenjdk.api.v3.models.ReleaseInfo +import net.adoptopenjdk.api.v3.models.ReleaseType +import org.apache.http.HttpResponse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class ReleaseVersionResolverTest : BaseTest() { + + @BeforeEach + fun init() { + setHttpClient() + } + + fun getRepos(): AdoptRepos { + return runBlocking { + val repo = AdoptReposBuilder.build(APIDataStore.variants.versions) + + val releases = repo.allReleases.getReleases() + .filter { it.version_data.major < 13 || it.version_data.major == 13 && it.release_type == ReleaseType.ea } + .groupBy { it.version_data.major } + .toMap() + .map { FeatureRelease(it.key, Releases(it.value)) } + + AdoptRepos(releases) + } + } + + @Test + fun isStoredToDb() { + runBlocking { + val info = ReleaseVersionResolver.formReleaseInfo(getRepos()) + ReleaseVersionResolver.updateDbVersion(getRepos()) + val version = ApiPersistenceFactory.get().getReleaseInfo() + assertEquals(JsonMapper.mapper.writeValueAsString(info), JsonMapper.mapper.writeValueAsString(version!!)) + } + } + + @Test + fun availableVersionsIsCorrect() { + check { releaseInfo -> + releaseInfo.available_releases.contentEquals(arrayOf(8, 9, 10, 11, 12)) + } + } + + @Test + fun availableLtsIsCorrect() { + check { releaseInfo -> + releaseInfo.available_lts_releases.contentEquals(arrayOf(8, 11)) + } + } + + @Test + fun mostRecentLtsIsCorrect() { + check { releaseInfo -> + releaseInfo.most_recent_lts == 11 + } + } + + @Test + fun mostRecentFeatureReleaseIsCorrect() { + check { releaseInfo -> + releaseInfo.most_recent_feature_release == 12 + } + } + + @Test + fun mostRecentFeatureVersionIsCorrect() { + check { releaseInfo -> + releaseInfo.most_recent_feature_version == 13 + } + } + + private fun check(matcher: (ReleaseInfo) -> Boolean) { + runBlocking { + val info = ReleaseVersionResolver.formReleaseInfo(getRepos()) + assertTrue(matcher(info)) + } + } + + @Test + fun tipVersionIsCorrect() { + check { releaseInfo -> + releaseInfo.tip_version == 15 + } + } + + private fun setHttpClient() { + UpdaterHtmlClientFactory.client = object : UpdaterHtmlClient { + override suspend fun get(url: String): String? { + return getMetadata(url) + } + + fun getMetadata(url: String): String { + return """ + DEFAULT_VERSION_FEATURE=15 + DEFAULT_VERSION_INTERIM=0 + """.trimIndent() + } + + override suspend fun getFullResponse(request: UrlRequest): HttpResponse? { + return null + } + } + } +}