Skip to content

Commit cbac0f7

Browse files
authored
chore(abg): use ktor client for fetching static files from GitHub (#2163)
Part of #2160. The biggest benefit is being able to distinguish between a failed call and non-existing resource. Other benefits exist, like being able to trace stuff with OpenTelemetry.
1 parent 007744d commit cbac0f7

File tree

11 files changed

+645
-378
lines changed

11 files changed

+645
-378
lines changed

action-binding-generator/api/action-binding-generator.api

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen
102102
}
103103

104104
public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt {
105-
public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;)Ljava/util/List;
106-
public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;ILjava/lang/Object;)Ljava/util/List;
105+
public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
106+
public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
107107
}
108108

109109
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input {
@@ -176,8 +176,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/met
176176
}
177177

178178
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReadingKt {
179-
public static final fun fetchMetadata (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lkotlin/jvm/functions/Function1;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;
180-
public static synthetic fun fetchMetadata$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;
179+
public static final fun fetchMetadata (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/ktor/client/HttpClient;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
180+
public static synthetic fun fetchMetadata$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/ktor/client/HttpClient;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
181181
}
182182

183183
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Output {

action-binding-generator/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ version = rootProject.version
1616
dependencies {
1717
implementation("com.squareup:kotlinpoet:2.2.0")
1818
implementation("com.charleskorn.kaml:kaml:0.104.0")
19+
implementation("io.ktor:ktor-client-core:3.3.3")
20+
implementation("io.ktor:ktor-client-cio:3.3.3")
1921
implementation("io.github.oshai:kotlin-logging:7.0.13")
2022
implementation(projects.sharedInternal)
2123

24+
testImplementation("io.ktor:ktor-client-mock:3.3.3")
2225
testImplementation(projects.githubWorkflowsKt)
2326
}
2427

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private object Properties {
6565
val CUSTOM_VERSION = "_customVersion"
6666
}
6767

68-
public fun ActionCoords.generateBinding(
68+
public suspend fun ActionCoords.generateBinding(
6969
metadataRevision: MetadataRevision,
7070
metadata: Metadata? = null,
7171
inputTypings: ActionTypings? = null,

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.CommitHa
77
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.MetadataRevision
88
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion
99
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName
10+
import io.ktor.client.HttpClient
11+
import io.ktor.client.engine.cio.CIO
12+
import io.ktor.client.request.get
13+
import io.ktor.client.statement.bodyAsText
14+
import io.ktor.http.HttpStatusCode
15+
import kotlinx.io.IOException
1016
import kotlinx.serialization.Serializable
1117
import kotlinx.serialization.decodeFromString
12-
import java.io.IOException
13-
import java.net.URI
1418

1519
private val logger = logger { }
1620

@@ -45,9 +49,11 @@ private fun ActionCoords.actionYmlUrl(gitRef: String) =
4549
private fun ActionCoords.actionYamlUrl(gitRef: String) =
4650
"https://raw.githubusercontent.com/$owner/$name/$gitRef$subName/action.yaml"
4751

48-
public fun ActionCoords.fetchMetadata(
52+
private val defaultHttpClient = HttpClient(CIO)
53+
54+
public suspend fun ActionCoords.fetchMetadata(
4955
metadataRevision: MetadataRevision,
50-
fetchUri: (URI) -> String = ::fetchUri,
56+
httpClient: HttpClient = defaultHttpClient,
5157
): Metadata? {
5258
val gitRef =
5359
when (metadataRevision) {
@@ -58,17 +64,16 @@ public fun ActionCoords.fetchMetadata(
5864

5965
return list
6066
.firstNotNullOfOrNull { url ->
61-
try {
62-
logger.info { " ... from $url" }
63-
fetchUri(URI(url))
64-
} catch (e: IOException) {
65-
null
67+
logger.info { " ... from $url" }
68+
val response = httpClient.get(url)
69+
when (response.status) {
70+
HttpStatusCode.OK -> response.bodyAsText()
71+
HttpStatusCode.NotFound -> null
72+
else -> throw IOException("Failed fetching from $url")
6673
}
6774
}?.let { yaml.decodeFromString(it) }
6875
}
6976

70-
internal fun fetchUri(uri: URI): String = uri.toURL().readText()
71-
7277
private val yaml =
7378
Yaml(
7479
configuration =

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,26 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingAc
1313
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.ACTION
1414
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.TYPING_CATALOG
1515
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName
16-
import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.fetchUri
1716
import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toPascalCase
17+
import io.ktor.client.HttpClient
18+
import io.ktor.client.engine.cio.CIO
19+
import io.ktor.client.request.get
20+
import io.ktor.client.statement.bodyAsText
21+
import io.ktor.http.HttpStatusCode
22+
import kotlinx.io.IOException
1823
import kotlinx.serialization.Serializable
1924
import kotlinx.serialization.decodeFromString
20-
import java.io.FileNotFoundException
21-
import java.net.URI
2225

2326
private val logger = logger { }
27+
private val defaultHttpClient = HttpClient(CIO)
2428

25-
internal fun ActionCoords.provideTypes(
29+
internal suspend fun ActionCoords.provideTypes(
2630
metadataRevision: MetadataRevision,
27-
fetchUri: (URI) -> String = ::fetchUri,
31+
httpClient: HttpClient = defaultHttpClient,
2832
): ActionTypings =
2933
(
30-
this.fetchTypingMetadata(metadataRevision, fetchUri)
31-
?: this.toMajorVersion().fetchFromTypingsFromCatalog(fetchUri)
34+
this.fetchTypingMetadata(metadataRevision, httpClient)
35+
?: this.toMajorVersion().fetchFromTypingsFromCatalog(httpClient)
3236
)?.let { (typings, typingActualSource) ->
3337
ActionTypings(
3438
inputTypings = typings.toTypesMap(),
@@ -51,9 +55,9 @@ private fun ActionCoords.actionTypesFromCatalog() =
5155

5256
private fun ActionCoords.catalogMetadata() = "$CATALOG_BASE_URL/${owner.lowercase()}/${name.lowercase()}/metadata.yml"
5357

54-
private fun ActionCoords.fetchTypingMetadata(
58+
private suspend fun ActionCoords.fetchTypingMetadata(
5559
metadataRevision: MetadataRevision,
56-
fetchUri: (URI) -> String = ::fetchUri,
60+
httpClient: HttpClient,
5761
): Pair<ActionTypes, TypingActualSource>? {
5862
val gitRef =
5963
when (metadataRevision) {
@@ -63,34 +67,38 @@ private fun ActionCoords.fetchTypingMetadata(
6367
val list = listOf(actionTypesYmlUrl(gitRef), actionTypesYamlUrl(gitRef))
6468
val typesMetadataYaml =
6569
list.firstNotNullOfOrNull { url ->
66-
try {
67-
logger.info { " ... types from action $url" }
68-
fetchUri(URI(url))
69-
} catch (e: FileNotFoundException) {
70-
logger.info { " ... types from action were not found: $url" }
71-
null
70+
logger.info { " ... types from action $url" }
71+
val response = httpClient.get(url)
72+
when (response.status) {
73+
HttpStatusCode.OK -> response.bodyAsText()
74+
HttpStatusCode.NotFound -> {
75+
logger.info { " ... types from action were not found: $url" }
76+
null
77+
}
78+
else -> throw IOException("Failed fetching from $url")
7279
}
7380
} ?: return null
7481

7582
return Pair(yaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYaml, ActionTypes()), ACTION)
7683
}
7784

78-
private fun ActionCoords.fetchFromTypingsFromCatalog(
79-
fetchUri: (URI) -> String = ::fetchUri,
85+
private suspend fun ActionCoords.fetchFromTypingsFromCatalog(
86+
httpClient: HttpClient,
8087
): Pair<ActionTypes, TypingActualSource>? =
8188
(
82-
fetchTypingsFromUrl(url = this.actionTypesFromCatalog(), fetchUri = fetchUri)
83-
?: this.fetchTypingsForOlderVersionFromCatalog(fetchUri = fetchUri)
89+
fetchTypingsFromUrl(url = this.actionTypesFromCatalog(), httpClient = httpClient)
90+
?: this.fetchTypingsForOlderVersionFromCatalog(httpClient = httpClient)
8491
)?.let { Pair(it, TYPING_CATALOG) }
8592

86-
private fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(fetchUri: (URI) -> String): ActionTypes? {
93+
private suspend fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(httpClient: HttpClient): ActionTypes? {
8794
val metadataUrl = this.catalogMetadata()
95+
logger.info { " ... metadata from $metadataUrl" }
96+
val response = httpClient.get(metadataUrl)
8897
val metadataYml =
89-
try {
90-
logger.info { " ... metadata from $metadataUrl" }
91-
fetchUri(URI(metadataUrl))
92-
} catch (e: FileNotFoundException) {
93-
return null
98+
when (response.status) {
99+
HttpStatusCode.OK -> response.bodyAsText()
100+
HttpStatusCode.NotFound -> return null
101+
else -> throw IOException("Failed fetching from $metadataUrl")
94102
}
95103
val metadata = yaml.decodeFromString<CatalogMetadata>(metadataYml)
96104
val requestedVersionAsInt = this.version.versionToIntOrNull() ?: return null
@@ -106,22 +114,25 @@ private fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(fetchUri: (URI)
106114
val adjustedCoords = this.copy(version = fallbackVersion)
107115
return fetchTypingsFromUrl(
108116
url = adjustedCoords.actionTypesFromCatalog(),
109-
fetchUri = fetchUri,
117+
httpClient = httpClient,
110118
)?.copy(fromFallbackVersion = true)
111119
}
112120

113-
private fun fetchTypingsFromUrl(
121+
private suspend fun fetchTypingsFromUrl(
114122
url: String,
115-
fetchUri: (URI) -> String,
123+
httpClient: HttpClient,
116124
): ActionTypes? {
125+
logger.info { " ... types from catalog $url" }
126+
val response = httpClient.get(url)
117127
val typesMetadataYml =
118-
try {
119-
logger.info { " ... types from catalog $url" }
120-
fetchUri(URI(url))
121-
} catch (e: FileNotFoundException) {
122-
logger.info { " ... types from catalog were not found: $url" }
123-
null
124-
} ?: return null
128+
when (response.status) {
129+
HttpStatusCode.OK -> response.bodyAsText()
130+
HttpStatusCode.NotFound -> {
131+
logger.info { " ... types from catalog were not found: $url" }
132+
return null
133+
}
134+
else -> throw IOException("Failed fetching from $url")
135+
}
125136
return yaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYml, ActionTypes())
126137
}
127138

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.github.typesafegithub.workflows.actionbindinggenerator
2+
3+
import io.ktor.client.HttpClient
4+
import io.ktor.client.engine.mock.MockEngine
5+
import io.ktor.client.engine.mock.respond
6+
7+
fun mockClientReturning(response: String) = HttpClient(MockEngine { respond(content = response) })

0 commit comments

Comments
 (0)