Skip to content

Commit

Permalink
Merge pull request #605 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jaguililla authored Feb 21, 2023
2 parents a84beac + 8063b63 commit 547a6ac
Show file tree
Hide file tree
Showing 54 changed files with 157 additions and 155 deletions.
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ plugins {
id("project-report")
id("org.jetbrains.dokka") version("1.7.20")
id("com.github.jk1.dependency-license-report") version("2.1")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.0")
id("io.gitlab.arturbosch.detekt") version("1.22.0") apply(false)
id("me.champeau.jmh") version("0.6.8") apply(false)
}
Expand Down Expand Up @@ -90,3 +91,7 @@ gradle.taskGraph.whenReady(closureOf<TaskExecutionGraph> {
}
}
})

apiValidation {
validationDisabled = true
}
2 changes: 1 addition & 1 deletion contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Other useful Gradle commands are:
* Build: `./gradlew build`
* Rebuild: `./gradlew clean build`
* Documentation: `./gradlew javadoc`
* Code Analysis: `./gradlew detektMain`, `./gradlew detektTest` or `./gradlew detekt`
* Code Analysis: `./gradlew detekt`
* Test: `./gradlew test`
* Run: `./gradlew ${MODULE}:run`
* Profile Build: `./gradlew ${TASK} --profile`
Expand Down
1 change: 0 additions & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

description = "Hexagon core utilities. Includes logging helpers."

Expand Down
28 changes: 25 additions & 3 deletions core/src/main/kotlin/com/hexagonkt/core/Strings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ fun String.decodeBase64(): ByteArray =
* @return .
*/
@Suppress("UNCHECKED_CAST") // All allowed types are checked at runtime
fun <T : Any> String?.parseOrNull(type: KClass<T>): T? =
this?.let {
fun <T : Any> String.parse(type: KClass<T>): T =
this.let {
require(type in parsedClasses) { "Unsupported type: ${type.qualifiedName}" }

when (type) {
Boolean::class -> this.toBooleanStrictOrNull()
Int::class -> this.toIntOrNull()
Expand All @@ -92,7 +94,27 @@ fun <T : Any> String?.parseOrNull(type: KClass<T>): T? =
LocalDateTime::class -> LocalDateTime.parse(this)
else -> error("Unsupported type: ${type.qualifiedName}")
}
} as? T
} as T

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
* @receiver .
* @param T .
* @param type .
* @return .
*/
@Suppress("UNCHECKED_CAST") // All allowed types are checked at runtime
fun <T : Any> String?.parseOrNull(type: KClass<T>): T? =
this?.let {
require(type in parsedClasses) { "Unsupported type: ${type.qualifiedName}" }
try {
parse(type)
}
catch (e: Exception) {
null
}
}

fun String.stripAnsi(): String =
replace(Ansi.REGEX, "")
Expand Down
6 changes: 4 additions & 2 deletions core/src/test/kotlin/com/hexagonkt/core/JvmTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ internal class JvmTest {
}

@Test fun `System settings handle parameter types correctly`() {

System.setProperty("validBoolean", true.toString())
System.setProperty("validInt", 123.toString())
System.setProperty("validLong", 456L.toString())
Expand Down Expand Up @@ -97,7 +96,10 @@ internal class JvmTest {
assertNull(Jvm.systemSettingOrNull(Boolean::class, "invalidBoolean"))

val type = System::class
val e = assertFailsWith<IllegalStateException> { Jvm.systemSettingOrNull<System>("error") }
val e = assertFailsWith<IllegalArgumentException> {
Jvm.systemSettingOrNull<System>("error")
}

assertEquals("Unsupported type: ${type.qualifiedName}", e.message)
}

Expand Down
5 changes: 5 additions & 0 deletions core/src/test/kotlin/com/hexagonkt/core/StringsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ internal class StringsTest {
tests.forEach { (k, v) -> assertNotNull(v.parseOrNull(k)) }
}

@Test fun `Invalid string transformations return null`() {
assertNull("2020-02-28".parseOrNull(InetAddress::class))
assertNull("2020-02-28".parseOrNull(URL::class))
}

@Test fun `String transformations work properly`() {
assertEquals(File("dir/f.txt"), "dir/f.txt".parseOrNull(File::class))
assertEquals(LocalDate.parse("2020-02-28"), "2020-02-28".parseOrNull(LocalDate::class))
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ org.gradle.warning.mode=all
org.gradle.console=plain

# Gradle
version=2.6.0
version=2.6.1
group=com.hexagonkt
description=The atoms of your platform

Expand Down Expand Up @@ -39,7 +39,7 @@ mockkVersion=1.13.4
junitVersion=5.9.2
gatlingVersion=3.9.1
jmhVersion=1.36
mkdocsMaterialVersion=9.0.12
mkdocsMaterialVersion=9.0.13
mermaidDokkaVersion=0.4.4
nativeToolsVersion=0.9.20

Expand Down
2 changes: 2 additions & 0 deletions gradle/detekt.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ detekt {
}

tasks.named("detekt") {
dependsOn("detektMain", "detektTest")

reports {
sarif.required.set(false)
html.required.set(false)
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# TODO Use Gradle 8 when Dokka support it
#distributionUrl=https://services.gradle.org/distributions/gradle-8.0-all.zip
#distributionUrl=https://services.gradle.org/distributions/gradle-8.0.1-all.zip
distributionUrl=https://services.gradle.org/distributions/gradle-7.6-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
Expand Down
1 change: 0 additions & 1 deletion handlers/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

description = "Handlers to be applied on events processing."
5 changes: 5 additions & 0 deletions helpers/src/main/kotlin/com/hexagonkt/helpers/Strings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import java.text.Normalizer.normalize
private const val VARIABLE_PREFIX = "{{"
private const val VARIABLE_SUFFIX = "}}"

val CAMEL_CASE: Regex = Regex("[a-z]+([A-Z][a-z0-9]+)+")
val PASCAL_CASE: Regex = Regex("([A-Z][a-z0-9]+)+")
val SNAKE_CASE: Regex = Regex("[A-Za-z]+(_[A-Za-z0-9]+)+")
val KEBAB_CASE: Regex = Regex("[A-Za-z]+(-[A-Za-z0-9]+)+")

/**
* Filter the target string substituting each key by its value. The keys format resembles Mustache's
* one: `{{key}}` and all occurrences are replaced by the supplied value.
Expand Down
19 changes: 19 additions & 0 deletions helpers/src/test/kotlin/com/hexagonkt/helpers/StringsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ internal class StringsTest {

enum class Size { S, M, L, X_L }

@Test fun `Case regex matches proper text`() {
assert("camelCaseTest1".matches(CAMEL_CASE))
assert("PascalCaseTest2".matches(PASCAL_CASE))
assert("Snake_Case_Test_3".matches(SNAKE_CASE))
assert("Kebab-Case-Test-4".matches(KEBAB_CASE))

assertFalse("0camelCaseTest1".matches(CAMEL_CASE))
assertFalse("CamelCaseTest1".matches(CAMEL_CASE))

assertFalse("1PascalCaseTest2".matches(PASCAL_CASE))
assertFalse("pascalCaseTest2".matches(PASCAL_CASE))

assertFalse("2_Snake_Case_Test_3".matches(SNAKE_CASE))
assertFalse("Snake-Case-Test-3".matches(SNAKE_CASE))

assertFalse("3-Kebab-Case-Test-4".matches(KEBAB_CASE))
assertFalse("Kebab_Case_Test_4".matches(KEBAB_CASE))
}

@Test fun `String case can changed`() {
val words = listOf("these", "are", "a", "few", "words")
assertEquals("These Are A Few Words", words.wordsToTitle())
Expand Down
1 change: 0 additions & 1 deletion http/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

description = "HTTP classes. These classes are shared among the HTTP client and the HTTP server."

Expand Down
1 change: 0 additions & 1 deletion http_client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

dependencies {
"api"(project(":http"))
Expand Down
1 change: 0 additions & 1 deletion http_client_jetty/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

dependencies {
val jettyVersion = properties["jettyVersion"]
Expand Down
1 change: 0 additions & 1 deletion http_server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/detekt.gradle")

dependencies {
"api"(project(":http"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import java.lang.System.nanoTime
*/
data class HttpServer(
private val adapter: HttpServerPort,
val handlers: List<HttpHandler>,
val handler: HttpHandler,
val settings: HttpServerSettings = HttpServerSettings()
) : Closeable {

Expand Down Expand Up @@ -72,20 +72,7 @@ data class HttpServer(
settings: HttpServerSettings = HttpServerSettings(),
block: ServerBuilder.() -> Unit
) :
this(adapter, listOf(path(block = block)), settings)

/**
* Utility constructor for the common case of having a single root handler.
*
* @param adapter The server engine.
* @param handler The only handler used for this server.
* @param settings Server settings. Port and address will be searched in this map.
*/
constructor(
adapter: HttpServerPort,
handler: HttpHandler,
settings: HttpServerSettings = HttpServerSettings(),
) : this(adapter, listOf(handler), settings)
this(adapter, path(block = block), settings)

override fun close() {
stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,6 @@ package com.hexagonkt.http.server
import com.hexagonkt.http.server.handlers.ServerBuilder
import com.hexagonkt.http.server.handlers.HttpHandler

/**
* Create a server and start it.
*
* @param adapter Adapter instance which implements [HttpServerPort].
* @param handlers List of handlers to be used by the server.
* @param settings Server settings info.
*
* @return The started [HttpServer] instance.
*/
fun serve(
adapter: HttpServerPort,
handlers: List<HttpHandler>,
settings: HttpServerSettings = HttpServerSettings()
): HttpServer =
HttpServer(adapter, handlers, settings).apply { start() }

/**
* Create a server and start it.
*
Expand All @@ -33,7 +17,7 @@ fun serve(
handler: HttpHandler,
settings: HttpServerSettings = HttpServerSettings()
): HttpServer =
serve(adapter, listOf(handler), settings)
HttpServer(adapter, handler, settings).apply { start() }

/**
* Create a server and start it.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.hexagonkt.http.server.handlers

import com.hexagonkt.core.media.TEXT_PLAIN
import com.hexagonkt.core.toText
import com.hexagonkt.handlers.Context
import com.hexagonkt.handlers.EventContext
import com.hexagonkt.handlers.Handler
import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.HttpMethod.GET
import com.hexagonkt.http.model.HttpProtocol.HTTP
import com.hexagonkt.http.model.HttpStatusType.SERVER_ERROR
import com.hexagonkt.http.server.model.HttpServerCall
import com.hexagonkt.http.server.model.HttpServerRequest
import com.hexagonkt.http.server.model.HttpServerRequestPort
Expand All @@ -16,6 +20,51 @@ sealed interface HttpHandler : Handler<HttpServerCall> {

fun addPrefix(prefix: String): HttpHandler

fun byMethod(): Map<HttpMethod, HttpHandler> =
serverPredicate.methods.associateWith { filter(it) }

fun filter(method: HttpMethod): HttpHandler =
when (this) {
is PathHandler ->
copy(
serverPredicate = serverPredicate.clearMethods(),
handlers = handlers
.filter {
val methods = it.serverPredicate.methods
method in methods || methods.isEmpty()
}
.map { it.filter(method) }
)

is OnHandler ->
copy(serverPredicate = serverPredicate.clearMethods())

is FilterHandler ->
copy(serverPredicate = serverPredicate.clearMethods())

is AfterHandler ->
copy(serverPredicate = serverPredicate.clearMethods())
}

fun processContext(request: HttpServerRequestPort): Context<HttpServerCall> =
process(EventContext(HttpServerCall(request = request), predicate)).let {
val event = it.event
val response = event.response
val exception = it.exception

if (exception != null && response.status.type != SERVER_ERROR)
it.with(
event = event.copy(
response = response.copy(
body = exception.toText(),
contentType = ContentType(TEXT_PLAIN),
status = INTERNAL_SERVER_ERROR_500,
)
)
)
else it
}

fun process(request: HttpServerRequestPort): HttpServerResponse =
EventContext(HttpServerCall(request = request), predicate).let { context ->
if (serverPredicate(context)) process(context).event.response
Expand Down
Loading

0 comments on commit 547a6ac

Please sign in to comment.