Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.leanix.githubagent.client

import feign.Response
import io.github.resilience4j.retry.annotation.Retry
import net.leanix.githubagent.config.FeignClientConfig
import net.leanix.githubagent.dto.ArtifactsListResponse
import net.leanix.githubagent.dto.GitHubAppResponse
Expand Down Expand Up @@ -30,45 +31,52 @@ interface GitHubClient {
@RequestHeader("Accept") accept: String = "application/vnd.github.v3+json"
): GitHubAppResponse

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/app/installations")
fun getInstallations(
@RequestHeader("Authorization") jwt: String,
@RequestParam("per_page", defaultValue = "30") perPage: Int,
@RequestParam("page", defaultValue = "1") page: Int
): List<Installation>

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/app/installations/{installationId}")
fun getInstallation(
@PathVariable("installationId") installationId: Long,
@RequestHeader("Authorization") jwt: String
): Installation

@Retry(name = "secondary_rate_limit")
@PostMapping("/api/v3/app/installations/{installationId}/access_tokens")
fun createInstallationToken(
@PathVariable("installationId") installationId: Long,
@RequestHeader("Authorization") jwt: String,
@RequestBody emptyBody: String = ""
): InstallationTokenResponse

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/organizations")
fun getOrganizations(
@RequestHeader("Authorization") jwt: String,
@RequestParam("per_page", defaultValue = "30") perPage: Int,
@RequestParam("since", defaultValue = "1") since: Int
): List<Organization>

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/orgs/{org}/repos")
fun getRepositories(
@PathVariable("org") org: String,
@RequestHeader("Authorization") token: String
): List<Repository>

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/search/code")
fun searchManifestFiles(
@RequestHeader("Authorization") token: String,
@RequestParam("q") query: String,
): GitHubSearchResponse

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/repos/{owner}/{repo}/actions/runs/{runId}/artifacts")
fun getRunArtifacts(
@PathVariable("owner") owner: String,
Expand All @@ -77,6 +85,7 @@ interface GitHubClient {
@RequestHeader("Authorization") token: String
): ArtifactsListResponse

@Retry(name = "secondary_rate_limit")
@GetMapping("/api/v3/repos/{owner}/{repo}/actions/artifacts/{artifactId}/zip")
fun downloadArtifact(
@PathVariable("owner") owner: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package net.leanix.githubagent.config

import io.github.resilience4j.core.IntervalFunction
import io.github.resilience4j.retry.RetryConfig
import io.github.resilience4j.retry.RetryRegistry
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration
import java.time.temporal.ChronoUnit
import java.util.*

@Configuration
class Resilience4jConfig {

private val logger = LoggerFactory.getLogger(Resilience4jConfig::class.java)

@Bean
fun retryRegistry(): RetryRegistry {
val intervalWithExponentialBackoff = IntervalFunction
.ofExponentialBackoff(Duration.ofSeconds(20), 2.0)
val retryConfig = RetryConfig.custom<Any>()
.maxAttempts(5)
.intervalFunction(intervalWithExponentialBackoff)
.retryOnException { throwable ->
logger.debug("Retrying due to exception: ${throwable.message}")
true
}
.retryOnResult { false }
.build()

val registry = RetryRegistry.of(retryConfig)

registry.retry("secondary_rate_limit").eventPublisher
.onRetry { event ->
val readableWaitTime = String.format(
Locale.getDefault(),
"%d minutes, %d seconds",
event.waitInterval.toMinutes(),
event.waitInterval.minus(event.waitInterval.toMinutes(), ChronoUnit.MINUTES).seconds
)
logger.info(
"Retrying call due to ${event.name}, attempt: ${event.numberOfRetryAttempts}, " +
"wait time: $readableWaitTime"
)
}
.onError { event ->
logger.error(
"Call failed due to ${event.name}, after attempts: ${event.numberOfRetryAttempts}, " +
"last exception: ${event.lastThrowable.message}"
)
}

return registry
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient
import reactor.util.retry.Retry
import java.time.Duration
import io.github.resilience4j.retry.annotation.Retry as ResilienceRetry

@Component
class GitHubGraphQLService(
Expand All @@ -24,6 +25,7 @@ class GitHubGraphQLService(
private const val PAGE_COUNT = 20
}

@ResilienceRetry(name = "secondary_rate_limit")
fun getRepositories(
token: String,
cursor: String? = null
Expand Down Expand Up @@ -67,6 +69,7 @@ class GitHubGraphQLService(
}
}

@ResilienceRetry(name = "secondary_rate_limit")
fun getManifestFileContent(
owner: String,
repositoryName: String,
Expand Down Expand Up @@ -95,6 +98,7 @@ class GitHubGraphQLService(
return (result.data?.repository?.manifestFile as? RepositoryManifestBlob)?.text
}

@ResilienceRetry(name = "secondary_rate_limit")
fun getRepository(
owner: String,
repositoryName: String,
Expand Down
Loading