From 27b29703a46bcde319961c2b52d38da3513a1da8 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sun, 28 Jun 2020 17:36:49 +0200 Subject: Make location a part of API --- cmd/Main.hs | 20 ++++++++------- lib/Web/OpenWeatherMap/API.hs | 44 ++++++-------------------------- lib/Web/OpenWeatherMap/Client.hs | 21 ++++----------- lib/Web/OpenWeatherMap/Types/Location.hs | 34 ++++++++++++++++++++++++ openweathermap.cabal | 3 +++ 5 files changed, 61 insertions(+), 61 deletions(-) create mode 100644 lib/Web/OpenWeatherMap/Types/Location.hs diff --git a/cmd/Main.hs b/cmd/Main.hs index 9fb5eb6..d1543d0 100644 --- a/cmd/Main.hs +++ b/cmd/Main.hs @@ -34,6 +34,7 @@ import System.Directory (createDirectoryIfMissing) import System.Environment.XDG.BaseDir (getUserConfigDir, getUserConfigFile) import qualified Web.OpenWeatherMap.Client as Client +import Web.OpenWeatherMap.Types.Location (Location(..)) import Paths_openweathermap (version) -- from cabal import Print (printCurrectWeather, printForecastWeather) @@ -41,16 +42,16 @@ import Print (printCurrectWeather, printForecastWeather) appName :: String appName = "openweathermap" -parseLocation :: Parser Client.Location +parseLocation :: Parser Location parseLocation = byName <|> byCoord where byName = - Client.Name <$> + Name <$> strOption (long "query" <> short 'q' <> metavar "STRING" <> help "City name, e. g. Santiago or Santiago,CU") byCoord = - Client.Coord <$> + Coord <$> option auto (long "lat" <> metavar "NUM" <> help "Latitude in decimal degrees") <*> @@ -87,12 +88,13 @@ parseWeather = flag' Forecast (long "forecast" <> short 'f' <> help "forecast weather") <|> pure Current -data Config = Config - { apikey :: Maybe ApiKey - , location :: Client.Location - , weather :: Weather - , debug :: Bool - } +data Config = + Config + { apikey :: Maybe ApiKey + , location :: Location + , weather :: Weather + , debug :: Bool + } parseConfig :: Parser Config parseConfig = diff --git a/lib/Web/OpenWeatherMap/API.hs b/lib/Web/OpenWeatherMap/API.hs index 516845c..8dde5c7 100644 --- a/lib/Web/OpenWeatherMap/API.hs +++ b/lib/Web/OpenWeatherMap/API.hs @@ -6,10 +6,8 @@ For API key (a.k.a appid) refer to . {-# LANGUAGE TypeOperators #-} module Web.OpenWeatherMap.API - ( weatherByName - , weatherByCoord - , forecastByName - , forecastByCoord + ( currentWeather + , forecastWeather ) where import Data.Proxy (Proxy(..)) @@ -19,44 +17,18 @@ import Servant.Client (ClientM, client) import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather) import Web.OpenWeatherMap.Types.ForecastWeather (ForecastWeather) +import Web.OpenWeatherMap.Types.Location (Location) type QueryParam = QueryParam' '[ Required, Strict] -type GetCurrentWeather = AppId :> Get '[ JSON] CurrentWeather - -type GetForecastWeather = AppId :> Get '[ JSON] ForecastWeather - -type AppId = QueryParam "appid" String - type Current - = "weather" :> (QueryParam "q" String :> GetCurrentWeather :<|> QueryParam "lat" Double :> QueryParam "lon" Double :> GetCurrentWeather) + = "weather" :> QueryParam "appid" String :> Location :> Get '[ JSON] CurrentWeather type Forecast - = "forecast" :> (QueryParam "q" String :> GetForecastWeather :<|> QueryParam "lat" Double :> QueryParam "lon" Double :> GetForecastWeather) + = "forecast" :> QueryParam "appid" String :> Location :> Get '[ JSON] ForecastWeather type API = Current :<|> Forecast --- | Request current weather in the city. -weatherByName :: - String -- ^ City name, e. g. \"Moscow\" or \"Moscow,ru\". - -> String -- ^ API key. - -> ClientM CurrentWeather --- | Request current weather at the geographic coordinates (in decimal degrees). -weatherByCoord :: - Double -- ^ Latitude, e. g. 55.7522200 for Moscow. - -> Double -- ^ Longitude, e. g. 37.6155600 for Moscow. - -> String -- ^ API key. - -> ClientM CurrentWeather --- | Request forecast weather in the city. -forecastByName :: - String -- ^ City name, e. g. \"Moscow\" or \"Moscow,ru\". - -> String -- ^ API key. - -> ClientM ForecastWeather --- | Request current weather at the geographic coordinates (in decimal degrees). -forecastByCoord :: - Double -- ^ Latitude, e. g. 55.7522200 for Moscow. - -> Double -- ^ Longitude, e. g. 37.6155600 for Moscow. - -> String -- ^ API key. - -> ClientM ForecastWeather -(weatherByName :<|> weatherByCoord) :<|> (forecastByName :<|> forecastByCoord) = - client (Proxy :: Proxy API) +forecastWeather :: String -> Location -> ClientM ForecastWeather +currentWeather :: String -> Location -> ClientM CurrentWeather +(currentWeather :<|> forecastWeather) = client (Proxy :: Proxy API) diff --git a/lib/Web/OpenWeatherMap/Client.hs b/lib/Web/OpenWeatherMap/Client.hs index d760812..31719bb 100644 --- a/lib/Web/OpenWeatherMap/Client.hs +++ b/lib/Web/OpenWeatherMap/Client.hs @@ -2,8 +2,7 @@ High-level client functions perfoming requests to OpenWeatherMap API. -} module Web.OpenWeatherMap.Client - ( Location(..) - , getWeather + ( getWeather , getForecast ) where @@ -20,12 +19,7 @@ import Servant.Client import qualified Web.OpenWeatherMap.API as API import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather) import Web.OpenWeatherMap.Types.ForecastWeather (ForecastWeather) - --- | Various way to specify location. -data Location - = Name String -- ^ City name. - | Coord Double - Double -- ^ Geographic coordinates: latitude and longitude. +import Web.OpenWeatherMap.Types.Location (Location) -- | Make a request to OpenWeatherMap API -- and return current weather in given location. @@ -33,10 +27,7 @@ getWeather :: String -- ^ API key. -> Location -> IO (Either ClientError CurrentWeather) -getWeather appid loc = defaultEnv >>= runClientM (api loc appid) - where - api (Name city) = API.weatherByName city - api (Coord lat lon) = API.weatherByCoord lat lon +getWeather appid loc = defaultEnv >>= runClientM (API.currentWeather appid loc) -- | Make a request to OpenWeatherMap API -- and return forecast weather in given location. @@ -44,10 +35,8 @@ getForecast :: String -- ^ API key. -> Location -> IO (Either ClientError ForecastWeather) -getForecast appid loc = defaultEnv >>= runClientM (api loc appid) - where - api (Name city) = API.forecastByName city - api (Coord lat lon) = API.forecastByCoord lat lon +getForecast appid loc = + defaultEnv >>= runClientM (API.forecastWeather appid loc) defaultEnv :: IO ClientEnv defaultEnv = do diff --git a/lib/Web/OpenWeatherMap/Types/Location.hs b/lib/Web/OpenWeatherMap/Types/Location.hs new file mode 100644 index 0000000..72712ed --- /dev/null +++ b/lib/Web/OpenWeatherMap/Types/Location.hs @@ -0,0 +1,34 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + +module Web.OpenWeatherMap.Types.Location + ( Location(..) + ) where + +import Data.Proxy (Proxy(..)) + +import Servant.API ((:>)) +import Servant.Client (Client, HasClient, clientWithRoute, hoistClientMonad) +import Servant.Client.Core.Request (appendToQueryString) +import Web.HttpApiData (toQueryParam) + +-- | Various way to specify location. +data Location + = Name String -- ^ City name. + | Coord Double Double -- ^ Geographic coordinates: latitude and longitude. + +instance HasClient m api => HasClient m (Location :> api) where + type Client m (Location :> api) = Location -> Client m api + clientWithRoute pm Proxy req loc = + clientWithRoute pm (Proxy :: Proxy api) (addParams loc req) + where + addParams (Name q) = appendToQueryString "q" (Just $ toQueryParam q) + addParams (Coord lat lon) = + appendToQueryString "lat" (Just $ toQueryParam lat) . + appendToQueryString "lon" (Just $ toQueryParam lon) + hoistClientMonad pm _ f cl = + \a -> hoistClientMonad pm (Proxy :: Proxy api) f (cl a) diff --git a/openweathermap.cabal b/openweathermap.cabal index dad65c2..91a81f8 100644 --- a/openweathermap.cabal +++ b/openweathermap.cabal @@ -28,9 +28,11 @@ library build-depends: base >= 4.9 && < 5 , aeson + , http-api-data , http-client , servant , servant-client >= 0.16 + , servant-client-core exposed-modules: Web.OpenWeatherMap.API Web.OpenWeatherMap.Client @@ -40,6 +42,7 @@ library Web.OpenWeatherMap.Types.CurrentWeather Web.OpenWeatherMap.Types.Forecast Web.OpenWeatherMap.Types.ForecastWeather + Web.OpenWeatherMap.Types.Location Web.OpenWeatherMap.Types.Main Web.OpenWeatherMap.Types.Sys Web.OpenWeatherMap.Types.Weather -- cgit v1.2.3