Home > Enterprise >  Trying to execute https get request in haskell with oAuth2
Trying to execute https get request in haskell with oAuth2

Time:09-17

This is my attempt at trying to connect to TDAmeritrade's api to receive a json response using the Network.HTTP.Req library in haskell.

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Lib
import Wuss
import Control.Concurrent
import Control.Monad
import Data.Text
import Network.WebSockets 
import Network.Connection
import Network.WebSockets.Stream
import Data.Text.Lazy
import Data.ByteString
import Lens.Micro.GHC.Internal
import Network.Curl
import Data.ByteString.Lazy.UTF8
import Network.HTTP.Req
import Control.Monad
import Control.Monad.IO.Class
import Data.Aeson
import Data.Maybe 
import Data.Monoid 
import GHC.Generics
import Network.HTTP.Req
import Data.ByteString.Char8

tok = Data.ByteString.Char8.pack "accesstoken"
main :: IO ()
main = runReq defaultHttpConfig $ do
  v <- req GET (https "api.tdameritrade.com" /: "v1" /: "userprincipals?fields=streamerConnectionInfo") (NoReqBody) jsonResponse (oAuth2Bearer tok)
  liftIO $ Data.ByteString.Char8.putStrLn (responseBody v)

if done correctly the response should only give an error that the auth token is incorrect because i purposely did not provide a valid token here. "accesstoken"

if i use bsResponse instead of jsonResponse it type checks but it does not type check with jsonResponse. The response from TDAmeritrade is going to be in JSON.

this is the error:

No instance for (FromJSON Data.ByteString.Char8.ByteString)
    arising from a use of ‘req’

this is the result if I use bsResponse:

  port                 = 443
  secure               = True
  requestHeaders       = [("Authorization","<REDACTED>")]
  path                 = "/v1/userprincipals?fields=streamerConnectionInfo"
  queryString          = ""
  method               = "GET"
  proxy                = Nothing
  rawBody              = False
  redirectCount        = 10
  responseTimeout      = ResponseTimeoutDefault
  requestVersion       = HTTP/1.1
}
 (StatusCodeException (Response {responseStatus = Status {statusCode = 400, statusMessage = "Bad Request"}, responseVersion = HTTP/1.1, responseHeaders = [("Date","Wed, 08 Sep 2021 15:27:41 GMT"),("Content-Type","application/json"),("Content-Length","78"),("Connection","keep-alive"),("Host","api.tdameritrade.com"),("X-Forwarded-Port","9002"),("X-Forwarded-Proto","http"),("Accept-Encoding","gzip"),("Authorization","Bearer accesstoken"),("NS-Proxy-Client-IP","82.46.185.98"),("Access-Control-Allow-Origin",""),("Access-Control-Allow-Headers","origin, x-requested-with, accept, authorization, content-type, correlationid, apikey, application-name"),("Access-Control-Max-Age","3628800"),("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH"),("X-Xss-Protection","1; mode=block"),("X-Content-Type-Options","nosniff"),("X-Frame-Options","SAMEORIGIN"),("Content-Security-Policy","frame-ancestors 'self'"),("Cache-Control","no-cache,no-store,must-revalidate"),("Strict-Transport-Security","max-age=31536000")], responseBody = (), responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}) "\n          {\n        \"error\":\"The endpoint doesnt exist.\"\n      \t\t}\n          "))

for reference this curl command in the terminal yields the correct response:

curl -X GET --header "Authorization: Bearer authorizationtoken" "https://api.tdameritrade.com/v1/userprincipals?fields=streamerConnectionInfo"

I am completely lost. please help

CodePudding user response:

To fix your immediate error you have to use the (=:) function construct the request parameter instead of manually writing the ?fields=streamerConnectionInfo part. And you also need to disable error status code checking. This is working code that replicates your curl command:

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Char8
import Network.HTTP.Req
import Control.Monad.IO.Class
import Data.Text

tok = Data.ByteString.Char8.pack "accesstoken"
main :: IO ()
main = runReq defaultHttpConfig { httpConfigCheckResponse = \_ _ _ -> Nothing } $ do
  v <- req GET (https "api.tdameritrade.com" /: "v1" /: "userprincipals") (NoReqBody) bsResponse ("fields" =: ("streamerConnectionInfo" :: Text) <> oAuth2Bearer tok)
  liftIO $ Data.ByteString.Char8.putStrLn (responseBody v)

Another problem is accessing fields from the resulting JSON body. I would recommend starting by reading the documentation of Aeson which is the main Haskell JSON package. If you use the jsonResponse request type then it automatically converts the response to any type with a FromJSON instance.

Perhaps this stackoverflow answer about working with JSON in Haskell can also help you.

  • Related