Skip to content

Commit

Permalink
Rework the Scaladex API
Browse files Browse the repository at this point in the history
`GET  /api/projects` to get the lists of all project references, except the ones that are moved or deleted on Github.
`GET /api/projects/<org>/<repo>/artifacts` to get the lists of artifact refs of a project.
`GET /api/artifacts/<groupid>/<artifactid>/<version>` to get some info about an artifact (language, platform, licenses, release date etc).

There are no filter anymore. Later we can (re-)introduce the `language` and `platform` filters.
There is no pagination and I don't plan on working on it.
  • Loading branch information
adpi2 committed Feb 28, 2024
1 parent aa8fc4e commit 0eb3c85
Show file tree
Hide file tree
Showing 37 changed files with 322 additions and 495 deletions.
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ lazy val core = crossProject(JSPlatform, JVMPlatform)
"io.github.cquiroz" %%% "scala-java-time" % "2.5.0",
"com.typesafe.play" %%% "play-json" % V.playJson,
"org.endpoints4s" %%% "algebra" % "1.11.1",
"org.endpoints4s" %% "json-schema-playjson" % "1.11.1" % Test,
"org.scalatest" %%% "scalatest" % V.scalatest % Test,
"org.jsoup" % "jsoup" % "1.17.2"
) ++ Seq(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package scaladex.core.api

import scaladex.core.model.Artifact
import scaladex.core.model.SemanticVersion
import scaladex.core.model.Project
import java.time.Instant
import scaladex.core.model.License
import scaladex.core.model.Platform
import scaladex.core.model.Language

final case class ArtifactResponse(
groupId: Artifact.GroupId,
artifactId: String,
version: SemanticVersion,
artifactName: Artifact.Name,
project: Project.Reference,
releaseDate: Instant,
licenses: Seq[License],
language: Language,
platform: Platform
)

object ArtifactResponse {
def apply(artifact: Artifact): ArtifactResponse = ArtifactResponse(
artifact.groupId,
artifact.artifactId,
artifact.version,
artifact.artifactName,
artifact.projectRef,
artifact.releaseDate,
artifact.licenses.toSeq,
artifact.language,
artifact.platform
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import scaladex.core.model.search.Sorting

case class AutocompletionParams(
query: String,
you: Boolean,
topics: Seq[String],
languages: Seq[String],
platforms: Seq[String],
contributingSearch: Boolean
contributingSearch: Boolean,
you: Boolean
) {
def withUser(user: Option[UserState]): SearchParams = {
val userRepos = if (you) user.map(_.repos).getOrElse(Set.empty) else Set.empty[Project.Reference]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package scaladex.core.api

import scaladex.core.model.Project
import scaladex.core.model.Artifact

trait Endpoints
extends JsonSchemas
with endpoints4s.algebra.Endpoints
with endpoints4s.algebra.JsonEntitiesFromSchemas {

private val projectPath: Path[Project.Reference] =
(segment[String]("organization") / segment[String]("repository"))
.xmap {
case (org, repo) => Project.Reference.from(org, repo)
}(ref => (ref.organization.value, ref.repository.value))

private val artifactPath: Path[Artifact.MavenReference] =
(segment[String]("groupId") / segment[String]("artifactId") / segment[String]("version"))
.xmap {
case (groupId, artifactId, version) => Artifact.MavenReference(groupId, artifactId, version)
}(ref => (ref.groupId, ref.artifactId, ref.version))

private val autocompletionQueryString: QueryString[AutocompletionParams] = (
qs[String]("q", docs = Some("Main query (e.g., 'json', 'testing', etc.)")) &
qs[Seq[String]]("topics", docs = Some("Filter on Github topics")) &
qs[Seq[String]](
"languages",
docs = Some("Filter on language versions (e.g., '3', '2.13', '2.12', '2.11', 'java')")
) &
qs[Seq[String]](
"platforms",
docs = Some("Filter on runtime platforms (e.g., 'jvm', 'sjs1', 'native0.4', 'sbt1.0')")
) &
qs[Option[Boolean]]("contributingSearch").xmap(_.getOrElse(false))(Option.when(_)(true)) &
qs[Option[String]]("you", docs = Some("internal usage")).xmap[Boolean](_.contains(""))(Option.when(_)(""))
).xmap((AutocompletionParams.apply _).tupled)(Function.unlift(AutocompletionParams.unapply))

val listProjects: Endpoint[Unit, Seq[Project.Reference]] =
endpoint(
get(path / "api" / "projects"),
ok(jsonResponse[Seq[Project.Reference]])
)

val listProjectArtifacts: Endpoint[Project.Reference, Seq[Artifact.MavenReference]] =
endpoint(
get(path / "api" / "projects" / projectPath / "artifacts"),
ok(jsonResponse[Seq[Artifact.MavenReference]])
)

val getArtifact: Endpoint[Artifact.MavenReference, Option[ArtifactResponse]] =
endpoint(
get(path / "api" / "artifacts" / artifactPath),
ok(jsonResponse[ArtifactResponse]).orNotFound()
)

val autocomplete: Endpoint[AutocompletionParams, Seq[AutocompletionResponse]] =
endpoint(
get(path / "api" / "autocomplete" /? autocompletionQueryString),
ok(jsonResponse[Seq[AutocompletionResponse]])
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package scaladex.core.api

import scaladex.core.model.Artifact
import scaladex.core.model.SemanticVersion
import scaladex.core.model.Project
import java.time.Instant
import scaladex.core.model.License
import scaladex.core.model.Language
import scaladex.core.model.Platform

/**
* The Json schema of the Scaladex API
*/
trait JsonSchemas extends endpoints4s.algebra.JsonSchemas {
implicit val projectReferenceSchema: JsonSchema[Project.Reference] =
field[String]("organization")
.zip(field[String]("repository"))
.xmap(Function.tupled(Project.Reference.from(_, _)))(ref => (ref.organization.value, ref.repository.value))

implicit val mavenReferenceSchema: JsonSchema[Artifact.MavenReference] =
field[String]("groupId")
.zip(field[String]("artifactId"))
.zip(field[String]("version"))
.xmap(Function.tupled(Artifact.MavenReference.apply _))(Function.unlift(Artifact.MavenReference.unapply))

implicit val getArtifactResponseSchema: JsonSchema[ArtifactResponse] =
field[String]("groupId").xmap(Artifact.GroupId.apply)(_.value)
.zip(field[String]("artifactId"))
.zip(field[String]("version").xmap(SemanticVersion.from)(_.encode))
.zip(field[String]("artifactName").xmap(Artifact.Name.apply)(_.value))
.zip(field[String]("project").xmap(Project.Reference.from)(_.toString))
.zip(field[Long]("releaseDate").xmap(Instant.ofEpochMilli)(_.toEpochMilli))
.zip(field[Seq[String]]("licenses").xmap(_.flatMap(License.get))(_.map(_.shortName)))
.zip(field[String]("language").xmap(Language.fromLabel(_).get)(_.label))
.zip(field[String]("platform").xmap(Platform.fromLabel(_).get)(_.label))
.xmap {
case (groupId, artifactId, version, artifactName, project, releaseDate, licenses, language, platform) =>
ArtifactResponse(groupId, artifactId, version, artifactName, project, releaseDate, licenses, language, platform)
} (Function.unlift(ArtifactResponse.unapply))

implicit val autocompletionResponseSchema: JsonSchema[AutocompletionResponse] =
field[String]("organization")
.zip(field[String]("repository"))
.zip(field[String]("description"))
.xmap[AutocompletionResponse] {
case (organization, repository, description) => AutocompletionResponse(organization, repository, description)
} { autocompletionResponse =>
(autocompletionResponse.organization, autocompletionResponse.repository, autocompletionResponse.description)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package scaladex.core.api

final case class ProjectParams(language: Option[String], platform: Option[String])

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import java.time.format.DateTimeFormatter
import fastparse.P
import fastparse.Start
import fastparse._
import scaladex.core.api.artifact.ArtifactMetadataResponse
import scaladex.core.model.PatchVersion
import scaladex.core.util.Parsers._

Expand Down Expand Up @@ -294,13 +293,4 @@ object Artifact {
def repoUrl: String =
s"https://repo1.maven.org/maven2/${groupId.replace('.', '/')}/$artifactId/$version/"
}

def toMetadataResponse(artifact: Artifact): ArtifactMetadataResponse =
ArtifactMetadataResponse(
version = artifact.version.toString,
projectReference = Some(artifact.projectRef.toString),
releaseDate = artifact.releaseDate.toString,
language = artifact.language.toString,
platform = artifact.platform.toString
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@ sealed trait GithubStatus extends Ordered[GithubStatus] {
val updateDate: Instant

def isOk: Boolean = this match {
case GithubStatus.Ok(_) => true
case _: GithubStatus.Ok => true
case _ => false
}

def isMoved: Boolean = this match {
case GithubStatus.Moved(_, _) => true
case _: GithubStatus.Moved => true
case _ => false
}

def isUnknown: Boolean = this match {
case _: GithubStatus.Unknown => true
case _ => false
}

def isNotFound: Boolean = this match {
case GithubStatus.NotFound(_) => true
case _ => false
}

def isFailed: Boolean = this match {
case GithubStatus.Failed(_, _, _) => true
case _: GithubStatus.Failed => true
case _ => false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,6 @@ object SemanticVersion {
case Parsed.Success(v, _) => Some(v)
case _ => None
}

def from(version: String): SemanticVersion = parse(version).get
}
Loading

0 comments on commit 0eb3c85

Please sign in to comment.