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 May 30, 2024
1 parent a69467e commit cfb11e6
Show file tree
Hide file tree
Showing 36 changed files with 334 additions and 498 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,36 @@
package scaladex.core.api

import java.time.Instant

import scaladex.core.model.Artifact
import scaladex.core.model.Language
import scaladex.core.model.License
import scaladex.core.model.Platform
import scaladex.core.model.Project
import scaladex.core.model.SemanticVersion

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.Artifact
import scaladex.core.model.Project

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,62 @@
package scaladex.core.api

import java.time.Instant

import scaladex.core.model.Artifact
import scaladex.core.model.Language
import scaladex.core.model.License
import scaladex.core.model.Platform
import scaladex.core.model.Project
import scaladex.core.model.SemanticVersion

/**
* 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 @@ -302,13 +301,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
)
}
Loading

0 comments on commit cfb11e6

Please sign in to comment.