packages feed

Spock-api-ghcjs-0.11.0.0: src/Web/Spock/Api/Client.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Web.Spock.Api.Client
    ( callEndpoint, callEndpoint' )
where

import Web.Spock.Api

import Data.HVect
import JavaScript.Web.XMLHttpRequest
import qualified Data.Aeson as A
import qualified Data.ByteString.Lazy as BSL
import qualified Data.HVect as HV
import qualified Data.JSString as J
import qualified Data.JSString.Text as J
import qualified Data.Text.Encoding as T

type Header = (J.JSString, J.JSString)

-- | Call an 'Endpoint' defined using the @Spock-api@ package passing extra headers
callEndpoint' ::
    forall p i o. (HasRep (MaybeToList i), HasRep p)
    => Endpoint p i o
    -> [Header]
    -> HVectElim p (HVectElim (MaybeToList i) (IO (Maybe o)))
callEndpoint' ep extraHeaders =
    HV.curry $ \hv -> HV.curry (callEndpointCore' ep extraHeaders hv)

-- | Call an 'Endpoint' defined using the @Spock-api@ package
callEndpoint ::
    forall p i o. (HasRep (MaybeToList i), HasRep p)
    => Endpoint p i o -> HVectElim p (HVectElim (MaybeToList i) (IO (Maybe o)))
callEndpoint ep = callEndpoint' ep []

data EndpointCall p i o
   = EndpointCall
   { epc_point :: !(Endpoint p i o)
   , epc_headers :: ![Header]
   , epc_params :: !(HVect p)
   , epc_body :: !(HVect (MaybeToList i))
   }

callEndpointCore' ::
    forall p i o.
    Endpoint p i o
    -> [Header]
    -> HVect p
    -> HVect (MaybeToList i)
    -> IO (Maybe o)
callEndpointCore' ep hdrs hv b = callEndpointCore (EndpointCall ep hdrs hv b)

callEndpointCore :: forall p i o. EndpointCall p i o -> IO (Maybe o)
callEndpointCore call =
    case call of
      EndpointCall (MethodPost Proxy path) hdrs params (body :&: HNil) ->
          do let rt = J.textToJSString $ renderRoute path params
                 bodyText = J.textToJSString $ T.decodeUtf8 $ BSL.toStrict $ A.encode body
                 req =
                     Request
                     { reqMethod = POST
                     , reqURI = rt
                     , reqLogin = Nothing
                     , reqHeaders = (("Content-Type", "application/json;charset=UTF-8") : hdrs)
                     , reqWithCredentials = False
                     , reqData = StringData bodyText
                     }
             runJsonReq req
      EndpointCall (MethodPut Proxy path) hdrs params (body :&: HNil) ->
          do let rt = J.textToJSString $ renderRoute path params
                 bodyText = J.textToJSString $ T.decodeUtf8 $ BSL.toStrict $ A.encode body
                 req =
                     Request
                     { reqMethod = PUT
                     , reqURI = rt
                     , reqLogin = Nothing
                     , reqHeaders = (("Content-Type", "application/json;charset=UTF-8") : hdrs)
                     , reqWithCredentials = False
                     , reqData = StringData bodyText
                     }
             runJsonReq req
      EndpointCall (MethodGet path) hdrs params HNil ->
          do let rt = J.textToJSString $ renderRoute path params
                 req =
                     Request
                     { reqMethod = GET
                     , reqURI = rt
                     , reqLogin = Nothing
                     , reqHeaders = hdrs
                     , reqWithCredentials = False
                     , reqData = NoData
                     }
             runJsonReq req

runJsonReq :: A.FromJSON o => Request -> IO (Maybe o)
runJsonReq req =
    do response <- xhrText req
       case (status response, contents response) of
         (200, Just txt) ->
             do let res = A.eitherDecodeStrict' (T.encodeUtf8 txt)
                case res of
                  Left errMsg ->
                      do putStrLn errMsg
                         pure Nothing
                  Right val ->
                      pure (Just val)
         _ -> pure Nothing