module Ziptastic.Client
( ApiKey(..)
, LocaleInfo(..)
, Core.LocaleCoords(..)
, forwardGeocode
, reverseGeocode
, reverseGeocodeWithRadius
) where
import Prelude ()
import Prelude.Compat
import Data.ISO3166_CountryCodes (CountryCode)
import Data.Proxy (Proxy(..))
import Data.Text (Text)
import Servant.API ((:<|>)(..))
import Servant.Client
( BaseUrl(..), ClientEnv(..), ClientM, Scheme(..), ServantError(..)
, client, runClientM
)
import Ziptastic.Core (ApiKey, ForApi(..), LocaleInfo)
import qualified Ziptastic.Core as Core
import Network.HTTP.Client
( Manager, HttpException(HttpExceptionRequest), HttpExceptionContent(TlsNotSupported) )
import Control.Exception (fromException)
forwardGeocode :: ApiKey
-> Manager
-> CountryCode
-> Text
-> IO (Either ServantError [LocaleInfo])
forwardGeocode apiKey manager countryCode postalCode =
tryTlsOrDegrade $ \scheme -> runClientM func (ClientEnv manager $ baseUrl scheme)
where func = forwardGeocode' (apiClient apiKey) (ForApi countryCode) postalCode
reverseGeocode :: ApiKey
-> Manager
-> Double
-> Double
-> IO (Either ServantError [LocaleInfo])
reverseGeocode apiKey manager lat long = reverseGeocodeWithRadius apiKey manager lat long 5000
reverseGeocodeWithRadius :: ApiKey
-> Manager
-> Double
-> Double
-> Int
-> IO (Either ServantError [LocaleInfo])
reverseGeocodeWithRadius apiKey manager lat long radius =
tryTlsOrDegrade $ \scheme -> runClientM func (ClientEnv manager $ baseUrl scheme)
where func = reverseGeocodeWithRadius' (mkReverseGeocode (apiClient apiKey) lat long) radius
data ApiClient = ApiClient
{ forwardGeocode' :: ForApi CountryCode -> Text -> ClientM [LocaleInfo]
, mkReverseGeocode :: Double -> Double -> ReverseGeocodeApiClient
}
data ReverseGeocodeApiClient = ReverseGeocodeApiClient
{ reverseGeocodeWithRadius' :: Int -> ClientM [LocaleInfo]
, reverseGeocode' :: ClientM [LocaleInfo]
}
apiClient :: ApiKey -> ApiClient
apiClient apiKey = ApiClient
{ forwardGeocode' = forwardGeocodeApi
, mkReverseGeocode = mkReversGeocodeEndpoints
}
where
forwardGeocodeApi :<|> reverseGeocodeApi = client (Proxy :: Proxy Core.Api) (Just apiKey)
mkReversGeocodeEndpoints lat long = ReverseGeocodeApiClient
{ reverseGeocodeWithRadius' = withRadius
, reverseGeocode' = withDefaultRadius
}
where
withRadius :<|> withDefaultRadius = reverseGeocodeApi lat long
baseUrl :: Scheme -> BaseUrl
baseUrl scheme = BaseUrl
{ baseUrlScheme = scheme
, baseUrlHost = Core.baseUrlHost
, baseUrlPort = case scheme of
Http -> 80
Https -> 443
, baseUrlPath = Core.baseUrlPath
}
tryTlsOrDegrade :: (Scheme -> IO (Either ServantError a)) -> IO (Either ServantError a)
tryTlsOrDegrade req = do
secureResponse <- req Https
case secureResponse of
Left (ConnectionError connE) -> case fromException connE of
Just (ConnectionError connE2) -> case fromException connE2 of
Just (HttpExceptionRequest _ TlsNotSupported) -> req Http
_ -> pure secureResponse
_ -> pure secureResponse
_ -> pure secureResponse