-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add query for loading kotlin weekly entries by url.
- Loading branch information
1 parent
3751df7
commit f0c0cfc
Showing
12 changed files
with
1,380 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...in/kotlin/io/github/reactivecircus/kstreamlined/backend/client/KotlinWeeklyIssueClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package io.github.reactivecircus.kstreamlined.backend.client | ||
|
||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.types.KotlinWeeklyIssueEntry | ||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.types.KotlinWeeklyIssueEntryType | ||
import io.ktor.client.HttpClient | ||
import io.ktor.client.engine.HttpClientEngine | ||
import io.ktor.client.plugins.HttpTimeout | ||
import io.ktor.client.request.get | ||
import io.ktor.client.statement.bodyAsText | ||
import it.skrape.core.htmlDocument | ||
import it.skrape.selects.eachText | ||
import it.skrape.selects.html5.a | ||
import it.skrape.selects.html5.div | ||
import it.skrape.selects.html5.span | ||
|
||
interface KotlinWeeklyIssueClient { | ||
suspend fun loadKotlinWeeklyIssue(url: String): List<KotlinWeeklyIssueEntry> | ||
} | ||
|
||
class RealKotlinWeeklyIssueClient( | ||
engine: HttpClientEngine, | ||
) : KotlinWeeklyIssueClient { | ||
|
||
private val httpClient = HttpClient(engine) { | ||
expectSuccess = true | ||
install(HttpTimeout) { | ||
connectTimeoutMillis = HttpTimeoutMillis | ||
requestTimeoutMillis = HttpTimeoutMillis | ||
} | ||
} | ||
|
||
override suspend fun loadKotlinWeeklyIssue(url: String): List<KotlinWeeklyIssueEntry> { | ||
return buildList { | ||
htmlDocument(httpClient.get(url).bodyAsText()) { | ||
div { | ||
withAttribute = "style" to "overflow: hidden;" | ||
findAll { | ||
forEach { section -> | ||
val type = section.div { | ||
findFirst { text } | ||
}.uppercase().let { | ||
KotlinWeeklyIssueEntryType.entries.find { type -> it == type.name } ?: return@forEach | ||
} | ||
|
||
val titleWithLinkPairs = mutableListOf<Pair<String, String>>() | ||
val summaries = mutableListOf<String>() | ||
val sources = mutableListOf<String>() | ||
|
||
section.div { | ||
findSecond { | ||
a { | ||
findAll { | ||
forEach { | ||
it.eachHref.first().let { url -> | ||
if (it.text != url) { | ||
titleWithLinkPairs.add(it.text to url) | ||
} else { | ||
sources.add(url) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
span { | ||
withAttribute = "style" to "font-size:14px" | ||
findAll { | ||
eachText.forEach { | ||
summaries.add(it) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
titleWithLinkPairs.forEachIndexed { index, pair -> | ||
add( | ||
KotlinWeeklyIssueEntry( | ||
type = type, | ||
title = pair.first, | ||
url = pair.second, | ||
summary = summaries[index], | ||
source = sources[index], | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
private const val HttpTimeoutMillis = 10_000L | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
...io/github/reactivecircus/kstreamlined/backend/datafetcher/KotlinWeeklyIssueDataFetcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.github.reactivecircus.kstreamlined.backend.datafetcher | ||
|
||
import com.github.benmanes.caffeine.cache.Cache | ||
import com.github.benmanes.caffeine.cache.Caffeine | ||
import com.netflix.graphql.dgs.DgsComponent | ||
import com.netflix.graphql.dgs.DgsQuery | ||
import com.netflix.graphql.dgs.InputArgument | ||
import io.github.reactivecircus.kstreamlined.backend.client.KotlinWeeklyIssueClient | ||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.DgsConstants | ||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.types.KotlinWeeklyIssueEntry | ||
import java.time.Duration | ||
|
||
@DgsComponent | ||
class KotlinWeeklyIssueDataFetcher( | ||
private val client: KotlinWeeklyIssueClient | ||
) { | ||
private val cache: Cache<String, List<KotlinWeeklyIssueEntry>> = Caffeine | ||
.newBuilder() | ||
.expireAfterAccess(Duration.ofHours(1)) | ||
.build() | ||
|
||
@DgsQuery(field = DgsConstants.QUERY.KotlinWeeklyIssue) | ||
suspend fun kotlinWeeklyIssue(@InputArgument url: String): List<KotlinWeeklyIssueEntry> { | ||
return cache.getIfPresent(url) ?: client.loadKotlinWeeklyIssue(url).also { | ||
cache.put(url, it) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
...otlin/io/github/reactivecircus/kstreamlined/backend/client/FakeKotlinWeeklyIssueClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package io.github.reactivecircus.kstreamlined.backend.client | ||
|
||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.types.KotlinWeeklyIssueEntry | ||
import io.github.reactivecircus.kstreamlined.backend.schema.generated.types.KotlinWeeklyIssueEntryType | ||
|
||
object FakeKotlinWeeklyIssueClient : KotlinWeeklyIssueClient { | ||
|
||
var nextKotlinWeeklyIssueResponse: () -> List<KotlinWeeklyIssueEntry> = { | ||
DummyKotlinWeeklyIssueEntries | ||
} | ||
|
||
override suspend fun loadKotlinWeeklyIssue(url: String): List<KotlinWeeklyIssueEntry> { | ||
return nextKotlinWeeklyIssueResponse() | ||
} | ||
} | ||
|
||
val DummyKotlinWeeklyIssueEntries = listOf( | ||
KotlinWeeklyIssueEntry( | ||
title = "Amper Update – December 2023", | ||
summary = "Last month JetBrains introduced Amper, a tool to improve the project configuration user experience. Marton Braun gives us an update about its state in December 2023.", | ||
url = "https://blog.jetbrains.com/amper/2023/12/amper-update-december-2023/", | ||
source = "blog.jetbrains.com", | ||
type = KotlinWeeklyIssueEntryType.ANNOUNCEMENTS, | ||
), | ||
KotlinWeeklyIssueEntry( | ||
title = "How to Use the Cucumber Framework to Test Application Use Cases", | ||
summary = "Matthias Schenk writes today about Cucumber, a framework that can be used in application development to verify the correct behavior of the application.", | ||
url = "https://towardsdev.com/how-to-use-the-cucumber-framework-to-test-application-use-cases-48b4f21ee0d0", | ||
source = "towardsdev.com", | ||
type = KotlinWeeklyIssueEntryType.ARTICLES, | ||
), | ||
KotlinWeeklyIssueEntry( | ||
title = "Using launcher and themed icons in Android Studio, the manual way", | ||
summary = "Marlon Lòpez describes how we can use the launcher and the themed icons in Android Studio.", | ||
url = "https://dev.to/marlonlom/using-launcher-and-themed-icons-in-android-studio-the-manual-way-1h2a", | ||
source = "dev.to", | ||
type = KotlinWeeklyIssueEntryType.ANDROID, | ||
), | ||
KotlinWeeklyIssueEntry( | ||
title = "Setting Sail with Compose Multiplatform by Isuru Rajapakse", | ||
summary = "Isuru Rajapakse talks at the DevFest Sri Lanka about Compose Multiplatform.", | ||
url = "https://www.youtube.com/watch?v=sG60644C47I", | ||
source = "www.youtube.com", | ||
type = KotlinWeeklyIssueEntryType.VIDEOS, | ||
), | ||
KotlinWeeklyIssueEntry( | ||
title = "Kim - Kotlin Image Metadata", | ||
summary = "Kim is a Kotlin image metadata manipulation library for Kotlin Multiplatform.", | ||
url = "https://github.com/Ashampoo/kim", | ||
source = "github.com", | ||
type = KotlinWeeklyIssueEntryType.LIBRARIES, | ||
), | ||
) |
Oops, something went wrong.