diff --git a/build.sbt b/build.sbt index c6380c8..adf9bcf 100644 --- a/build.sbt +++ b/build.sbt @@ -35,9 +35,9 @@ lazy val root = project Dependencies.Libraries.jodaTime, Dependencies.Libraries.jodaConvert, Dependencies.Libraries.collUtil, + Dependencies.Libraries.circeGeneric, + Dependencies.Libraries.circeParser, Dependencies.Libraries.hammockCore, - Dependencies.Libraries.hammockCirce, - Dependencies.Libraries.circeLiteral, Dependencies.Libraries.specs2, Dependencies.Libraries.specs2Mock, Dependencies.Libraries.specsScalaCheck diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e33438f..60a85da 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -25,9 +25,9 @@ object Dependencies { val jodaTime = "2.10" val jodaConvert = "2.1" // Scala + val circe = "0.9.3" val collUtil = "6.39.0" val hammock = "0.8.5" - val circe = "0.9.3" // Tests val specs2 = "3.9.4" } @@ -37,11 +37,11 @@ object Dependencies { val jodaTime = "joda-time" % "joda-time" % V.jodaTime val jodaConvert = "org.joda" % "joda-convert" % V.jodaConvert // Scala + val circeGeneric = "io.circe" %% "circe-generic" % V.circe + val circeParser = "io.circe" %% "circe-parser" % V.circe val collUtil = "com.twitter" %% "util-collection" % V.collUtil val hammockCore = "com.pepegar" %% "hammock-core" % V.hammock - val hammockCirce = "com.pepegar" %% "hammock-circe" % V.hammock // Tests - val circeLiteral = "io.circe" %% "circe-literal" % V.circe % "test" val specs2 = "org.specs2" %% "specs2-core" % V.specs2 % "test" val specs2Mock = "org.specs2" %% "specs2-mock" % V.specs2 % "test" val specsScalaCheck = "org.specs2" %% "specs2-scalacheck" % V.specs2 % "test" diff --git a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Client.scala b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Client.scala index 22b4e58..a2168f8 100644 --- a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Client.scala +++ b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Client.scala @@ -29,17 +29,16 @@ import Responses._ import Requests._ /** - * Base client trait with defined client methods such as `historyById`, `historyByName` + * Base client trait with defines client methods such as `historyById`, `forecastByName` * common for subclasses * - * @tparam F response wrapper for `Client` subclass, such as cats `IO` - * all `receive` logic should be wrapped in it + * @tparam F effect type */ trait Client[F[_]] { /** * Main client logic for Request => Response function, - * where Response is wrappeed in tparam `F` + * where Response is wrapped in tparam `F` * * @param owmRequest request built by client method * @tparam W type of weather response to extract diff --git a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmAsyncClient.scala b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmAsyncClient.scala index 861b601..9a963d3 100644 --- a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmAsyncClient.scala +++ b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmAsyncClient.scala @@ -42,20 +42,16 @@ case class OwmAsyncClient[F[_]: Sync](appId: String, apiHost: String = "api.open def receive[W <: OwmResponse: Decoder](request: OwmRequest): F[Either[WeatherError, W]] = { - val uri = request.constructQuery(appId) - val scheme = if (ssl) "https://" else "http://" - val url = scheme + apiHost + uri + val scheme = if (ssl) "https" else "http" + val authority = Uri.Authority(None, Uri.Host.Other(apiHost), None) + val baseUri = Uri(Some(scheme), Some(authority)) - Uri - .fromString(url) - .leftMap(InternalError) - .traverse { uri => - Hammock - .request(Method.GET, uri, Map()) - .map(uri => processResponse(uri)) - .exec[F] - } - .map(x => x.joinRight) + val uri = request.constructQuery(baseUri, appId) + + Hammock + .request(Method.GET, uri, Map()) + .map(uri => processResponse(uri)) + .exec[F] } /** diff --git a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmCacheClient.scala b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmCacheClient.scala index 738a662..b12e96a 100644 --- a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmCacheClient.scala +++ b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/OwmCacheClient.scala @@ -36,7 +36,7 @@ import WeatherCache.{CacheKey, Position} /** * Blocking OpenWeatherMap client with history (only) cache - * Uses AsyncOwmClient under the hood, have same method set, but also uses timeouts + * Uses AsyncOwmClient under the hood, has the same method set, but also uses timeouts * * WARNING. This client uses pro.openweathermap.org for data access, * It will not work with free OWM licenses. diff --git a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Requests.scala b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Requests.scala index b0fda77..e3a0d8b 100644 --- a/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Requests.scala +++ b/src/main/scala/com.snowplowanalytics/weather/providers/openweather/Requests.scala @@ -12,10 +12,13 @@ */ package com.snowplowanalytics.weather.providers.openweather +import cats.data.NonEmptyList +import hammock.Uri + private[weather] object Requests { sealed trait WeatherRequest { - def constructQuery(appId: String): String + def constructQuery(baseUri: Uri, apiKey: String): Uri } sealed trait OwmRequest extends WeatherRequest { @@ -23,18 +26,14 @@ private[weather] object Requests { val resource: String val parameters: Map[String, String] - /** - * Construct URI for specific type of request and all other data - * - * @param appId API key - * @return URI string ready to be sent - */ - def constructQuery(appId: String): String = { - val end = endpoint.map(e => s"$e/$resource").getOrElse(s"$resource") - val params = parameters ++ Map("appid" -> appId) - val queryString = params.map { case (a, b) => s"$a=$b" }.mkString("&") - s"/data/2.5/$end?$queryString" + def constructQuery(baseUri: Uri, apiKey: String): Uri = { + val versionedBaseUri = baseUri / "data" / "2.5" + val uriWithPath = endpoint.map(e => versionedBaseUri / e / resource).getOrElse(versionedBaseUri / resource) + val params = NonEmptyList.of("appid" -> apiKey) ++ parameters.toList + + uriWithPath ? params } + } final case class OwmHistoryRequest(resource: String, parameters: Map[String, String]) extends OwmRequest {