2023-04-23 22:41:38 +03:00
|
|
|
package fetch
|
|
|
|
|
|
|
|
|
|
import cats.effect._
|
|
|
|
|
import cats.implicits._
|
|
|
|
|
import org.http4s._
|
|
|
|
|
import org.http4s.client.Client
|
|
|
|
|
import org.http4s.headers.Authorization
|
2023-05-17 01:20:01 +03:00
|
|
|
import org.http4s.ember.client.EmberClientBuilder
|
2023-05-21 22:37:33 +03:00
|
|
|
import org.typelevel.log4cats.Logger
|
|
|
|
|
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
2023-05-16 14:54:02 +03:00
|
|
|
import pureconfig._
|
|
|
|
|
import pureconfig.generic.auto._
|
2023-04-23 22:41:38 +03:00
|
|
|
|
|
|
|
|
import java.time.{LocalDate, LocalDateTime}
|
|
|
|
|
import scala.concurrent.ExecutionContext.global
|
|
|
|
|
|
2023-05-16 14:54:02 +03:00
|
|
|
final case class WeatherServerConfig(
|
|
|
|
|
username: String,
|
|
|
|
|
password: String,
|
|
|
|
|
url: String,
|
|
|
|
|
)
|
|
|
|
|
|
2023-04-23 22:41:38 +03:00
|
|
|
object FetchService {
|
2023-05-21 22:37:33 +03:00
|
|
|
def of: IO[FetchService] = {
|
2023-05-23 22:02:56 +03:00
|
|
|
Slf4jLogger.create[IO].map(logger => new FetchService(new FileNameService, logger))
|
2023-05-21 22:37:33 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-23 22:02:56 +03:00
|
|
|
class FetchService(fileNameService: FileNameService, log: Logger[IO]) {
|
2023-05-16 14:54:02 +03:00
|
|
|
private val weatherServerConfig: WeatherServerConfig = ConfigSource.default.load[WeatherServerConfig] match {
|
|
|
|
|
case Right(config) => config
|
|
|
|
|
case Left(errors) => throw new RuntimeException(s"Unable to load config: $errors")
|
|
|
|
|
}
|
2023-05-01 16:43:21 +03:00
|
|
|
|
2023-05-16 14:54:02 +03:00
|
|
|
private val basicCredentials: BasicCredentials =
|
|
|
|
|
BasicCredentials(weatherServerConfig.username, weatherServerConfig.password)
|
2023-05-01 16:43:21 +03:00
|
|
|
|
2023-05-16 14:54:02 +03:00
|
|
|
private val baseUrl: IO[Uri] = IO(Uri.unsafeFromString(weatherServerConfig.url))
|
2023-04-23 22:41:38 +03:00
|
|
|
|
|
|
|
|
private def makeRequest(client: Client[IO], url: Uri): IO[Either[Throwable, (String, String)]] = {
|
|
|
|
|
val fileName = url.path.toString()
|
2023-05-16 14:54:02 +03:00
|
|
|
val request = Request[IO](Method.GET, url).withHeaders(Authorization(basicCredentials))
|
2023-05-01 16:43:21 +03:00
|
|
|
|
2023-05-16 14:54:02 +03:00
|
|
|
client.expect[String](request).redeemWith(
|
2023-05-24 16:01:48 +03:00
|
|
|
error => IO(Left(error)) // <* log.error(s"Request failed to url: $url with error: ${error.getMessage}")
|
2023-04-23 22:41:38 +03:00
|
|
|
,
|
2023-05-24 16:01:48 +03:00
|
|
|
fileContent => IO(Right((fileName, fileContent))) // <* log.info(s"Fetched: $fileName")
|
2023-04-23 22:41:38 +03:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def fetchFiles(fileNames: List[String]): IO[List[Either[Throwable, (String, String)]]] = {
|
2023-05-16 14:54:02 +03:00
|
|
|
val IOUrls = baseUrl.map(baseUrl => fileNames.map(baseUrl / _))
|
2023-05-17 01:20:01 +03:00
|
|
|
EmberClientBuilder.default[IO].build.use { client =>
|
2023-05-01 16:43:21 +03:00
|
|
|
IOUrls.flatMap(_.traverse(url => makeRequest(client, url)))
|
2023-04-23 22:41:38 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-20 23:05:30 +03:00
|
|
|
def fetchSingleFile(fileName: String): IO[Either[Throwable, (String, String)]] = {
|
|
|
|
|
fetchFiles(List(fileName)).map { results =>
|
2023-05-14 15:48:52 +03:00
|
|
|
results.headOption match {
|
2023-05-20 23:05:30 +03:00
|
|
|
case Some(Right(result)) =>
|
2023-05-21 22:37:33 +03:00
|
|
|
log.info(s"fetched: $fileName").as(Right(result))
|
2023-05-20 23:05:30 +03:00
|
|
|
case Some(Left(err)) =>
|
2023-05-21 22:37:33 +03:00
|
|
|
log.error(s"failed fetch: $fileName with error: ${err.getMessage}").as(Left(err))
|
2023-05-20 23:05:30 +03:00
|
|
|
case None =>
|
2023-05-21 22:37:33 +03:00
|
|
|
log.error(s"failed fetch: $fileName").as(Left(new Exception("No file fetched")))
|
2023-05-14 15:48:52 +03:00
|
|
|
}
|
2023-05-20 23:05:30 +03:00
|
|
|
}.flatten
|
2023-05-14 15:48:52 +03:00
|
|
|
}
|
|
|
|
|
|
2023-04-23 22:41:38 +03:00
|
|
|
def fetchInRange(from: LocalDateTime, to: LocalDateTime): IO[List[Either[Throwable, (String, String)]]] = {
|
2023-05-23 22:02:56 +03:00
|
|
|
val fileNames = fileNameService.generate(from, to)
|
2023-04-23 22:41:38 +03:00
|
|
|
fetchFiles(fileNames)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def fetchFromDate(date: LocalDate): IO[List[Either[Throwable, (String, String)]]] = {
|
2023-05-23 22:02:56 +03:00
|
|
|
val fileNames = fileNameService.generateFromDate(date)
|
2023-04-23 22:41:38 +03:00
|
|
|
fetchFiles(fileNames)
|
|
|
|
|
}
|
|
|
|
|
}
|