Skip to content

Commit

Permalink
Merge pull request #403 from HandyMenny/refactor-javalin
Browse files Browse the repository at this point in the history
Refactor JavalinApp
- Move routes and jsonMapper in separate classes
- Unify exception handling
- Create new JavalinServer each time with newServer()
- Drop /store/status and /version
  • Loading branch information
handymenny authored Feb 7, 2024
2 parents 2d62996 + c706577 commit 950aa71
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 317 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package it.smartphonecombo.uecapabilityparser.server

import io.javalin.json.JsonMapper
import it.smartphonecombo.uecapabilityparser.extension.custom
import java.io.InputStream
import java.lang.reflect.Type
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.serializer

@OptIn(ExperimentalSerializationApi::class)
@Suppress("UNCHECKED_CAST")
object CustomJsonMapper : JsonMapper {
override fun <T : Any> fromJsonString(json: String, targetType: Type): T {
val deserializer = serializer(targetType) as KSerializer<T>
return Json.custom().decodeFromString(deserializer, json)
}

override fun <T : Any> fromJsonStream(json: InputStream, targetType: Type): T {
val deserializer = serializer(targetType) as KSerializer<T>
return Json.custom().decodeFromStream(deserializer, json)
}

override fun toJsonString(obj: Any, type: Type): String {
val serializer = serializer(obj.javaClass)
return Json.custom().encodeToString(serializer, obj)
}
}
368 changes: 97 additions & 271 deletions src/main/java/it/smartphonecombo/uecapabilityparser/server/JavalinApp.kt

Large diffs are not rendered by default.

147 changes: 147 additions & 0 deletions src/main/java/it/smartphonecombo/uecapabilityparser/server/Routes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package it.smartphonecombo.uecapabilityparser.server

import io.javalin.http.ContentType
import io.javalin.http.Context
import it.smartphonecombo.uecapabilityparser.extension.attachFile
import it.smartphonecombo.uecapabilityparser.extension.bodyAsClassEfficient
import it.smartphonecombo.uecapabilityparser.extension.custom
import it.smartphonecombo.uecapabilityparser.extension.mutableListWithCapacity
import it.smartphonecombo.uecapabilityparser.extension.notFound
import it.smartphonecombo.uecapabilityparser.extension.toInputSource
import it.smartphonecombo.uecapabilityparser.io.IOUtils
import it.smartphonecombo.uecapabilityparser.model.Capabilities
import it.smartphonecombo.uecapabilityparser.model.LogType
import it.smartphonecombo.uecapabilityparser.model.MultiCapabilities
import it.smartphonecombo.uecapabilityparser.model.index.LibraryIndex
import it.smartphonecombo.uecapabilityparser.query.Query
import it.smartphonecombo.uecapabilityparser.query.SearchableField
import it.smartphonecombo.uecapabilityparser.util.Config
import it.smartphonecombo.uecapabilityparser.util.MultiParsing
import it.smartphonecombo.uecapabilityparser.util.Parsing
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import kotlin.contracts.ExperimentalContracts
import kotlinx.serialization.json.Json

object Routes {
private val dataFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss")
private val idRegex = "[a-f0-9-]{36}(?:-[0-9]+)?".toRegex()

@OptIn(ExperimentalContracts::class)
private fun validateId(id: String?) {
kotlin.contracts.contract { returns() implies (id != null) }
if (id?.matches(idRegex) != true) throw IllegalArgumentException("Wrong id")
}

fun parse(ctx: Context, store: String?, index: LibraryIndex, compression: Boolean) {
val request = ctx.bodyAsClassEfficient<RequestParse>()
val parsed = Parsing.fromRequest(request)!!
ctx.json(parsed.capabilities)
if (store != null) parsed.store(index, store, compression)
}

fun parseMultiPart(ctx: Context, store: String?, index: LibraryIndex, compression: Boolean) {
val requestsStr = ctx.formParam("requests")!!
val requestsJson = Json.custom().decodeFromString<List<RequestMultiPart>>(requestsStr)
val files = ctx.uploadedFiles()
val parsed = MultiParsing.fromRequest(requestsJson, files)!!
ctx.json(parsed.getMultiCapabilities())
if (store != null) parsed.store(index, store, compression)
}

fun csv(ctx: Context) {
val request = ctx.bodyAsClassEfficient<RequestCsv>()
val comboList = request.input
val type = request.type
val date = dataFormatter.format(ZonedDateTime.now(ZoneOffset.UTC))
val newFmt = (request as? RequestCsv.LteCa)?.newCsvFormat ?: false
ctx.attachFile(
IOUtils.toCsv(comboList, newFmt).toInputSource(),
"${type}-${date}.csv",
ContentType.TEXT_CSV
)
}

fun status(ctx: Context, maxRequestSize: Long, endpoints: List<String>) {
val version = Config.getOrDefault("project.version", "")
val logTypes = LogType.validEntries
val status =
ServerStatus(
version,
endpoints,
logTypes,
maxRequestSize,
SearchableField.getAllSearchableFields()
)
ctx.json(status)
}

fun storeList(ctx: Context, index: LibraryIndex) {
ctx.json(index)
}

fun storeGetItem(ctx: Context, index: LibraryIndex) {
val id = ctx.queryParam("id") ?: throw IllegalArgumentException("Wrong id")
val item = index.find(id) ?: return ctx.notFound()
ctx.json(item)
}

fun storeGetMultiItem(ctx: Context, index: LibraryIndex) {
val id = ctx.queryParam("id") ?: throw IllegalArgumentException("Wrong id")
val item = index.findMulti(id) ?: return ctx.notFound()
ctx.json(item)
}

fun storeGetOutput(ctx: Context, index: LibraryIndex, store: String) {
val id = ctx.queryParam("id")
validateId(id)

val capabilities = index.getOutput(id, store) ?: return ctx.notFound()
ctx.json(capabilities)
}

fun storeGetMultiOutput(ctx: Context, index: LibraryIndex, store: String) {
val id = ctx.queryParam("id")
validateId(id)

val multiIndexLine = index.findMulti(id) ?: return ctx.notFound()
val indexLineIds = multiIndexLine.indexLineIds
val capabilitiesList = mutableListWithCapacity<Capabilities>(indexLineIds.size)
for (indexId in indexLineIds) {
val capabilities = index.getOutput(indexId, store) ?: continue
capabilitiesList.add(capabilities)
}

val multiCapabilities =
MultiCapabilities(capabilitiesList, multiIndexLine.description, multiIndexLine.id)
ctx.json(multiCapabilities)
}

fun storeGetInput(ctx: Context, index: LibraryIndex, store: String) {
val id = ctx.queryParam("id")
validateId(id)

val indexLine = index.findByInput(id) ?: return ctx.notFound()
val compressed = indexLine.compressed
val filePath = "$store/input/$id"

val file = IOUtils.getInputSource(filePath, compressed) ?: return ctx.notFound()
ctx.attachFile(file, id, ContentType.APPLICATION_OCTET_STREAM)
}

fun storeListFiltered(ctx: Context, index: LibraryIndex, store: String) {
val request = ctx.bodyAsClassEfficient<Query>()
val result = index.filterByQuery(request, store)
ctx.json(result)
}

fun getOpenApi(ctx: Context) {
val openapi = {}.javaClass.getResourceAsStream("/swagger/openapi.json")
if (openapi != null) {
val text = openapi.reader().readText()
ctx.contentType(ContentType.JSON)
ctx.result(text)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object ServerMode {
* used by the server (useful for input 0)
*/
fun run(port: Int): Int {
val app = JavalinApp().app
val app = JavalinApp().newServer()
app.start(port)
Runtime.getRuntime().addShutdownHook(Thread { app.stop() })
return app.port()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ internal class ServerModeCompressionTest {
json: Boolean = true,
gzip: Boolean = false
) =
JavalinTest.test(JavalinApp().app) { _, client ->
JavalinTest.test(JavalinApp().newServer()) { _, client ->
val response = client.get(url)
Assertions.assertEquals(HttpStatus.OK.code, response.code)
val actualText = response.body?.string() ?: ""
Expand All @@ -140,7 +140,7 @@ internal class ServerModeCompressionTest {
}

private fun storeTest(url: String, request: JsonObject, oraclePath: String) =
JavalinTest.test(JavalinApp().app) { _, client ->
JavalinTest.test(JavalinApp().newServer()) { _, client ->
val response = client.post(url, request)
Assertions.assertEquals(HttpStatus.OK.code, response.code)
capabilitiesAssertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test

internal class ServerModeCsvTest {
private val path = "src/test/resources/server"
private val app = JavalinApp().app
private val app = JavalinApp().newServer()
private val endpoint = arrayOf("/csv/", "/csv").random()

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package it.smartphonecombo.uecapabilityparser.server

import io.javalin.Javalin
import io.javalin.http.HttpStatus
import io.javalin.testtools.JavalinTest
import it.smartphonecombo.uecapabilityparser.extension.custom
Expand Down Expand Up @@ -31,6 +30,7 @@ import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.MethodOrderer
Expand All @@ -40,7 +40,6 @@ import org.junit.jupiter.api.TestMethodOrder
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
internal class ServerModeFilterTest {
private val endpoint = arrayOf("/store/list/filtered/", "/store/list/filtered").random()
private var app: Javalin = JavalinApp().app

@Test
fun emptyResult() {
Expand Down Expand Up @@ -302,7 +301,7 @@ internal class ServerModeFilterTest {
}

private fun javalinTest(request: JsonObject, oraclePath: String) {
JavalinTest.test(app) { _, client ->
JavalinTest.test(app.newServer()) { _, client ->
val response = client.post(endpoint, request)
Assertions.assertEquals(HttpStatus.OK.code, response.code)

Expand All @@ -314,19 +313,28 @@ internal class ServerModeFilterTest {
}

private fun javalinErrorTest(request: JsonObject, errorCode: Int) {
JavalinTest.test(app) { _, client ->
JavalinTest.test(app.newServer()) { _, client ->
val response = client.post(endpoint, request)
Assertions.assertEquals(errorCode, response.code)
}
}

companion object {
private val path = "src/test/resources/server/"
private lateinit var app: JavalinApp

@JvmStatic
@BeforeAll
fun setup() {
Config["store"] = "$path/inputForQuery/"
Config["cache"] = "100"
app = JavalinApp()
}

@JvmStatic
@AfterAll
fun teardown() {
Config.clear()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test
internal class ServerModeMultiPartParseTest {
private val inputPath = "src/test/resources/cli/input/"
private val oraclePath = "src/test/resources/server/oracleForMultiParse/"
private val app = JavalinApp().app
private val app = JavalinApp().newServer()
private val endpoint = arrayOf("/parse/multiPart", "/parse/multiPart/").random()

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ internal class ServerModeMultiStoreTest {
}

private fun getTest(url: String, oraclePath: String, json: Boolean = true) =
JavalinTest.test(JavalinApp().app) { _, client ->
JavalinTest.test(JavalinApp().newServer()) { _, client ->
val response = client.get(url)
Assertions.assertEquals(HttpStatus.OK.code, response.code)
val actualText = response.body?.string() ?: ""
Expand All @@ -262,7 +262,7 @@ internal class ServerModeMultiStoreTest {
files: List<String>,
oraclePath: String
) =
JavalinTest.test(JavalinApp().app) { _, client ->
JavalinTest.test(JavalinApp().newServer()) { _, client ->
val response =
client.request(
UtilityForTests.multiPartRequest(client.origin + url, request, files)
Expand Down Expand Up @@ -328,7 +328,7 @@ internal class ServerModeMultiStoreTest {
}

private fun getTestError(url: String, statusCode: Int) =
JavalinTest.test(JavalinApp().app) { _, client ->
JavalinTest.test(JavalinApp().newServer()) { _, client ->
val response = client.get(url)
Assertions.assertEquals(statusCode, response.code)
}
Expand Down
Loading

0 comments on commit 950aa71

Please sign in to comment.