diff --git a/modules/core/shared/src/main/scala/scaladex/core/api/Endpoints.scala b/modules/core/shared/src/main/scala/scaladex/core/api/Endpoints.scala index 3dd7cd3da..afb10c2ad 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/api/Endpoints.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/api/Endpoints.scala @@ -51,7 +51,7 @@ trait Endpoints private val platformFilters = qs[Seq[Platform]]( "platform", - qsDoc("Filter on platform versions", Seq("jvm", "sjs1", "native0.5", "sbt1.0", "mill0.11")) + qsDoc("Filter on platform versions", Seq("jvm", "sjs1", "native0.5", "sbt1", "mill0.11")) ) private val binaryVersionFilters: QueryString[Seq[BinaryVersion]] = diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala index cb66c8d0e..5f157c155 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Artifact.scala @@ -298,6 +298,8 @@ object Artifact { def name: Name = artifactId.name def binaryVersion: BinaryVersion = artifactId.binaryVersion + def platform: Platform = binaryVersion.platform + def language: Language = binaryVersion.language def searchUrl: String = s"https://search.maven.org/#artifactdetails|$groupId|$artifactId|$version|jar" diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala b/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala index a43c6bb8d..22874c254 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/BinaryVersion.scala @@ -15,16 +15,22 @@ final case class BinaryVersion(platform: Platform, language: Language) { // non-empty def value: String = (platform, language) match { - case (Jvm, Java) => language.value - case (Jvm, Scala(sv)) => s"_${sv.value}" - case (SbtPlugin(sbtV), Scala(sv)) => s"_${sv.value}_${sbtV.value}" - case (platform, language) => s"_${platform.value}_${language.value}" + case (Jvm, Java) => language.value + case (Jvm, Scala(sv)) => s"_${sv.value}" + case (SbtPlugin(sbtV), Scala(sv)) => + sbtV match { + case Version.Major(1) => s"_${sv.value}_1.0" + case Version.Minor(0, 13) => s"_${sv.value}_0.13" + case sbtV => s"_sbt${sbtV.value}_${sv.value}" + } + case (platform, language) => s"_${platform.value}_${language.value}" } - override def toString: String = (platform, language) match { - case (Jvm, Java) => "Java" - case (Jvm, Scala(version)) => s"Scala $version" - case (_, _) => s"$platform ($language)" + override def toString: String = platform match { + case Jvm => language.toString + case p: SbtPlugin => p.toString + case p: MillPlugin => p.toString + case _ => s"$platform ($language)" } } @@ -32,7 +38,7 @@ object BinaryVersion { implicit val ordering: Ordering[BinaryVersion] = Ordering.by(v => (v.platform, v.language)) def IntermediateParser[A: P]: P[(String, Option[Version], Option[Version])] = - ("_sjs" | "_native" | "_mill" | "_" | "").! ~ (Version.SemanticParser.?) ~ ("_" ~ Version.SemanticParser).? + ("_sjs" | "_native" | "_mill" | "_sbt" | "_" | "").! ~ (Version.SemanticParser.?) ~ ("_" ~ Version.SemanticParser).? def IntermediateParserButNotInvalidSbt[A: P]: P[(String, Option[Version], Option[Version])] = IntermediateParser.filter { @@ -46,6 +52,7 @@ object BinaryVersion { case ("_sjs", Some(jsV), Some(scalaV)) => Some(BinaryVersion(ScalaJs(jsV), Scala(scalaV))) case ("_native", Some(nativeV), Some(scalaV)) => Some(BinaryVersion(ScalaNative(nativeV), Scala(scalaV))) case ("_mill", Some(millV), Some(scalaV)) => Some(BinaryVersion(MillPlugin(millV), Scala(scalaV))) + case ("_sbt", Some(sbtV), Some(scalaV)) => Some(BinaryVersion(SbtPlugin(sbtV), Scala(scalaV))) case ("_", Some(scalaV), Some(sbtV)) => Some(BinaryVersion(SbtPlugin(sbtV), Scala(scalaV))) case ("_", Some(scalaV), None) => Some(BinaryVersion(Jvm, Scala(scalaV))) case ("", None, None) => Some(BinaryVersion(Jvm, Java)) diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Language.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Language.scala index 354676603..6fe6e1017 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Language.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Language.scala @@ -32,6 +32,7 @@ case object Java extends Language { override def value: String = "java" override def label: String = toString override def isValid: Boolean = true + override def toString: String = "Java" } final case class Scala(version: Version) extends Language { diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala index d968b4bb6..781330108 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Platform.scala @@ -27,19 +27,27 @@ object ScalaJs { } case class SbtPlugin(version: Version) extends Platform { - override def toString: String = - version match { - case Version.Minor(1, 0) => s"sbt 1.x" - case _ => s"sbt $version" - } + override def toString: String = s"sbt $version" override def value: String = s"sbt${version.value}" - override def isValid: Boolean = SbtPlugin.stableVersions.contains(this) + override def isValid: Boolean = SbtPlugin.stableVersions.contains(this) || isSbt2PreRelease + + private def isSbt2PreRelease: Boolean = version match { + case Version.SemanticLike(2, Some(0), Some(0), None, Some(_), None) => true + case _ => false + } } object SbtPlugin { val `0.13`: SbtPlugin = SbtPlugin(Version(0, 13)) - val `1.0`: SbtPlugin = SbtPlugin(Version(1, 0)) - val stableVersions: Set[SbtPlugin] = Set(`0.13`, `1.0`) + val `1.x`: SbtPlugin = SbtPlugin(Version(1)) + val `2.x`: SbtPlugin = SbtPlugin(Version(2)) + + def apply(version: Version): SbtPlugin = version match { + case Version.Minor(1, 0) => `1.x` + case _ => new SbtPlugin(version) + } + + val stableVersions: Set[SbtPlugin] = Set(`0.13`, `1.x`, `2.x`) implicit val ordering: Ordering[SbtPlugin] = Ordering.by(p => p.asInstanceOf[Platform]) } diff --git a/modules/core/shared/src/main/scala/scaladex/core/model/Version.scala b/modules/core/shared/src/main/scala/scaladex/core/model/Version.scala index 59b444158..b98c23437 100644 --- a/modules/core/shared/src/main/scala/scaladex/core/model/Version.scala +++ b/modules/core/shared/src/main/scala/scaladex/core/model/Version.scala @@ -34,7 +34,7 @@ object Version { // We prefer the latest stable artifact. val PreferStable: Ordering[Version] = - Ordering.by[Version, Boolean](_.isStable).orElse(ordering) + Ordering.by((v: Version) => v.isStable).orElse(ordering) final case class SemanticLike( major: Int, @@ -44,8 +44,7 @@ object Version { preRelease: Option[PreRelease] = None, metadata: Option[String] = None ) extends Version { - override def isSemantic: Boolean = - patch.isDefined && patch2.isEmpty && preRelease.forall(_.isSemantic) + override def isSemantic: Boolean = patch2.isEmpty && preRelease.forall(_.isSemantic) override def isPreRelease: Boolean = preRelease.isDefined || metadata.isDefined diff --git a/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactIdTests.scala b/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactIdTests.scala index ea8e452dd..0c2442ec9 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactIdTests.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/model/ArtifactIdTests.scala @@ -41,7 +41,7 @@ class ArtifactIdTests extends AsyncFunSpec with Matchers { it("parses sbt") { val artifactId = "sbt-microsites_2.12_1.0" val expected = - ArtifactId(Name("sbt-microsites"), BinaryVersion(SbtPlugin.`1.0`, Scala.`2.12`)) + ArtifactId(Name("sbt-microsites"), BinaryVersion(SbtPlugin.`1.x`, Scala.`2.12`)) val result = ArtifactId(artifactId) result shouldBe expected result.value shouldBe artifactId diff --git a/modules/core/shared/src/test/scala/scaladex/core/model/PlatformTests.scala b/modules/core/shared/src/test/scala/scaladex/core/model/PlatformTests.scala index d25df54ed..457ecd876 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/model/PlatformTests.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/model/PlatformTests.scala @@ -2,19 +2,18 @@ package scaladex.core import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers -import scaladex.core.model.Jvm -import scaladex.core.model.MillPlugin -import scaladex.core.model.Platform -import scaladex.core.model.SbtPlugin -import scaladex.core.model.ScalaJs -import scaladex.core.model.ScalaNative +import scaladex.core.model._ class PlatformTests extends AnyFunSpec with Matchers { - it("should yield a Platform from its label") { + it("should parse a Platform from its value") { Platform.parse("sjs1").get shouldBe ScalaJs.`1.x` Platform.parse("jvm").get shouldBe Jvm Platform.parse("native0.4").get shouldBe ScalaNative.`0.4` - Platform.parse("sbt1.0").get shouldBe SbtPlugin.`1.0` + Platform.parse("sbt1.0").get shouldBe SbtPlugin.`1.x` + Platform.parse("sbt1").get shouldBe SbtPlugin.`1.x` + Platform.parse("sbt2.0.0-M2").get shouldBe SbtPlugin( + Version.SemanticLike(2, Some(0), Some(0), preRelease = Some(Milestone(2))) + ) Platform.parse("mill0.10").get shouldBe MillPlugin.`0.10` } } diff --git a/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala b/modules/core/shared/src/test/scala/scaladex/core/model/VersionTests.scala similarity index 95% rename from modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala rename to modules/core/shared/src/test/scala/scaladex/core/model/VersionTests.scala index 9f1f315a5..8e842923b 100644 --- a/modules/core/shared/src/test/scala/scaladex/core/model/SemanticVersionTests.scala +++ b/modules/core/shared/src/test/scala/scaladex/core/model/VersionTests.scala @@ -5,7 +5,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks import scaladex.core.test.Values._ -class SemanticVersionTests extends AsyncFunSpec with Matchers with TableDrivenPropertyChecks { +class VersionTests extends AsyncFunSpec with Matchers with TableDrivenPropertyChecks { it("should parse any version") { val inputs = Table( ("input", "output"), diff --git a/modules/infra/src/main/scala/scaladex/infra/migrations/V25__fix_sbt_platform.scala b/modules/infra/src/main/scala/scaladex/infra/migrations/V25__fix_sbt_platform.scala new file mode 100644 index 000000000..dcaa140a0 --- /dev/null +++ b/modules/infra/src/main/scala/scaladex/infra/migrations/V25__fix_sbt_platform.scala @@ -0,0 +1,37 @@ +package scaladex.infra.migrations +import com.typesafe.scalalogging.LazyLogging +import doobie.Query0 +import doobie.util.update.Update +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import scaladex.core.model._ +import scaladex.infra.sql.DoobieUtils.Mappings._ +import scaladex.infra.sql.DoobieUtils.selectRequest +import scaladex.infra.sql.DoobieUtils.updateRequest + +class V25__fix_sbt_platform extends BaseJavaMigration with ScaladexBaseMigration with LazyLogging { + override def migrate(context: Context): Unit = + try { + val request = + for { + artifactRefs <- selectArtifactRefs.to[Seq] + _ <- updatePlatformAndLanguage.updateMany(artifactRefs.map(a => (a.platform, a.language, a))) + } yield () + run(xa)(request).unsafeRunSync() + + } catch { + case e: Throwable => + logger.info("failed to migrate the database") + throw new Exception(s"failed to migrate the database because of ${e.getMessage}") + } + + val selectArtifactRefs: Query0[Artifact.Reference] = selectRequest( + "artifacts", + Seq("group_id", "artifact_id", "version"), + where = Seq("platform='sbt1.0' OR artifact_id LIKE '%_sbt2.0.0-M2_3'") + ) + + val updatePlatformAndLanguage: Update[(Platform, Language, Artifact.Reference)] = + updateRequest("artifacts", Seq("platform", "language_version"), Seq("group_id", "artifact_id", "version")) + +} diff --git a/modules/infra/src/main/scala/scaladex/infra/migrations/V26__fix_artifact_names.scala b/modules/infra/src/main/scala/scaladex/infra/migrations/V26__fix_artifact_names.scala new file mode 100644 index 000000000..4feac2d4d --- /dev/null +++ b/modules/infra/src/main/scala/scaladex/infra/migrations/V26__fix_artifact_names.scala @@ -0,0 +1,37 @@ +package scaladex.infra.migrations +import com.typesafe.scalalogging.LazyLogging +import doobie.Query0 +import doobie.util.update.Update +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import scaladex.core.model._ +import scaladex.infra.sql.DoobieUtils.Mappings._ +import scaladex.infra.sql.DoobieUtils.selectRequest +import scaladex.infra.sql.DoobieUtils.updateRequest + +class V26__fix_artifact_names extends BaseJavaMigration with ScaladexBaseMigration with LazyLogging { + override def migrate(context: Context): Unit = + try { + val request = + for { + artifactRefs <- selectArtifactRefs.to[Seq] + _ <- updateNames.updateMany(artifactRefs.map(a => (a.name, a))) + } yield () + run(xa)(request).unsafeRunSync() + + } catch { + case e: Throwable => + logger.info("failed to migrate the database") + throw new Exception(s"failed to migrate the database because of ${e.getMessage}") + } + + val selectArtifactRefs: Query0[Artifact.Reference] = selectRequest( + "artifacts", + Seq("group_id", "artifact_id", "version"), + where = Seq("artifact_id LIKE '%_sbt2.0.0-M2_3'") + ) + + val updateNames: Update[(Artifact.Name, Artifact.Reference)] = + updateRequest("artifacts", Seq("artifact_name"), Seq("group_id", "artifact_id", "version")) + +} diff --git a/modules/template/src/main/scala/scaladex/view/model/EcosystemHighlight.scala b/modules/template/src/main/scala/scaladex/view/model/EcosystemHighlight.scala index a28b7f91d..b8413a5fe 100644 --- a/modules/template/src/main/scala/scaladex/view/model/EcosystemHighlight.scala +++ b/modules/template/src/main/scala/scaladex/view/model/EcosystemHighlight.scala @@ -5,10 +5,6 @@ import scaladex.core.model.Version final case class EcosystemVersion(version: Version, libraryCount: Int, search: Url) -object EcosystemVersion { - val ordering: Ordering[EcosystemVersion] = Ordering.by(_.version) -} - final case class EcosystemHighlight( ecosystem: String, currentVersion: EcosystemVersion, @@ -17,7 +13,7 @@ final case class EcosystemHighlight( object EcosystemHighlight { def apply(ecosystem: String, allVersions: Seq[EcosystemVersion]): Option[EcosystemHighlight] = { - val sortedVersions = allVersions.sorted(EcosystemVersion.ordering.reverse) + val sortedVersions = allVersions.sortBy(_.version)(Version.PreferStable).reverse sortedVersions.headOption.map(EcosystemHighlight(ecosystem, _, sortedVersions.tail)) } } diff --git a/modules/template/src/main/twirl/scaladex/view/project/artifact.scala.html b/modules/template/src/main/twirl/scaladex/view/project/artifact.scala.html index dbbacade4..6c6b8e98d 100644 --- a/modules/template/src/main/twirl/scaladex/view/project/artifact.scala.html +++ b/modules/template/src/main/twirl/scaladex/view/project/artifact.scala.html @@ -28,17 +28,17 @@
diff --git a/modules/template/src/test/scala/scaladex/view/model/EcosystemHightlightTests.scala b/modules/template/src/test/scala/scaladex/view/model/EcosystemHightlightTests.scala new file mode 100644 index 000000000..394791ff9 --- /dev/null +++ b/modules/template/src/test/scala/scaladex/view/model/EcosystemHightlightTests.scala @@ -0,0 +1,18 @@ +package scaladex.view.model + +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers +import scaladex.core.model._ + +class EcosystemHighlightTest extends AnyFunSpec with Matchers { + it("ordering") { + val `1.x` = EcosystemVersion(Version(1), 0, Url("")) + val `0.13` = EcosystemVersion(Version(0, 13), 0, Url("")) + val `2.0.0-M2` = + EcosystemVersion(Version.SemanticLike(2, Some(0), Some(0), preRelease = Some(Milestone(2))), 0, Url("")) + + val highlight = EcosystemHighlight("sbt", Seq(`1.x`, `0.13`, `2.0.0-M2`)).get + highlight.currentVersion shouldBe `1.x` + (highlight.otherVersions should contain).theSameElementsInOrderAs(Seq(`0.13`, `2.0.0-M2`)) + } +}