CryptoScheduler.kt

package com.distasilucas.cryptobalancetracker.scheduler

import com.distasilucas.cryptobalancetracker.entity.Crypto
import com.distasilucas.cryptobalancetracker.exception.TooManyRequestsException
import com.distasilucas.cryptobalancetracker.service.CoingeckoService
import com.distasilucas.cryptobalancetracker.service.CryptoService
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpStatus
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import org.springframework.web.client.RestClientResponseException
import java.time.Clock
import java.time.LocalDateTime

@Component
class CryptoScheduler(
  @Value("\${crypto.scheduler.update.quantity}")
  private val maxLimit: Int,
  private val clock: Clock,
  private val cryptoService: CryptoService,
  private val coingeckoService: CoingeckoService
) {

  private val logger = KotlinLogging.logger { }

  @Scheduled(cron = "\${crypto.scheduler.update.cron}")
  fun updateCryptosInformation() {
    logger.info { "Running cron to update cryptos information..." }

    val cryptosToUpdate = getCryptosToUpdate()
      .map {
        try {
          val coingeckoCryptoInfo = coingeckoService.retrieveCryptoInfo(it.id)
          coingeckoCryptoInfo.toCrypto(clock)
        } catch (exception: RestClientResponseException) {
          if (exception.statusCode == HttpStatus.TOO_MANY_REQUESTS) {
            throw TooManyRequestsException()
          } else {
            logger.warn { "A WebClientResponseException occurred while retrieving info for ${it.id}. Exception: $exception" }
            it
          }
        } catch (exception: Exception) {
          logger.error { "An exception occurred while retrieving info for ${it.id}, therefore crypto info might be outdated. Exception: $exception" }
          it
        }
      }

    if (cryptosToUpdate.isNotEmpty()) {
      logger.info { "About to update ${cryptosToUpdate.size} crypto(s)" }

      cryptoService.updateCryptos(cryptosToUpdate)
    } else {
      logger.info { "No cryptos to update" }
    }
  }

  private fun getCryptosToUpdate(): List<Crypto> {
    return cryptoService.findOldestNCryptosByLastPriceUpdate(
      LocalDateTime.now(clock).minusMinutes(5),
      maxLimit
    )
  }
}