Skip to content
This repository has been archived by the owner on Oct 14, 2021. It is now read-only.

Commit

Permalink
Release installer paths from staging (#198)
Browse files Browse the repository at this point in the history
* Release installer paths

* Possible solution to #209 on staging (#211)

* Fix #209

* Added some reasoning

Co-authored-by: Martijn Verburg <martijnverburg@gmail.com>

Co-authored-by: Martijn Verburg <martijnverburg@gmail.com>
  • Loading branch information
johnoliver and karianna committed May 24, 2020
1 parent ad86961 commit b4d664b
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package net.adoptopenjdk.api.v3

import net.adoptopenjdk.api.v3.dataSources.APIDataStore
import net.adoptopenjdk.api.v3.routes.AssetsResource
import net.adoptopenjdk.api.v3.routes.BinaryResource
import net.adoptopenjdk.api.v3.routes.packages.BinaryResource
import net.adoptopenjdk.api.v3.routes.V1Route
import net.adoptopenjdk.api.v3.routes.VersionResource
import net.adoptopenjdk.api.v3.routes.info.AvailableReleasesResource
import net.adoptopenjdk.api.v3.routes.info.PlatformsResource
import net.adoptopenjdk.api.v3.routes.info.ReleaseListResource
import net.adoptopenjdk.api.v3.routes.info.VariantsResource
import net.adoptopenjdk.api.v3.routes.packages.InstallerResource
import net.adoptopenjdk.api.v3.routes.stats.DownloadStatsResource
import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition
import org.eclipse.microprofile.openapi.annotations.info.Info
Expand Down Expand Up @@ -65,7 +66,8 @@ class V3 : Application() {
ReleaseListResource::class.java,
VariantsResource::class.java,
VersionResource::class.java,
DownloadStatsResource::class.java
DownloadStatsResource::class.java,
InstallerResource::class.java
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package net.adoptopenjdk.api.v3.routes
package net.adoptopenjdk.api.v3.routes.packages

import net.adoptopenjdk.api.v3.JsonMapper
import net.adoptopenjdk.api.v3.OpenApiDocs
import net.adoptopenjdk.api.v3.dataSources.APIDataStore
import net.adoptopenjdk.api.v3.dataSources.SortOrder
import net.adoptopenjdk.api.v3.filters.BinaryFilter
import net.adoptopenjdk.api.v3.filters.ReleaseFilter
import net.adoptopenjdk.api.v3.models.APIError
import net.adoptopenjdk.api.v3.models.Architecture
import net.adoptopenjdk.api.v3.models.Binary
import net.adoptopenjdk.api.v3.models.HeapSize
import net.adoptopenjdk.api.v3.models.ImageType
import net.adoptopenjdk.api.v3.models.JvmImpl
import net.adoptopenjdk.api.v3.models.OperatingSystem
import net.adoptopenjdk.api.v3.models.Package
import net.adoptopenjdk.api.v3.models.Project
import net.adoptopenjdk.api.v3.models.Release
import net.adoptopenjdk.api.v3.models.ReleaseType
Expand All @@ -25,7 +21,6 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponses
import org.eclipse.microprofile.openapi.annotations.tags.Tag
import org.jboss.resteasy.annotations.jaxrs.PathParam
import org.jboss.resteasy.annotations.jaxrs.QueryParam
import java.net.URI
import javax.ws.rs.GET
import javax.ws.rs.HEAD
import javax.ws.rs.HeaderParam
Expand All @@ -39,7 +34,7 @@ import javax.ws.rs.core.Response
@Tag(name = "Binary")
@Path("/v3/binary/")
@Produces(MediaType.APPLICATION_JSON)
class BinaryResource {
class BinaryResource : PackageEndpoint() {

@GET
@HEAD
Expand Down Expand Up @@ -94,68 +89,29 @@ class BinaryResource {
@HeaderParam("User-Agent")
userAgent: String
): Response {
val releases = getReleases(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project)
return when (request.method) {
"HEAD" -> versionHead(userAgent, release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project)
else -> versionGet(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project)
}
}

private fun versionGet(
release_name: String?,
vendor: Vendor?,
os: OperatingSystem?,
arch: Architecture?,
image_type: ImageType?,
jvm_impl: JvmImpl?,
heap_size: HeapSize?,
project: Project?
): Response {
return getBinary(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project) { `package` ->
Response.temporaryRedirect(URI.create(`package`.link)).build()
"HEAD" -> versionHead(userAgent, releases)
else -> formResponse(releases)
}
}

private fun versionHead(
userAgent: String,
release_name: String?,
vendor: Vendor?,
os: OperatingSystem?,
arch: Architecture?,
image_type: ImageType?,
jvm_impl: JvmImpl?,
heap_size: HeapSize?,
project: Project?
releases: List<Release>
): Response {
return if (userAgent.contains("Gradle")) {
getBinary(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project) { `package` ->
return formResponse(releases) { `package` ->
Response.status(200)
.header("size", `package`.size)
.header("content-disposition", """attachment; filename="${`package`.name}"; filename*=UTF-8''${`package`.name}""")
.build()
}
} else {
versionGet(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project)
formResponse(releases)
}
}

private fun getBinary(
release_name: String?,
vendor: Vendor?,
os: OperatingSystem?,
arch: Architecture?,
image_type: ImageType?,
jvm_impl: JvmImpl?,
heap_size: HeapSize?,
project: Project?,
createResponse: (net.adoptopenjdk.api.v3.models.Package) -> Response
): Response {
val releaseFilter = ReleaseFilter(releaseName = release_name, vendor = vendor)
val binaryFilter = BinaryFilter(os, arch, image_type, jvm_impl, heap_size, project)
val releases = APIDataStore.getAdoptRepos().getFilteredReleases(releaseFilter, binaryFilter, SortOrder.DESC).toList()

return formResponse(releases, createResponse)
}

@GET
@Path("/latest/{feature_version}/{release_type}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}")
@Produces("application/octet-stream")
Expand Down Expand Up @@ -205,53 +161,21 @@ class BinaryResource {
@QueryParam("project")
project: Project?
): Response {
val releaseFilter = ReleaseFilter(releaseType = release_type, featureVersion = version, vendor = vendor)
val binaryFilter = BinaryFilter(os, arch, image_type, jvm_impl, heap_size, project)
val releases = APIDataStore.getAdoptRepos().getFilteredReleases(releaseFilter, binaryFilter, SortOrder.DESC).toList()

val comparator = compareBy<Release> { it.version_data.major }
.thenBy { it.version_data.minor }
.thenBy { it.version_data.security }
.thenBy { it.version_data.pre }
.thenBy { it.version_data.build }
.thenBy { it.version_data.adopt_build_number }
.thenBy { it.version_data.optional }
val releaseList = getRelease(release_type, version, vendor, os, arch, image_type, jvm_impl, heap_size, project)

val release = releases.sortedWith(comparator).lastOrNull()
val release = releaseList.lastOrNull()

return formResponse(if (release == null) emptyList() else listOf(release)) { `package` ->
Response.temporaryRedirect(URI.create(`package`.link)).build()
}
return formResponse(if (release == null) emptyList() else listOf(release))
}

private fun formResponse(releases: List<Release>, createResponse: (net.adoptopenjdk.api.v3.models.Package) -> Response): Response {
if (releases.size == 0) {
return formErrorResponse(Response.Status.NOT_FOUND, "No releases match the request")
} else if (releases.size > 1) {
val versions = releases
.map { it.release_name }
return formErrorResponse(Response.Status.BAD_REQUEST, "Multiple releases match request: $versions")
} else {
val binaries = releases.get(0).binaries
val packages = binaries
.map { it.`package` }
.filterNotNull()

if (packages.size == 0) {
return formErrorResponse(Response.Status.NOT_FOUND, "No binaries match the request")
} else if (packages.size > 1) {
val names = packages.map { it.name }
return formErrorResponse(Response.Status.BAD_REQUEST, "Multiple binaries match request: $names")
} else {
return createResponse(packages.first())
}
}
protected fun formResponse(
releases: List<Release>,
createResponse: (Package) -> Response = redirectToAsset()
): Response {
return formResponse(releases, extractPackage(), createResponse)
}

private fun formErrorResponse(status: Response.Status, message: String): Response {
return Response
.status(status)
.entity(JsonMapper.mapper.writeValueAsString(APIError(message)))
.build()
private fun extractPackage(): (binary: Binary) -> Package {
return { binary -> binary.`package` }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package net.adoptopenjdk.api.v3.routes.packages

import net.adoptopenjdk.api.v3.OpenApiDocs
import net.adoptopenjdk.api.v3.models.Architecture
import net.adoptopenjdk.api.v3.models.Binary
import net.adoptopenjdk.api.v3.models.HeapSize
import net.adoptopenjdk.api.v3.models.ImageType
import net.adoptopenjdk.api.v3.models.Installer
import net.adoptopenjdk.api.v3.models.JvmImpl
import net.adoptopenjdk.api.v3.models.OperatingSystem
import net.adoptopenjdk.api.v3.models.Project
import net.adoptopenjdk.api.v3.models.Release
import net.adoptopenjdk.api.v3.models.ReleaseType
import net.adoptopenjdk.api.v3.models.Vendor
import org.eclipse.microprofile.openapi.annotations.Operation
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType
import org.eclipse.microprofile.openapi.annotations.media.Schema
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses
import org.eclipse.microprofile.openapi.annotations.tags.Tag
import org.jboss.resteasy.annotations.jaxrs.PathParam
import org.jboss.resteasy.annotations.jaxrs.QueryParam
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Tag(name = "Installer")
@Path("/v3/installer/")
@Produces(MediaType.APPLICATION_JSON)
class InstallerResource : PackageEndpoint() {

@GET
@Path("/version/{release_name}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}")
@Produces("application/octet-stream")
@Operation(summary = "Redirects to the installer that matches your current query", description = "Redirects to the installer that matches your current query")
@APIResponses(value = [
APIResponse(responseCode = "307", description = "link to installer that matches your current query"),
APIResponse(responseCode = "400", description = "bad input parameter"),
APIResponse(responseCode = "404", description = "No matching installer found")
]
)
fun returnInstallerByVersion(
@Parameter(name = "os", description = "Operating System", required = true)
@PathParam("os")
os: OperatingSystem?,

@Parameter(name = "arch", description = "Architecture", required = true)
@PathParam("arch")
arch: Architecture?,

@Parameter(name = "release_name", description = OpenApiDocs.RELASE_NAME, required = true,
schema = Schema(defaultValue = "jdk-11.0.6+10", type = SchemaType.STRING)
)
@PathParam("release_name")
release_name: String?,

@Parameter(name = "image_type", description = "Image Type", required = true)
@PathParam("image_type")
image_type: ImageType?,

@Parameter(name = "jvm_impl", description = "JVM Implementation", required = true)
@PathParam("jvm_impl")
jvm_impl: JvmImpl?,

@Parameter(name = "heap_size", description = "Heap Size", required = true)
@PathParam("heap_size")
heap_size: HeapSize?,

@Parameter(name = "vendor", description = OpenApiDocs.VENDOR, required = true)
@PathParam("vendor")
vendor: Vendor?,

@Parameter(name = "project", description = "Project", schema = Schema(defaultValue = "jdk", enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false), required = false)
@QueryParam("project")
project: Project?
): Response {
val releases = getReleases(release_name, vendor, os, arch, image_type, jvm_impl, heap_size, project)
return formResponseInstaller(releases)
}

@GET
@Path("/latest/{feature_version}/{release_type}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}")
@Produces("application/octet-stream")
@Operation(summary = "Redirects to the installer that matches your current query", description = "Redirects to the installer that matches your current query")
@APIResponses(value = [
APIResponse(responseCode = "307", description = "link to installer that matches your current query"),
APIResponse(responseCode = "400", description = "bad input parameter"),
APIResponse(responseCode = "404", description = "No matching installer found")
]
)
fun returnInstaller(
@Parameter(name = "feature_version", description = OpenApiDocs.FEATURE_RELEASE, required = true,
schema = Schema(defaultValue = "8", type = SchemaType.INTEGER)
)
@PathParam("feature_version")
version: Int?,

@Parameter(name = "release_type", description = OpenApiDocs.RELEASE_TYPE, required = true)
@PathParam("release_type")
release_type: ReleaseType?,

@Parameter(name = "os", description = "Operating System", required = true)
@PathParam("os")
os: OperatingSystem?,

@Parameter(name = "arch", description = "Architecture", required = true)
@PathParam("arch")
arch: Architecture?,

@Parameter(name = "image_type", description = "Image Type", required = true)
@PathParam("image_type")
image_type: ImageType?,

@Parameter(name = "jvm_impl", description = "JVM Implementation", required = true)
@PathParam("jvm_impl")
jvm_impl: JvmImpl?,

@Parameter(name = "heap_size", description = "Heap Size", required = true)
@PathParam("heap_size")
heap_size: HeapSize?,

@Parameter(name = "vendor", description = OpenApiDocs.VENDOR, required = true)
@PathParam("vendor")
vendor: Vendor?,

@Parameter(name = "project", description = "Project", schema = Schema(defaultValue = "jdk", enumeration = ["jdk", "valhalla", "metropolis", "jfr"], required = false), required = false)
@QueryParam("project")
project: Project?
): Response {
val releaseList = getRelease(release_type, version, vendor, os, arch, image_type, jvm_impl, heap_size, project)

val release = releaseList
.lastOrNull { release ->
release.binaries.any { it.installer != null }
}

return formResponseInstaller(if (release == null) emptyList() else listOf(release))
}

private fun formResponseInstaller(releases: List<Release>): Response {
return formResponse(releases, extractInstaller(), redirectToAsset())
}

private fun extractInstaller(): (Binary) -> Installer? {
return { binary -> binary.installer }
}
}
Loading

0 comments on commit b4d664b

Please sign in to comment.