JsonPlaceholderService.kt

package com.example.templateproject.client.jsonplaceholder

import com.example.templateproject.client.GenericHttpClient
import com.example.templateproject.client.jsonplaceholder.api.Post
import com.example.templateproject.client.jsonplaceholder.api.User
import com.example.templateproject.client.jsonplaceholder.configuration.JsonPlaceholderCacheConfiguration
import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.Cacheable
import org.springframework.core.retry.RetryTemplate
import org.springframework.core.retry.Retryable
import org.springframework.http.ResponseEntity
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.util.concurrent.CompletableFuture

@Service
class JsonPlaceholderService(
    @param:Value($$"${client.jsonplaceholder.client-id}") val clientId: String,
    @param:Value($$"${client.jsonplaceholder.cache.enabled}") val cacheEnabled: Boolean,
    private val jsonPlaceholderClient: JsonPlaceholderClient,
    private val httpClient: GenericHttpClient,
    private val retryTemplateForHttpServerError: RetryTemplate,
) {
    @Cacheable(
        JsonPlaceholderCacheConfiguration.USERS_CACHE_NAME,
        condition = "#root.target.cacheEnabled",
    )
    @Async("jsonPlaceHolderExecutor")
    fun getUsers(): CompletableFuture<List<User>> {
        val request = USERS_ENDPOINT
        return execute(request, listOf()) { jsonPlaceholderClient.getUsers() }
    }

    @Async("jsonPlaceHolderExecutor")
    fun getPostsByUserId(userId: Long): CompletableFuture<List<Post>> {
        val request = "$POSTS_ENDPOINT?userId=$userId"
        return execute(request, listOf()) { jsonPlaceholderClient.getAllPostByUserId(userId) }
    }

    private fun <T : Any> execute(
        request: Any,
        defaultResponse: T,
        httpCall: () -> ResponseEntity<T>,
    ): CompletableFuture<T> =
        CompletableFuture.completedFuture(
            retryTemplateForHttpServerError.execute(
                retryable {
                    httpClient.perform(clientId, request, defaultResponse, httpCall)
                },
            ),
        )

    internal fun <T> retryable(method: () -> T) =
        object : Retryable<T> {
            override fun execute(): T = method()

            override fun getName() = "JsonPlaceholderServiceRetryable"
        }
}