Skip to content

Commit

Permalink
Merge pull request #672 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Update dependencies, new Core utilities, verify OpenAPI callback, refactor modules
  • Loading branch information
jaguililla committed Nov 2, 2023
2 parents 55b1301 + c071eec commit b68d208
Show file tree
Hide file tree
Showing 38 changed files with 1,151 additions and 327 deletions.
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import org.gradle.api.tasks.wrapper.Wrapper.DistributionType.ALL
*/

plugins {
kotlin("jvm") version("1.9.20-RC") apply(false)
kotlin("jvm") version("1.9.20") apply(false)

id("idea")
id("eclipse")
Expand All @@ -27,8 +27,8 @@ plugins {
id("com.github.jk1.dependency-license-report") version("2.5")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.2")
id("org.graalvm.buildtools.native") version("0.9.28") apply(false)
id("io.gitlab.arturbosch.detekt") version("1.23.1") apply(false)
id("me.champeau.jmh") version("0.7.1") apply(false)
id("io.gitlab.arturbosch.detekt") version("1.23.3") apply(false)
id("me.champeau.jmh") version("0.7.2") apply(false)
}

apply(from = "gradle/certificates.gradle")
Expand Down
39 changes: 39 additions & 0 deletions contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,42 @@ git log 1.2.0...1.3.0 \

git log 1.2.0...1.3.0 --date=iso8601 --reverse --pretty=format:'%an %ae'|sort|uniq >>CHANGELOG.md
```

## Documentation Guidelines
ONLY public members require documentation.

Some hints to write the comments are:
* Use the imperative form.
* Capitalize descriptions (parameters, receivers, return types, etc.) and end sentences with a dot.
* Complete all KDoc tags (I.e.: `@param`, `@receiver`, etc. for the methods that have them.
* Focus on what they do, not how, neither what it is.
* Not saying *method* or *property* (that is clear from the context).
* Comments to ignore warnings (false positives) should include an explanation.
* Explain corner cases, default values and allowed values/formats if it applies.
* These are recommendations, feel free to make an exception if you think it is required to explain
the use/structure of the code better.

## Logging Guidelines
Take care of the level assigned to the log statements:
* `error` some error stopped the correct processing of the request or the process.
* `warn` something failed and was ignored (it wasn't a big deal to stop request or process), but it
could be an issue later or with other data.
* `info` only for really useful information that is not written very often.
* `debug` for information with useful information to diagnose problems or failures (that could be
used to diagnose client code bugs).
* `trace` for low level details that are logged very often (information that could be used to fix
this library's bugs).

Rules of thumb:
* Prefer to group related information in a single log statement rather than using many of them.
* Do not log re-thrown exceptions. If this is done, chances are that this exception is logged twice
(making diagnosis harder). All not handled exceptions are logged at entry points (main or request
handlers), there is no need to log them in every catch, or where they are thrown.
* Catching an exception doesn't mean it is an error or warning. Assign categories based on the rules
above.
* Generally is a good idea to log the places where the program makes a decision (adding the
information that lead to the program flow selection). I.e.: "User <id> not deleted (not found in
the data store)".
* If some condition leads to default return values, it is a good place to put a logging statement to
add more information about it. I.e.: HTTP Request timeout (<relevant call information>), returning
empty array.
52 changes: 39 additions & 13 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public final class com/hexagonkt/core/DataKt {
}

public final class com/hexagonkt/core/DatesKt {
public static final fun getGMT_ZONE ()Ljava/time/ZoneId;
public static final fun parseDuration (Ljava/lang/String;)Ljava/time/Duration;
public static final fun parseLocalDate (Ljava/lang/String;)Ljava/time/LocalDate;
public static final fun parsePeriod (Ljava/lang/String;)Ljava/time/Period;
Expand All @@ -96,11 +97,13 @@ public final class com/hexagonkt/core/DatesKt {
public static final fun toNumber (Ljava/time/LocalDate;)I
public static final fun toNumber (Ljava/time/LocalDateTime;)J
public static final fun toNumber (Ljava/time/LocalTime;)I
public static final fun toTotalDays (Ljava/time/Period;)D
public static final fun withZone (Ljava/time/LocalDateTime;Ljava/time/ZoneId;)Ljava/time/ZonedDateTime;
public static synthetic fun withZone$default (Ljava/time/LocalDateTime;Ljava/time/ZoneId;ILjava/lang/Object;)Ljava/time/ZonedDateTime;
}

public final class com/hexagonkt/core/ExceptionsKt {
public static final fun check (Ljava/lang/String;[Lkotlin/jvm/functions/Function0;)V
public static final fun filterStackTrace (Ljava/lang/Throwable;Ljava/lang/String;)[Ljava/lang/StackTraceElement;
public static final fun getAssertEnabled ()Z
public static final fun getFail ()Ljava/lang/Void;
Expand All @@ -109,7 +112,6 @@ public final class com/hexagonkt/core/ExceptionsKt {
}

public final class com/hexagonkt/core/HelpersKt {
public static final fun check (Ljava/lang/String;[Lkotlin/jvm/functions/Function0;)V
public static final fun exec (Ljava/lang/String;Ljava/io/File;JZ)Ljava/lang/String;
public static final fun exec (Ljava/util/List;Ljava/io/File;JZ)Ljava/lang/String;
public static synthetic fun exec$default (Ljava/lang/String;Ljava/io/File;JZILjava/lang/Object;)Ljava/lang/String;
Expand Down Expand Up @@ -141,16 +143,22 @@ public final class com/hexagonkt/core/I18nKt {
public final class com/hexagonkt/core/Jvm {
public static final field INSTANCE Lcom/hexagonkt/core/Jvm;
public final fun getCharset ()Ljava/nio/charset/Charset;
public final fun getConsole ()Ljava/io/Console;
public final fun getCpuCount ()I
public final fun getHostName ()Ljava/lang/String;
public final fun getIp ()Ljava/lang/String;
public final fun getLocale ()Ljava/util/Locale;
public final fun getLocaleCode ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getOs ()Ljava/lang/String;
public final fun getOsKind ()Lcom/hexagonkt/core/OsKind;
public final fun getRuntime ()Ljava/lang/Runtime;
public final fun getTimeZone ()Ljava/util/TimeZone;
public final fun getVersion ()Ljava/lang/String;
public final fun getZoneId ()Ljava/time/ZoneId;
public final fun isConsole ()Z
public final fun loadSystemSettings (Ljava/util/Map;Z)V
public static synthetic fun loadSystemSettings$default (Lcom/hexagonkt/core/Jvm;Ljava/util/Map;ZILjava/lang/Object;)V
public final fun systemFlag (Ljava/lang/String;)Z
public final fun systemSetting (Lkotlin/reflect/KClass;Ljava/lang/String;)Ljava/lang/Object;
public final fun systemSettingOrNull (Lkotlin/reflect/KClass;Ljava/lang/String;)Ljava/lang/Object;
Expand Down Expand Up @@ -190,6 +198,16 @@ public final class com/hexagonkt/core/NetworkKt {
public static final fun urlOf (Ljava/lang/String;)Ljava/net/URL;
}

public final class com/hexagonkt/core/OsKind : java/lang/Enum {
public static final field LINUX Lcom/hexagonkt/core/OsKind;
public static final field MACOS Lcom/hexagonkt/core/OsKind;
public static final field UNIX Lcom/hexagonkt/core/OsKind;
public static final field WINDOWS Lcom/hexagonkt/core/OsKind;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/hexagonkt/core/OsKind;
public static fun values ()[Lcom/hexagonkt/core/OsKind;
}

public final class com/hexagonkt/core/ResourceNotFoundException : java/io/IOException {
public fun <init> (Ljava/lang/String;)V
}
Expand Down Expand Up @@ -440,14 +458,17 @@ public final class com/hexagonkt/core/security/KeyStoresKt {
}

public final class com/hexagonkt/core/text/Ansi {
public static final field CSI Ljava/lang/String;
public static final field INSTANCE Lcom/hexagonkt/core/text/Ansi;
public static final field RESET Ljava/lang/String;
public final fun getREGEX ()Lkotlin/text/Regex;
}

public final class com/hexagonkt/core/text/AnsiColor {
public static final field BLACK Ljava/lang/String;
public static final field BLACK_BG Ljava/lang/String;
public static final field BLINK Ljava/lang/String;
public static final field BLINK_OFF Ljava/lang/String;
public static final field BLUE Ljava/lang/String;
public static final field BLUE_BG Ljava/lang/String;
public static final field BOLD Ljava/lang/String;
public static final field BOLD_OFF Ljava/lang/String;
public static final field BRIGHT_BLACK Ljava/lang/String;
public static final field BRIGHT_BLACK_BG Ljava/lang/String;
public static final field BRIGHT_BLUE Ljava/lang/String;
Expand All @@ -464,28 +485,33 @@ public final class com/hexagonkt/core/text/Ansi {
public static final field BRIGHT_WHITE_BG Ljava/lang/String;
public static final field BRIGHT_YELLOW Ljava/lang/String;
public static final field BRIGHT_YELLOW_BG Ljava/lang/String;
public static final field CSI Ljava/lang/String;
public static final field CYAN Ljava/lang/String;
public static final field CYAN_BG Ljava/lang/String;
public static final field DEFAULT Ljava/lang/String;
public static final field DEFAULT_BG Ljava/lang/String;
public static final field GREEN Ljava/lang/String;
public static final field GREEN_BG Ljava/lang/String;
public static final field INSTANCE Lcom/hexagonkt/core/text/Ansi;
public static final field INVERSE Ljava/lang/String;
public static final field INVERSE_OFF Ljava/lang/String;
public static final field INSTANCE Lcom/hexagonkt/core/text/AnsiColor;
public static final field MAGENTA Ljava/lang/String;
public static final field MAGENTA_BG Ljava/lang/String;
public static final field RED Ljava/lang/String;
public static final field RED_BG Ljava/lang/String;
public static final field RESET Ljava/lang/String;
public static final field UNDERLINE Ljava/lang/String;
public static final field UNDERLINE_OFF Ljava/lang/String;
public static final field WHITE Ljava/lang/String;
public static final field WHITE_BG Ljava/lang/String;
public static final field YELLOW Ljava/lang/String;
public static final field YELLOW_BG Ljava/lang/String;
public final fun getREGEX ()Lkotlin/text/Regex;
}

public final class com/hexagonkt/core/text/AnsiEffect {
public static final field BLINK Ljava/lang/String;
public static final field BLINK_OFF Ljava/lang/String;
public static final field BOLD Ljava/lang/String;
public static final field BOLD_OFF Ljava/lang/String;
public static final field INSTANCE Lcom/hexagonkt/core/text/AnsiEffect;
public static final field INVERSE Ljava/lang/String;
public static final field INVERSE_OFF Ljava/lang/String;
public static final field UNDERLINE Ljava/lang/String;
public static final field UNDERLINE_OFF Ljava/lang/String;
}

public final class com/hexagonkt/core/text/CasesKt {
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/Dates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ private const val HOUR_OFFSET: Int = 10_000_000
private const val MINUTE_OFFSET: Int = 100_000
private const val SECOND_OFFSET: Int = 1_000
private const val NANO_OFFSET: Int = 1_000_000
private const val DAYS_PER_MONTH: Double = 30.4375

/** GMT zone ID. */
val GMT_ZONE: ZoneId by lazy { ZoneId.of("GMT") }

/**
* Convert a date time to a number with the following format: `YYYYMMDDHHmmss`.
Expand Down Expand Up @@ -143,6 +147,15 @@ fun Date.toLocalDateTime(): LocalDateTime =
fun Date.toLocalDate(): LocalDate =
this.toLocalDateTime().toLocalDate()

/**
* Calculate the aproximate number of days comprised in a time period.
*
* @receiver Period from which calculate the number of days.
* @return Aproximate number of days of the period.
*/
fun Period.toTotalDays(): Double =
(toTotalMonths() * DAYS_PER_MONTH) + days

/**
* Parse a time period allowing a more relaxed format: with spaces or commas, lowercase characters
* and not forcing the text to start with 'P'.
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,21 @@ fun Throwable.toText(prefix: String = ""): String =
this.filterStackTrace(prefix).joinToString(eol, eol) { "\tat $it" } +
if (this.cause == null) ""
else "${eol}Caused by: " + (this.cause as Throwable).toText(prefix)

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*/
fun check(message: String, vararg blocks: () -> Unit) {
val exceptions: List<Exception> = blocks.mapNotNull {
try {
it()
null
}
catch(e: Exception) {
e
}
}

if (exceptions.isNotEmpty())
throw MultipleException(message, exceptions)
}
20 changes: 0 additions & 20 deletions core/src/main/kotlin/com/hexagonkt/core/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ fun properties(url: URL): Map<String, String> =
.mapKeys { it.key as String }
.mapValues { it.value as String }

// PROCESSES ///////////////////////////////////////////////////////////////////////////////////////
/**
* Execute a lambda until no exception is thrown or a number of times is reached.
*
Expand Down Expand Up @@ -130,22 +129,3 @@ fun String.shell(
): String =
listOf(getenv("SHELL") ?: "bash", "-c", replace("""(\s+\\\s*)?\n""".toRegex(), ""))
.exec(workingDirectory, timeout, fail)

// ERROR HANDLING //////////////////////////////////////////////////////////////////////////////////
/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*/
fun check(message: String, vararg blocks: () -> Unit) {
val exceptions: List<Exception> = blocks.mapNotNull {
try {
it()
null
}
catch(e: Exception) {
e
}
}

if (exceptions.isNotEmpty())
throw MultipleException(message, exceptions)
}
54 changes: 53 additions & 1 deletion core/src/main/kotlin/com/hexagonkt/core/Jvm.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.hexagonkt.core

import com.hexagonkt.core.text.parseOrNull
import java.io.Console
import java.net.InetAddress
import java.nio.charset.Charset
import java.time.ZoneId
Expand All @@ -12,6 +13,25 @@ import kotlin.reflect.KClass
* Object with utilities to gather information about the running JVM.
*/
object Jvm {
private val systemSettingPattern: Regex by lazy { Regex("[a-zA-Z_]+[a-zA-Z0-9_]*") }

/** Operating system name ('os.name' property). If `null` throws an exception. */
val os: String by lazy { os() }

/** Operating system type. */
val osKind: OsKind by lazy { osKind() }

/**
* JVM Console, if the program don't have a console (i.e.: input or output redirected), an
* exception is thrown.
*/
val console: Console by lazy {
System.console() ?: error("Program doesn't have a console (I/O may be redirected)")
}

/** True if the program has a console (terminal, TTY, PTY...), false if I/O is piped. */
val isConsole: Boolean by lazy { System.console() != null }

/** Current JVM runtime. */
val runtime: Runtime by lazy { Runtime.getRuntime() }

Expand Down Expand Up @@ -63,6 +83,21 @@ object Jvm {
fun usedMemory(): String =
(runtime.totalMemory() - runtime.freeMemory()).let { "%,d".format(it / 1024) }

fun loadSystemSettings(settings: Map<String, String>, overwrite: Boolean = false) {
settings.keys.forEach {
check(it.matches(systemSettingPattern)) {
"Property name must match $systemSettingPattern ($it)"
}
}

val systemProperties = System.getProperties()
val properties =
if (overwrite) settings.entries
else settings.entries.filter { !systemProperties.containsKey(it.key) }

properties.forEach { (k, v) -> System.setProperty(k, v) }
}

/**
* Retrieve a setting by name by looking in the JVM system properties first and in OS
* environment variables if not found.
Expand Down Expand Up @@ -106,7 +141,24 @@ object Jvm {
systemSetting(T::class, name)

private fun systemSettingRaw(name: String): String? {
require(name.isNotBlank()) { "Setting name can not be blank" }
val correctName = name.matches(systemSettingPattern)
require(correctName) { "Setting name must match $systemSettingPattern" }
return System.getProperty(name, System.getenv(name))
}

/** Operating system name ('os.name' property). If `null` throws an exception. */
internal fun os(): String =
System.getProperty("os.name") ?: error("OS property ('os.name') not found")

/** Operating system type. */
internal fun osKind(): OsKind =
os().lowercase().let {
when {
it.contains("win") -> OsKind.WINDOWS
it.contains("mac") -> OsKind.MACOS
it.contains("nux") -> OsKind.LINUX
it.contains("nix") || it.contains("aix") -> OsKind.UNIX
else -> error("Unsupported OS: ${os()}")
}
}
}
8 changes: 8 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/OsKind.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.hexagonkt.core

enum class OsKind {
WINDOWS,
MACOS,
LINUX,
UNIX,
}

0 comments on commit b68d208

Please sign in to comment.