diff --git a/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionDao.kt b/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionDao.kt index 33d9fe86..473478d5 100644 --- a/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionDao.kt +++ b/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionDao.kt @@ -11,13 +11,6 @@ import ca.cgagnier.wlednativeandroid.model.Version import ca.cgagnier.wlednativeandroid.model.VersionWithAssets import kotlinx.coroutines.flow.Flow -/** - * nightly tag is not supported at the moment. Exclude it from results. - * TODO: Add support for nightly tags. This will need special handling since the tag itself never - * changes. Probably need a new Branch option for it too. - */ -private const val IGNORED_TAG = "nightly" - @Dao interface VersionDao { @Insert(onConflict = OnConflictStrategy.REPLACE) @@ -38,31 +31,6 @@ interface VersionDao { @Query("SELECT * FROM version WHERE repositoryId = :repositoryId") suspend fun getVersionsByRepository(repositoryId: Long): List - @Transaction - @Query( - """ - SELECT * FROM version - WHERE repositoryId = :repositoryId - AND isPrerelease = 0 - AND tagName != '$IGNORED_TAG' - ORDER BY publishedDate DESC - LIMIT 1 - """, - ) - suspend fun getLatestStableVersionWithAssets(repositoryId: Long): VersionWithAssets? - - @Transaction - @Query( - """ - SELECT * FROM version - WHERE repositoryId = :repositoryId - AND tagName != '$IGNORED_TAG' - ORDER BY publishedDate DESC - LIMIT 1 - """, - ) - suspend fun getLatestBetaVersionWithAssets(repositoryId: Long): VersionWithAssets? - @Transaction @Query("SELECT * FROM version WHERE repositoryId = :repositoryId AND tagName = :tagName LIMIT 1") suspend fun getVersionByTagName(repositoryId: Long, tagName: String): VersionWithAssets? diff --git a/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepository.kt b/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepository.kt index 03cee2ad..4d1a1c30 100644 --- a/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepository.kt +++ b/app/src/main/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepository.kt @@ -6,8 +6,16 @@ import ca.cgagnier.wlednativeandroid.model.Asset import ca.cgagnier.wlednativeandroid.model.Repository import ca.cgagnier.wlednativeandroid.model.Version import ca.cgagnier.wlednativeandroid.model.VersionWithAssets +import com.vdurmont.semver4j.Semver import javax.inject.Inject +/** + * nightly tag is not supported at the moment. Exclude it from results. + * TODO: Add support for nightly tags. This will need special handling since the tag itself never + * changes. Probably need a new Branch option for it too. + */ +private const val IGNORED_TAG = "nightly" + class VersionWithAssetsRepository @Inject constructor( private val database: DevicesDatabase, private val repositoryDao: RepositoryDao, @@ -76,12 +84,41 @@ class VersionWithAssetsRepository @Inject constructor( } } - suspend fun getLatestStableVersionWithAssets(repositoryId: Long): VersionWithAssets? = - versionDao.getLatestStableVersionWithAssets(repositoryId) + suspend fun getLatestStableVersionWithAssets(repositoryId: Long): VersionWithAssets? { + val latestVersion = getLatestVersion( + versionDao.getVersionsByRepository(repositoryId).filter { !it.isPrerelease && it.tagName != IGNORED_TAG }, + ) + return latestVersion?.let { versionDao.getVersionByTagName(repositoryId, it.tagName) } + } - suspend fun getLatestBetaVersionWithAssets(repositoryId: Long): VersionWithAssets? = - versionDao.getLatestBetaVersionWithAssets(repositoryId) + suspend fun getLatestBetaVersionWithAssets(repositoryId: Long): VersionWithAssets? { + val latestVersion = getLatestVersion( + versionDao.getVersionsByRepository(repositoryId).filter { it.tagName != IGNORED_TAG }, + ) + return latestVersion?.let { versionDao.getVersionByTagName(repositoryId, it.tagName) } + } suspend fun getVersionByTag(repositoryId: Long, tagName: String): VersionWithAssets? = versionDao.getVersionByTagName(repositoryId, tagName) + + companion object { + val semVerComparator = + Comparator> { v1, v2 -> + val semver1 = v1.second + val semver2 = v2.second + + when { + semver1 != null && semver2 != null -> semver1.compareTo(semver2) + semver1 != null -> 1 + semver2 != null -> -1 + else -> v1.first.publishedDate.compareTo(v2.first.publishedDate) + } + } + + fun getLatestVersion(versions: List): Version? = versions.map { + it to runCatching { + Semver(it.tagName, Semver.SemverType.LOOSE) + }.getOrNull() + }.maxWithOrNull(semVerComparator)?.first + } } diff --git a/app/src/test/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepositoryTest.kt b/app/src/test/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepositoryTest.kt new file mode 100644 index 00000000..117b9614 --- /dev/null +++ b/app/src/test/java/ca/cgagnier/wlednativeandroid/repository/VersionWithAssetsRepositoryTest.kt @@ -0,0 +1,52 @@ +package ca.cgagnier.wlednativeandroid.repository + +import ca.cgagnier.wlednativeandroid.model.Version +import ca.cgagnier.wlednativeandroid.model.VersionWithAssets +import com.vdurmont.semver4j.Semver +import org.junit.Assert.assertEquals +import org.junit.Test + +class VersionWithAssetsRepositoryTest { + + @Test + fun semVerComparator_sortsCorrectly() { + val versions = listOf( + createVersion("0.14.0", "2023-01-01T00:00:00Z"), + createVersion("0.15.0", "2023-06-01T00:00:00Z"), + createVersion("0.15.5", "2023-07-01T00:00:00Z"), + createVersion("16.0.0", "2022-01-01T00:00:00Z"), // Older date, newer semver + createVersion("invalid-tag", "2024-01-01T00:00:00Z"), // Fallback to date + createVersion("invalid-old", "2023-12-01T00:00:00Z"), // Fallback to date + ).shuffled() + + val parsedVersions = versions.map { + it to runCatching { + Semver(it.tagName, Semver.SemverType.LOOSE) + }.getOrNull() + } + val sorted = parsedVersions.sortedWith(VersionWithAssetsRepository.semVerComparator).map { it.first } + + // Invalid semver tags are sorted by date and placed before valid semver tags + assertEquals("invalid-old", sorted[0].tagName) + assertEquals("invalid-tag", sorted[1].tagName) + // Valid semver tags are sorted by semver + assertEquals("0.14.0", sorted[2].tagName) + assertEquals("0.15.0", sorted[3].tagName) + assertEquals("0.15.5", sorted[4].tagName) + assertEquals("16.0.0", sorted[5].tagName) + + val latest = VersionWithAssetsRepository.getLatestVersion(versions) + assertEquals("16.0.0", latest?.tagName) + } + + private fun createVersion(tagName: String, publishedDate: String): Version = Version( + id = 0, + repositoryId = 0, + tagName = tagName, + name = tagName, + description = "", + isPrerelease = false, + publishedDate = publishedDate, + htmlUrl = "", + ) +}