Skip to content

Commit 6d2c81e

Browse files
RateLimiting improvements (#53)
- Change from hardcoded ratelimit backOff to use `Retry-After` header instead - Switch to spotless/ktfmt - Fix `kotlin.time` migration - Switch to JDK 21 - Add `focDate` to Issue
1 parent 458acfd commit 6d2c81e

39 files changed

+1510
-1652
lines changed

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- uses: actions/setup-java@v4
2828
with:
2929
distribution: 'temurin'
30-
java-version: '17'
30+
java-version: '21'
3131
- uses: actions/cache@v4
3232
with:
3333
path: |

.github/workflows/testing.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ on:
88
branches:
99
- main
1010

11+
permissions:
12+
contents: read
13+
1114
jobs:
1215
gradle-test:
1316
runs-on: ubuntu-latest
@@ -17,7 +20,7 @@ jobs:
1720
- uses: actions/setup-java@v4
1821
with:
1922
distribution: 'temurin'
20-
java-version: '17'
23+
java-version: '21'
2124
- uses: actions/cache@v4
2225
with:
2326
path: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ jte-classes/
77
logs/
88

99
### Files ###
10+
.envrc

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Kraken
22

3-
![Java Version](https://img.shields.io/badge/Temurin-17-green?style=flat-square&logo=eclipse-adoptium)
4-
![Kotlin Version](https://img.shields.io/badge/Kotlin-2.1.0-green?style=flat-square&logo=kotlin)
3+
![Java Version](https://img.shields.io/badge/Temurin-21-green?style=flat-square&logo=eclipse-adoptium)
4+
![Kotlin Version](https://img.shields.io/badge/Kotlin-2.2.0-green?style=flat-square&logo=kotlin)
55
![Status](https://img.shields.io/badge/Status-Beta-yellowgreen?style=flat-square)
66

7-
[![Gradle](https://img.shields.io/badge/Gradle-8.12.0-informational?style=flat-square&logo=gradle)](https://github.com/gradle/gradle)
8-
[![Ktlint](https://img.shields.io/badge/Ktlint-1.5.0-informational?style=flat-square)](https://github.com/pinterest/ktlint)
7+
[![Gradle](https://img.shields.io/badge/Gradle-8.14.3-informational?style=flat-square&logo=gradle)](https://github.com/gradle/gradle)
8+
[![Spotless](https://img.shields.io/badge/Spotless-7.1.0-informational?style=flat-square)](https://github.com/diffplug/spotless)
99

1010
[![Github - Version](https://img.shields.io/github/v/tag/Buried-In-Code/Kraken?logo=Github&label=Version&style=flat-square)](https://github.com/Buried-In-Code/Kraken/tags)
1111
[![Github - License](https://img.shields.io/github/license/Buried-In-Code/Kraken?logo=Github&label=License&style=flat-square)](https://opensource.org/licenses/MIT)
@@ -30,7 +30,7 @@ Then, add Kraken as a dependency.
3030

3131
```kts
3232
dependencies {
33-
implementation("com.github.Buried-In-Code:Kraken:0.2.3")
33+
implementation("com.github.Buried-In-Code:Kraken:0.4.0")
3434
}
3535
```
3636

@@ -40,6 +40,7 @@ dependencies {
4040
import github.buriedincode.kraken.Metron
4141
import github.buriedincode.kraken.SQLiteCache
4242
import github.buriedincode.kraken.AuthenticationException
43+
import github.buriedincode.kraken.RateLimitException
4344
import github.buriedincode.kraken.ServiceException
4445

4546
fun main() {
@@ -64,6 +65,8 @@ fun main() {
6465

6566
} catch (ae: AuthenticationException) {
6667
println("Invalid Metron Username/Password.")
68+
} catch(re: RatelimitException) {
69+
println("Rate limit exceeded. Please try again later.")
6770
} catch (se: ServiceException) {
6871
println("Unsuccessful request: ${se.message}")
6972
}

build.gradle.kts

Lines changed: 126 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,176 @@
11
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
22
import java.net.HttpURLConnection
3-
import java.net.URL
3+
import java.net.URI
44
import java.nio.file.Files
55
import java.nio.file.StandardOpenOption
6+
import kotlin.io.path.absolutePathString
7+
import kotlin.io.path.createDirectories
8+
import kotlin.io.path.div
69

710
plugins {
8-
`java-library`
9-
alias(libs.plugins.kotlin.jvm)
10-
alias(libs.plugins.kotlinx.serialization)
11-
alias(libs.plugins.dokka)
12-
alias(libs.plugins.ktlint)
13-
alias(libs.plugins.versions)
14-
`maven-publish`
11+
`java-library`
12+
alias(libs.plugins.kotlin.jvm)
13+
alias(libs.plugins.kotlinx.serialization)
14+
alias(libs.plugins.dokka)
15+
alias(libs.plugins.spotless)
16+
alias(libs.plugins.versions)
17+
`maven-publish`
1518
}
1619

1720
println("Kotlin v${KotlinVersion.CURRENT}")
21+
1822
println("Java v${System.getProperty("java.version")}")
23+
1924
println("Arch: ${System.getProperty("os.arch")}")
2025

2126
group = "github.buriedincode"
22-
version = "0.3.1"
27+
28+
version = "0.4.0"
2329

2430
repositories {
25-
mavenCentral()
26-
mavenLocal()
31+
mavenCentral()
32+
mavenLocal()
2733
}
2834

2935
dependencies {
30-
implementation(libs.bundles.kotlinx.serialization)
31-
implementation(libs.kotlin.logging)
32-
runtimeOnly(libs.sqlite.jdbc)
33-
testImplementation(libs.junit.jupiter)
34-
testRuntimeOnly(libs.junit.platform.launcher)
35-
testRuntimeOnly(libs.kotlin.reflect)
36-
testRuntimeOnly(libs.log4j2.slf4j2.impl)
37-
}
36+
implementation(libs.bundles.kotlinx.serialization)
37+
implementation(libs.kotlin.logging)
3838

39-
java {
40-
toolchain {
41-
languageVersion = JavaLanguageVersion.of(17)
42-
}
43-
}
39+
runtimeOnly(libs.sqlite.jdbc)
40+
41+
testImplementation(libs.junit.jupiter)
4442

45-
kotlin {
46-
jvmToolchain(17)
43+
testRuntimeOnly(libs.junit.platform.launcher)
44+
testRuntimeOnly(libs.log4j2.slf4j2)
4745
}
4846

49-
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
50-
version = "1.5.0"
47+
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
48+
49+
kotlin { jvmToolchain(21) }
50+
51+
spotless {
52+
kotlin {
53+
ktfmt().kotlinlangStyle().configure {
54+
it.setMaxWidth(120)
55+
it.setBlockIndent(2)
56+
it.setContinuationIndent(2)
57+
it.setRemoveUnusedImports(true)
58+
it.setManageTrailingCommas(true)
59+
}
60+
}
61+
kotlinGradle {
62+
ktfmt().kotlinlangStyle().configure {
63+
it.setMaxWidth(120)
64+
it.setBlockIndent(2)
65+
it.setContinuationIndent(2)
66+
it.setRemoveUnusedImports(true)
67+
it.setManageTrailingCommas(true)
68+
}
69+
}
5170
}
5271

5372
tasks.test {
54-
environment("METRON__USERNAME", System.getenv("METRON__USERNAME"))
55-
environment("METRON__PASSWORD", System.getenv("METRON__PASSWORD"))
56-
useJUnitPlatform()
57-
testLogging {
58-
events("passed", "skipped", "failed")
59-
}
73+
environment("METRON__USERNAME", System.getenv("METRON__USERNAME"))
74+
environment("METRON__PASSWORD", System.getenv("METRON__PASSWORD"))
75+
useJUnitPlatform()
76+
testLogging { events("passed", "skipped", "failed") }
6077
}
6178

6279
fun isNonStable(version: String): Boolean {
63-
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
64-
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
65-
val isStable = stableKeyword || regex.matches(version)
66-
return isStable.not()
80+
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
81+
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
82+
val isStable = stableKeyword || regex.matches(version)
83+
return isStable.not()
6784
}
6885

6986
tasks.withType<DependencyUpdatesTask> {
70-
gradleReleaseChannel = "current"
71-
resolutionStrategy {
72-
componentSelection {
73-
all {
74-
if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
75-
reject("Release candidate")
76-
}
77-
}
87+
gradleReleaseChannel = "current"
88+
checkForGradleUpdate = true
89+
checkConstraints = false
90+
checkBuildEnvironmentConstraints = false
91+
resolutionStrategy {
92+
componentSelection {
93+
all {
94+
if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
95+
reject("Release candidate")
7896
}
97+
}
7998
}
99+
}
80100
}
81101

82-
publishing {
83-
publications {
84-
create<MavenPublication>("kraken") {
85-
from(components["java"])
86-
}
87-
}
88-
}
102+
publishing { publications { create<MavenPublication>("kraken") { from(components["java"]) } } }
89103

90104
tasks.register("processReadme") {
91-
group = "documentation"
92-
description = "Processes the README.md file to inline SVG badges."
105+
group = "documentation"
106+
description = "Processes the README.md file to inline SVG badges."
93107

94-
doLast {
95-
val linkedBadgePattern = """\[\!\[(.*?)\]\((.*?)\)\]\((.*?)\)""".toRegex() // [![alt](url)](link)
96-
val badgePattern = """\!\[(.*?)\]\((.*?)\)""".toRegex() // ![alt](url)
108+
doLast {
109+
val linkedBadgePattern = """\[\!\[(.*?)\]\((.*?)\)\]\((.*?)\)""".toRegex() // [![alt](url)](link)
110+
val badgePattern = """\!\[(.*?)\]\((.*?)\)""".toRegex() // ![alt](url)
97111

98-
val inputPath = project.rootDir.toPath().resolve("README.md")
99-
val outputPath = project.buildDir.toPath().resolve("Processed-README.md")
112+
val inputPath = project.rootDir.toPath() / "README.md"
113+
val outputPath = project.layout.buildDirectory.get().asFile.toPath() / "Processed-README.md"
100114

101-
if (!Files.exists(inputPath)) {
102-
throw IllegalStateException("${inputPath.toAbsolutePath()} not found.")
103-
}
115+
if (!Files.exists(inputPath)) {
116+
throw IllegalStateException("${inputPath.absolutePathString()} not found.")
117+
}
104118

105-
var content = Files.readAllLines(inputPath).joinToString("\n")
106-
content = content.replaceFirst("# Kraken", "# Module Kraken")
107-
108-
fun fetchSvg(url: String): String? {
109-
return try {
110-
val connection = URL(url).openConnection() as HttpURLConnection
111-
connection.requestMethod = "GET"
112-
connection.connect()
113-
114-
if (connection.responseCode == 200 && connection.contentType.contains("image/svg+xml")) {
115-
connection.inputStream.bufferedReader().use { it.readText() }
116-
} else {
117-
println("Warning: $url is not an SVG badge")
118-
null
119-
}
120-
} catch (e: Exception) {
121-
println("Error fetching $url: ${e.message}")
122-
null
123-
}
124-
}
119+
var content = Files.readAllLines(inputPath).joinToString("\n")
120+
content = content.replaceFirst("# Kraken", "# Module Kraken")
125121

126-
fun processContent(pattern: Regex, replaceFunction: (MatchResult) -> String): String {
127-
return pattern.replace(content) { match ->
128-
replaceFunction(match)
129-
}
130-
}
122+
fun fetchSvg(url: String): String? {
123+
return try {
124+
val connection = URI.create(url).toURL().openConnection() as HttpURLConnection
125+
connection.requestMethod = "GET"
126+
connection.connect()
131127

132-
content = processContent(linkedBadgePattern) { match ->
133-
val altText = match.groupValues[1]
134-
val badgeUrl = match.groupValues[2]
135-
val linkUrl = match.groupValues[3]
136-
val svgContent = fetchSvg(badgeUrl)
137-
if (svgContent != null) {
138-
"""<a href="$linkUrl" target="_blank">$svgContent</a>"""
139-
} else {
140-
"""<a href="$linkUrl" target="_blank"><img alt="$altText" src="$badgeUrl" /></a>"""
141-
}
142-
}
143-
144-
content = processContent(badgePattern) { match ->
145-
val altText = match.groupValues[1]
146-
val badgeUrl = match.groupValues[2]
147-
val svgContent = fetchSvg(badgeUrl)
148-
svgContent ?: """<img alt="$altText" src="$badgeUrl" />"""
128+
if (connection.responseCode == 200 && connection.contentType.contains("image/svg+xml")) {
129+
connection.inputStream.bufferedReader().use { it.readText() }
130+
} else {
131+
println("Warning: $url is not an SVG badge")
132+
null
149133
}
134+
} catch (e: Exception) {
135+
println("Error fetching $url: ${e.message}")
136+
null
137+
}
138+
}
150139

151-
Files.createDirectories(outputPath.parent)
152-
Files.writeString(outputPath, content, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
153-
println("Processing complete. Output written to ${outputPath.toAbsolutePath()}")
140+
fun processContent(pattern: Regex, replaceFunction: (MatchResult) -> String): String {
141+
return pattern.replace(content) { match -> replaceFunction(match) }
154142
}
143+
144+
content =
145+
processContent(linkedBadgePattern) { match ->
146+
val altText = match.groupValues[1]
147+
val badgeUrl = match.groupValues[2]
148+
val linkUrl = match.groupValues[3]
149+
val svgContent = fetchSvg(badgeUrl)
150+
if (svgContent != null) {
151+
"""<a href="$linkUrl" target="_blank">$svgContent</a>"""
152+
} else {
153+
"""<a href="$linkUrl" target="_blank"><img alt="$altText" src="$badgeUrl" /></a>"""
154+
}
155+
}
156+
157+
content =
158+
processContent(badgePattern) { match ->
159+
val altText = match.groupValues[1]
160+
val badgeUrl = match.groupValues[2]
161+
val svgContent = fetchSvg(badgeUrl)
162+
svgContent ?: """<img alt="$altText" src="$badgeUrl" />"""
163+
}
164+
165+
outputPath.parent.createDirectories()
166+
Files.writeString(outputPath, content, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
167+
println("Processing complete. Output written to ${outputPath.absolutePathString()}")
168+
}
155169
}
156170

157171
tasks.dokkaHtml {
158-
dependsOn("processReadme")
159-
dokkaSourceSets {
160-
configureEach {
161-
includes.from(project.buildDir.toPath().resolve("Processed-README.md"))
162-
}
163-
}
172+
dependsOn("processReadme")
173+
dokkaSourceSets {
174+
configureEach { includes.from(project.layout.buildDirectory.get().asFile.toPath() / "Processed-README.md") }
175+
}
164176
}

cache.sqlite

4 KB
Binary file not shown.

gradle/libs.versions.toml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ kotlin = "2.2.0"
55
dokka = { id = "org.jetbrains.dokka", version = "2.0.0" }
66
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
77
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
8-
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "13.0.0" }
8+
spotless = { id = "com.diffplug.spotless", version = "7.1.0" }
99
versions = { id = "com.github.ben-manes.versions", version = "0.52.0" }
1010

1111
[libraries]
12-
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.13.3" }
13-
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
14-
kotlin-logging = { group = "io.github.oshai", name = "kotlin-logging-jvm", version = "7.0.7" }
15-
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
16-
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version = "0.7.1-0.6.x-compat" }
17-
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.9.0" }
18-
log4j2-slf4j2-impl = { group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl", version = "2.25.1" }
19-
sqlite-jdbc = { group = "org.xerial", name = "sqlite-jdbc", version = "3.50.2.0" }
12+
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version = "5.13.3" }
13+
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version = "1.13.3" }
14+
kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "7.0.7" }
15+
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.7.1" }
16+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.9.0" }
17+
log4j2-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.25.1" }
18+
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version = "3.50.2.0" }
2019

2120
[bundles]
2221
kotlinx-serialization = ["kotlinx-serialization-json", "kotlinx-datetime"]

gradle/wrapper/gradle-wrapper.jar

122 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)