packages feed

rfc-http-client (empty) → 0.0.0.1

raw patch · 6 files changed

+318/−0 lines, 6 filesdep +aesondep +basedep +http-client

Dependencies added: aeson, base, http-client, http-client-tls, http-types, lens, network-uri, rfc-prelude, scientific, servant-server, vector, wreq

Files

+ LICENSE view
@@ -0,0 +1,7 @@+Copyright Robert Fischer (c) 2018++Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,4 @@+RFC HTTP Client+===================++This provides a bunch of utility functions and a monad for simplifying the use of [`wreq`](https://hackage.haskell.org/package/wreq).
+ package.common.yaml view
@@ -0,0 +1,103 @@+- &defaults+  author: Robert Fischer+  maintainer: smokejumperit+rfc@gmail.com+  license: MIT+  license-file: LICENSE++  flags:+    production:+      description: If true, disables failing on warnings and enables aggressive optimizations.+      manual: true+      default: false++  library:+    source-dirs:+      - './src'++  extra-source-files:+    - LICENSE+    - README.md+    - package.yaml+    - package.common.yaml++  default-extensions:+    - ApplicativeDo+    - BangPatterns+    - ConstraintKinds+    - DataKinds+    - DefaultSignatures+    - DeriveFoldable+    - DeriveFunctor+    - DeriveGeneric+    - DeriveLift+    - DeriveTraversable+    - ExistentialQuantification+    - FlexibleContexts+    - FlexibleInstances+    - FunctionalDependencies+    - GADTs+    - GeneralizedNewtypeDeriving+    - InstanceSigs+    - KindSignatures+    - LambdaCase+    - MultiParamTypeClasses+    - MultiWayIf+    - NamedFieldPuns+    - PatternSynonyms+    - RankNTypes+    - ScopedTypeVariables+    - StandaloneDeriving+    - TupleSections+    - TypeApplications+    - TypeFamilies+    - TypeFamilyDependencies+    - TypeOperators  +    - RebindableSyntax+    - MonadFailDesugaring+    - DisambiguateRecordFields++  ghc-options: +    - -Wall+    - -Wcompat +    - -Wincomplete-record-updates +    - -Wincomplete-uni-patterns +    - -Wredundant-constraints +    - -fno-max-relevant-binds +    - -fprint-potential-instances +    - -fprint-explicit-foralls+    - -Wno-missing-monadfail-instances+    - -fno-warn-name-shadowing +    - -fno-warn-tabs +    - -fno-warn-orphans+    - -Wnoncanonical-monad-instances +    - -Wall-missed-specialisations+    - -Wnoncanonical-monadfail-instances +    - -Wimplicit-prelude +    - -Wmissing-exported-signatures +    - -Widentities+    - -j++  when:+  - condition: flag(production)+    then:+      ghc-options:+        - -feager-blackholing +        - -funbox-strict-fields +        - -fstatic-argument-transformation+        - -flate-dmd-anal +        - -fexpose-all-unfoldings +        - -fspecialise-aggressively +        - -fexcess-precision +        - -fmax-simplifier-iterations=15+        - -fsimplifier-phases=4+        - -fstrictness-before=3+        - -funfolding-keeness-factor=1.5+        - -funfolding-use-threshold=150+    else:+      cpp-options:+        - -DDEVELOPMENT+      ghc-options:+        - -freverse-errors+        - -Werror +        - -dcore-lint +        - -dcmm-lint
+ package.yaml view
@@ -0,0 +1,27 @@+name: rfc-http-client+version: 0.0.0.1+synopsis: The HTTP client extensions from the Robert Fischer Commons.+description: >+  Provides best-of-breed support for HTTP client calls in Haskell.+category: Web+github: RobertFischer/rfc/rfc-http-client+author: Robert Fischer+license: MIT++_common: !include package.common.yaml++<<: *defaults++dependencies:+  - rfc-prelude +  - base >= 4.10 && < 5+  - lens >= 4.16+  - aeson >= 1.4+  - scientific >= 0.3+  - vector >= 0.12+  - servant-server >= 0.13+  - network-uri >= 2.6+  - http-client-tls >= 0.3+  - http-client >= 0.5+  - wreq >= 0.5+  - http-types >= 0.12
+ rfc-http-client.cabal view
@@ -0,0 +1,63 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 28a30d28f7b32c113a6b309cf6f78f5ca01200c74f32a0baeee0f5d4df4096f2++name:           rfc-http-client+version:        0.0.0.1+synopsis:       The HTTP client extensions from the Robert Fischer Commons.+description:    Provides best-of-breed support for HTTP client calls in Haskell.+category:       Web+homepage:       https://github.com/RobertFischer/rfc#readme+bug-reports:    https://github.com/RobertFischer/rfc/issues+author:         Robert Fischer+maintainer:     smokejumperit+rfc@gmail.com+license:        MIT+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    LICENSE+    package.common.yaml+    package.yaml+    README.md++source-repository head+  type: git+  location: https://github.com/RobertFischer/rfc+  subdir: rfc-http-client++flag production+  description: If true, disables failing on warnings and enables aggressive optimizations.+  manual: True+  default: False++library+  exposed-modules:+      RFC.HTTP.Client+  other-modules:+      Paths_rfc_http_client+  hs-source-dirs:+      ./src+  default-extensions: ApplicativeDo BangPatterns ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns PatternSynonyms RankNTypes ScopedTypeVariables StandaloneDeriving TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators RebindableSyntax MonadFailDesugaring DisambiguateRecordFields+  ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -fno-max-relevant-binds -fprint-potential-instances -fprint-explicit-foralls -Wno-missing-monadfail-instances -fno-warn-name-shadowing -fno-warn-tabs -fno-warn-orphans -Wnoncanonical-monad-instances -Wall-missed-specialisations -Wnoncanonical-monadfail-instances -Wimplicit-prelude -Wmissing-exported-signatures -Widentities -j+  build-depends:+      aeson >=1.4+    , base >=4.10 && <5+    , http-client >=0.5+    , http-client-tls >=0.3+    , http-types >=0.12+    , lens >=4.16+    , network-uri >=2.6+    , rfc-prelude+    , scientific >=0.3+    , servant-server >=0.13+    , vector >=0.12+    , wreq >=0.5+  if flag(production)+    ghc-options: -feager-blackholing -funbox-strict-fields -fstatic-argument-transformation -flate-dmd-anal -fexpose-all-unfoldings -fspecialise-aggressively -fexcess-precision -fmax-simplifier-iterations=15 -fsimplifier-phases=4 -fstrictness-before=3 -funfolding-keeness-factor=1.5 -funfolding-use-threshold=150+  else+    ghc-options: -freverse-errors -Werror -dcore-lint -dcmm-lint+    cpp-options: -DDEVELOPMENT+  default-language: Haskell2010
+ src/RFC/HTTP/Client.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude     #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE UndecidableInstances  #-}++module RFC.HTTP.Client+  ( withAPISession+  , HasAPIClient(..)+  , HasHttpManager(..)+  , BadStatusException+  , apiGet+  , module Network.Wreq.Session+  , module Network.HTTP.Types.Status+  , geocodeGet+  , GoogleGeocodeResult(..)+  ) where++import           Control.Lens+import           Data.Aeson                as JSON+import           Data.Aeson.Types          as JSON+import qualified Data.Scientific           as Sci+import           Data.Vector               ( (!?) )+import           Network.HTTP.Client       ( Manager, ManagerSettings, newManager )+import           Network.HTTP.Client.TLS   ( tlsManagerSettings )+import           Network.HTTP.Types.Status hiding ( statusCode, statusMessage )+import           Network.URI               as URI+import           Network.Wreq.Lens+import           Network.Wreq.Session      hiding ( withAPISession )+import           RFC.Prelude+import           Servant.Server++data GoogleGeocodeResult = GoogleGeocodeResult+  { ggrLat  :: Float+  , ggrLng  :: Float+  , ggrAddr :: StrictText+  } deriving (Eq,Ord,Show,Generic,Typeable)+instance FromJSON GoogleGeocodeResult where+  parseJSON = JSON.withObject "GoogleGeocodeResult" $ \v -> do+      resultsAry <- v .: "results"+      result <- JSON.withArray "results[0]" extractHead resultsAry+      withObject "results[0] content" parseResult result+    where+      extractHead :: JSON.Array -> JSON.Parser JSON.Value+      extractHead vec =+        maybe+          (fail "Empty results array returned")+          return+          (vec !? 0)+      parseResult :: JSON.Object -> JSON.Parser GoogleGeocodeResult+      parseResult obj = do+        addr <- obj .: "formatted_address" >>= withText "results[0].formatted_address" return+        geometry <- obj .: "geometry" >>= withObject "results[0].geometry" return+        location <- geometry .: "location" >>= withObject "results[0].geometry.location" return+        lat <- location .: "lat" >>= withScientific "results[0].geometry.location.lat" (return . Sci.toRealFloat)+        lng <- location .: "lng" >>= withScientific "results[0].geometry.location.lng" (return . Sci.toRealFloat)+        return GoogleGeocodeResult { ggrLat = lat, ggrLng = lng, ggrAddr = addr }++rfcManagerSettings :: ManagerSettings+rfcManagerSettings = tlsManagerSettings++createRfcManager :: (MonadIO m) => m Manager+createRfcManager = liftIO $ newManager rfcManagerSettings++withAPISession :: (MonadIO m) => (Session -> m a) -> m a+withAPISession = (>>=) $ liftIO (newSessionControl Nothing rfcManagerSettings)++newtype BadStatusException = BadStatusException (Status,URI)+  deriving (Show,Eq,Ord,Generic,Typeable)+instance Exception BadStatusException++apiExecute :: (HasAPIClient m, MonadUnliftIO m)  =>+  URI -> (Session -> String -> IO (Response LazyByteString)) -> (LazyByteString -> m a) -> m a+apiExecute rawUrl action converter = webExecute rawUrl action >>= converter++webExecute :: (HasAPIClient m, MonadUnliftIO m) =>+  URI -> (Session -> String -> IO (Response LazyByteString)) -> m LazyByteString+webExecute rawUrl action = do+  session <- getAPIClient+  response <- liftIO $ action session url+  let status = response ^. responseStatus+  case status ^. statusCode of+    200 -> return $ response ^. responseBody+    _   -> throwIO $ badResponseStatus status+  where+    url = show rawUrl+    badResponseStatus status = BadStatusException (status, rawUrl)++apiGet :: (HasAPIClient m, FromJSON a, MonadUnliftIO m, Exception e) => URI -> (e -> m a) -> m a+apiGet url onError =+  handle onError $ apiExecute url get decodeOrDie++geocodeGet ::+  (HasAPIClient m, MonadUnliftIO m, Exception e) =>+  StrictText -> (e -> m GoogleGeocodeResult) -> m GoogleGeocodeResult+geocodeGet addr onError = handle onError $+    case URI.parseURI "https://maps.googleapis.com/maps/api/geocode/json" of+      Nothing -> throwIO err500 { errBody = "Cannot parse the geocoding URI" }+      Just uri -> do+        let address = URI.escapeURIString URI.isUnescapedInURIComponent (fromText addr)+        let apiKey = "AIzaSyDL1yQ2fFJExMM6CcmsgpklR2Q4c-O4kks"+        let uriWithQuery = uri { uriQuery = "address=" <> address <> "&" <> "key=" <> apiKey <> "&" <> uriQuery uri }+        apiGet uriWithQuery onError++class HasAPIClient m where+  getAPIClient :: m Session++class HasHttpManager m where+  getHttpManager :: m Manager++instance (MonadIO m) => HasHttpManager m where+  getHttpManager = liftIO createRfcManager