11module Network.HTTP.Affjax
2- ( AJAX
3- , Affjax
2+ ( Affjax
43 , AffjaxRequest , defaultRequest
54 , AffjaxResponse
65 , URL
@@ -16,58 +15,53 @@ module Network.HTTP.Affjax
1615 , retry
1716 ) where
1817
19- import Prelude hiding ( max )
18+ import Prelude
2019
21- import Control.Monad.Aff (Aff , try , delay )
22- import Control.Monad.Aff.Compat as AC
23- import Control.Monad.Eff (kind Effect )
24- import Control.Monad.Eff.Class (liftEff )
25- import Control.Monad.Eff.Exception (Error , error )
26- import Control.Monad.Eff.Ref (REF , newRef , readRef , writeRef )
2720import Control.Monad.Except (runExcept , throwError )
2821import Control.Parallel (parOneOf )
29-
22+ import Data.Argonaut.Core (Json )
23+ import Data.Argonaut.Core as J
3024import Data.Argonaut.Parser (jsonParser )
25+ import Data.Array (intercalate )
3126import Data.Array as Arr
3227import Data.Either (Either (..), either )
3328import Data.Foldable (any )
34- import Data.Foreign ( F , Foreign , ForeignError (JSONError), fail , readString , toForeign )
29+ import Data.FormURLEncoded as FormURLEncoded
3530import Data.Function (on )
3631import Data.Function.Uncurried (Fn2 , runFn2 )
3732import Data.HTTP.Method (Method (..), CustomMethod )
3833import Data.HTTP.Method as Method
3934import Data.Int (toNumber )
4035import Data.Maybe (Maybe (..))
41- import Data.MediaType (MediaType )
4236import Data.Nullable (Nullable , toNullable )
4337import Data.Time.Duration (Milliseconds (..))
44- import Data.Tuple (Tuple (..), fst , snd )
45-
46- import Math (max , pow )
47-
48- import Network.HTTP.Affjax.Request (class Requestable , RequestContent , toRequest )
49- import Network.HTTP.Affjax.Response (class Respondable , ResponseContent , ResponseType (..), fromResponse , responseType , responseTypeToString )
38+ import Effect.Aff (Aff , try , delay )
39+ import Effect.Aff.Compat as AC
40+ import Effect.Class (liftEffect )
41+ import Effect.Exception (Error , error )
42+ import Effect.Ref as Ref
43+ import Foreign (F , Foreign , ForeignError (..), fail , renderForeignError , unsafeReadTagged , unsafeToForeign )
44+ import Math as Math
45+ import Network.HTTP.Affjax.Request as Request
46+ import Network.HTTP.Affjax.Response as Response
5047import Network.HTTP.RequestHeader (RequestHeader (..), requestHeaderName , requestHeaderValue )
5148import Network.HTTP.ResponseHeader (ResponseHeader , responseHeader )
5249import Network.HTTP.StatusCode (StatusCode (..))
5350
54- -- | The effect type for AJAX requests made with Affjax.
55- foreign import data AJAX :: Effect
56-
57- -- | The type for Affjax requests.
58- type Affjax e a = Aff (ajax :: AJAX | e ) (AffjaxResponse a )
51+ -- | The result type for Affjax requests.
52+ type Affjax a = Aff (AffjaxResponse a )
5953
60- type AffjaxRequest a =
54+ type AffjaxRequest =
6155 { method :: Either Method CustomMethod
6256 , url :: URL
6357 , headers :: Array RequestHeader
64- , content :: Maybe a
58+ , content :: Maybe Request.Request
6559 , username :: Maybe String
6660 , password :: Maybe String
6761 , withCredentials :: Boolean
6862 }
6963
70- defaultRequest :: AffjaxRequest Unit
64+ defaultRequest :: AffjaxRequest
7165defaultRequest =
7266 { method: Left GET
7367 , url: " /"
@@ -89,70 +83,70 @@ type AffjaxResponse a =
8983type URL = String
9084
9185-- | Makes a `GET` request to the specified URL.
92- get :: forall e a . Respondable a = > URL -> Affjax e a
93- get u = affjax $ defaultRequest { url = u }
86+ get :: forall a . Response.Response a - > URL -> Affjax a
87+ get rt u = affjax rt $ defaultRequest { url = u }
9488
9589-- | Makes a `POST` request to the specified URL, sending data.
96- post :: forall e a b . Requestable a => Respondable b => URL -> a -> Affjax e b
97- post u c = affjax $ defaultRequest { method = Left POST , url = u, content = Just c }
90+ post :: forall a . Response.Response a -> URL -> Request.Request -> Affjax a
91+ post rt u c = affjax rt $ defaultRequest { method = Left POST , url = u, content = Just c }
9892
9993-- | Makes a `POST` request to the specified URL with the option to send data.
100- post' :: forall e a b . Requestable a => Respondable b => URL -> Maybe a -> Affjax e b
101- post' u c = affjax $ defaultRequest { method = Left POST , url = u, content = c }
94+ post' :: forall a . Response.Response a -> URL -> Maybe Request.Request -> Affjax a
95+ post' rt u c = affjax rt $ defaultRequest { method = Left POST , url = u, content = c }
10296
10397-- | Makes a `POST` request to the specified URL, sending data and ignoring the
10498-- | response.
105- post_ :: forall e a . Requestable a => URL -> a -> Affjax e Unit
106- post_ = post
99+ post_ :: URL -> Request.Request -> Affjax Unit
100+ post_ = post Response .ignore
107101
108102-- | Makes a `POST` request to the specified URL with the option to send data,
109103-- | and ignores the response.
110- post_' :: forall e a . Requestable a => URL -> Maybe a -> Affjax e Unit
111- post_' = post'
104+ post_' :: URL -> Maybe Request.Request -> Affjax Unit
105+ post_' = post' Response .ignore
112106
113107-- | Makes a `PUT` request to the specified URL, sending data.
114- put :: forall e a b . Requestable a => Respondable b => URL -> a -> Affjax e b
115- put u c = affjax $ defaultRequest { method = Left PUT , url = u, content = Just c }
108+ put :: forall a . Response.Response a -> URL -> Request.Request -> Affjax a
109+ put rt u c = affjax rt $ defaultRequest { method = Left PUT , url = u, content = Just c }
116110
117111-- | Makes a `PUT` request to the specified URL with the option to send data.
118- put' :: forall e a b . Requestable a => Respondable b => URL -> Maybe a -> Affjax e b
119- put' u c = affjax $ defaultRequest { method = Left PUT , url = u, content = c }
112+ put' :: forall a . Response.Response a -> URL -> Maybe Request.Request -> Affjax a
113+ put' rt u c = affjax rt $ defaultRequest { method = Left PUT , url = u, content = c }
120114
121115-- | Makes a `PUT` request to the specified URL, sending data and ignoring the
122116-- | response.
123- put_ :: forall e a . Requestable a => URL -> a -> Affjax e Unit
124- put_ = put
117+ put_ :: URL -> Request.Request -> Affjax Unit
118+ put_ = put Response .ignore
125119
126120-- | Makes a `PUT` request to the specified URL with the option to send data,
127121-- | and ignores the response.
128- put_' :: forall e a . Requestable a => URL -> Maybe a -> Affjax e Unit
129- put_' = put'
122+ put_' :: URL -> Maybe Request.Request -> Affjax Unit
123+ put_' = put' Response .ignore
130124
131125-- | Makes a `DELETE` request to the specified URL.
132- delete :: forall e a . Respondable a = > URL -> Affjax e a
133- delete u = affjax $ defaultRequest { method = Left DELETE , url = u }
126+ delete :: forall a . Response.Response a - > URL -> Affjax a
127+ delete rt u = affjax rt $ defaultRequest { method = Left DELETE , url = u }
134128
135129-- | Makes a `DELETE` request to the specified URL and ignores the response.
136- delete_ :: forall e . URL -> Affjax e Unit
137- delete_ = delete
130+ delete_ :: URL -> Affjax Unit
131+ delete_ = delete Response .ignore
138132
139133-- | Makes a `PATCH` request to the specified URL, sending data.
140- patch :: forall e a b . Requestable a => Respondable b => URL -> a -> Affjax e b
141- patch u c = affjax $ defaultRequest { method = Left PATCH , url = u, content = Just c }
134+ patch :: forall a . Response.Response a -> URL -> Request.Request -> Affjax a
135+ patch rt u c = affjax rt $ defaultRequest { method = Left PATCH , url = u, content = Just c }
142136
143137-- | Makes a `PATCH` request to the specified URL with the option to send data.
144- patch' :: forall e a b . Requestable a => Respondable b => URL -> Maybe a -> Affjax e b
145- patch' u c = affjax $ defaultRequest { method = Left PATCH , url = u, content = c }
138+ patch' :: forall a . Response.Response a -> URL -> Maybe Request.Request -> Affjax a
139+ patch' rt u c = affjax rt $ defaultRequest { method = Left PATCH , url = u, content = c }
146140
147141-- | Makes a `PATCH` request to the specified URL, sending data and ignoring the
148142-- | response.
149- patch_ :: forall e a . Requestable a => URL -> a -> Affjax e Unit
150- patch_ = patch
143+ patch_ :: URL -> Request.Request -> Affjax Unit
144+ patch_ = patch Response .ignore
151145
152146-- | Makes a `PATCH` request to the specified URL with the option to send data,
153147-- | and ignores the response.
154- patch_' :: forall e a . Requestable a => URL -> Maybe a -> Affjax e Unit
155- patch_' = patch'
148+ patch_' :: URL -> Maybe Request.Request -> Affjax Unit
149+ patch_' = patch' Response .ignore
156150
157151-- | A sequence of retry delays, in milliseconds.
158152type RetryDelayCurve = Int -> Milliseconds
@@ -168,39 +162,34 @@ type RetryPolicy =
168162defaultRetryPolicy :: RetryPolicy
169163defaultRetryPolicy =
170164 { timeout : Nothing
171- , delayCurve : \n -> Milliseconds $ max (30.0 * 1000.0 ) $ 100.0 * (pow 2.0 $ toNumber (n - 1 ))
165+ , delayCurve : \n -> Milliseconds $ max (30.0 * 1000.0 ) $ 100.0 * (Math . pow 2.0 $ toNumber (n - 1 ))
172166 , shouldRetryWithStatusCode : const false
173167 }
174168
175169-- | Either we have a failure (which may be an exception or a failed response), or we have a successful response.
176170type RetryState e a = Either (Either e a ) a
177171
178172-- | Retry a request using a `RetryPolicy`. After the timeout, the last received response is returned; if it was not possible to communicate with the server due to an error, then this is bubbled up.
179- retry
180- :: forall e a b
181- . Requestable a
182- => RetryPolicy
183- -> (AffjaxRequest a -> Affjax (ref :: REF | e ) b )
184- -> (AffjaxRequest a -> Affjax (ref :: REF | e ) b )
173+ retry :: forall a . RetryPolicy -> (AffjaxRequest -> Affjax a ) -> AffjaxRequest -> Affjax a
185174retry policy run req = do
186175 -- failureRef is either an exception or a failed request
187- failureRef <- liftEff $ newRef Nothing
176+ failureRef <- liftEffect $ Ref .new Nothing
188177 let loop = go failureRef
189178 case policy.timeout of
190179 Nothing -> loop 1
191180 Just timeout -> do
192181 result <- parOneOf [ Just <$> loop 1 , Nothing <$ delay timeout ]
193182 case result of
194183 Nothing -> do
195- failure <- liftEff $ readRef failureRef
184+ failure <- liftEffect $ Ref .read failureRef
196185 case failure of
197186 Nothing -> throwError $ error " Timeout"
198187 Just failure' -> either throwError pure failure'
199188 Just resp -> pure resp
200189 where
201190 retryState
202- :: Either Error (AffjaxResponse b )
203- -> RetryState Error (AffjaxResponse b )
191+ :: Either Error (AffjaxResponse a )
192+ -> RetryState Error (AffjaxResponse a )
204193 retryState (Left exn) = Left $ Left exn
205194 retryState (Right resp) =
206195 case resp.status of
@@ -215,73 +204,81 @@ retry policy run req = do
215204 result <- retryState <$> try (run req)
216205 case result of
217206 Left err -> do
218- liftEff $ writeRef failureRef $ Just err
207+ liftEffect $ Ref .write ( Just err) failureRef
219208 delay (policy.delayCurve n)
220209 go failureRef (n + 1 )
221210 Right resp -> pure resp
222211
223212-- | Makes an `Affjax` request.
224- affjax
225- :: forall e a b
226- . Requestable a
227- => Respondable b
228- => AffjaxRequest a
229- -> Affjax e b
230- affjax req = do
231- res <- AC .fromEffFnAff $ runFn2 _ajax responseHeader req'
232- case res { response = _ } <$> runExcept (fromResponse' res.response) of
233- Left err -> throwError $ error (show err)
234- Right res' -> pure res'
213+ affjax :: forall a . Response.Response a -> AffjaxRequest -> Affjax a
214+ affjax rt req = do
215+ res <- AC .fromEffectFnAff $ runFn2 _ajax responseHeader req'
216+ case runExcept (fromResponse' res.response) of
217+ Left err -> throwError $ error $ intercalate " \n " (map renderForeignError err)
218+ Right res' -> pure (res { response = res' })
235219 where
236220
237- req' :: AjaxRequest
221+ req' :: AjaxRequest a
238222 req' =
239223 { method: Method .print req.method
240224 , url: req.url
241- , headers: (\h -> { field: requestHeaderName h, value: requestHeaderValue h }) <$> headers
242- , content: toNullable (snd requestSettings )
243- , responseType: responseTypeToString (snd responseSettings)
225+ , headers: (\h -> { field: requestHeaderName h, value: requestHeaderValue h }) <$> headers req.content
226+ , content: toNullable (extractContent <$> req.content )
227+ , responseType: Response .toResponseType rt
244228 , username: toNullable req.username
245229 , password: toNullable req.password
246230 , withCredentials: req.withCredentials
247231 }
248232
249- requestSettings :: Tuple (Maybe MediaType ) (Maybe RequestContent )
250- requestSettings = case toRequest <$> req.content of
251- Nothing -> Tuple Nothing Nothing
252- Just (Tuple mime rt) -> Tuple mime (Just rt)
253-
254- responseSettings :: Tuple (Maybe MediaType ) (ResponseType b )
255- responseSettings = responseType
256-
257- headers :: Array RequestHeader
258- headers =
259- addHeader (ContentType <$> fst requestSettings) $
260- addHeader (Accept <$> fst responseSettings)
233+ extractContent :: Request.Request -> Foreign
234+ extractContent = case _ of
235+ Request.ArrayView f → f unsafeToForeign
236+ Request.Blob x → unsafeToForeign x
237+ Request.Document x → unsafeToForeign x
238+ Request.String x → unsafeToForeign x
239+ Request.FormData x → unsafeToForeign x
240+ Request.FormURLEncoded x → unsafeToForeign (FormURLEncoded .encode x)
241+ Request.Json x → unsafeToForeign (J .stringify x)
242+
243+ headers :: Maybe Request.Request -> Array RequestHeader
244+ headers reqContent =
245+ addHeader (ContentType <$> (Request .toMediaType =<< reqContent)) $
246+ addHeader (Accept <$> Response .toMediaType rt)
261247 req.headers
262248
263249 addHeader :: Maybe RequestHeader -> Array RequestHeader -> Array RequestHeader
264250 addHeader mh hs = case mh of
265251 Just h | not $ any (on eq requestHeaderName h) hs -> hs `Arr.snoc` h
266252 _ -> hs
267253
268- parseJSON :: String -> F Foreign
269- parseJSON = either (fail <<< JSONError ) (pure <<< toForeign) <<< jsonParser
270-
271- fromResponse' :: ResponseContent -> F b
272- fromResponse' = case snd responseSettings of
273- JSONResponse -> fromResponse <=< parseJSON <=< readString
274- _ -> fromResponse
275-
276- type AjaxRequest =
254+ parseJSON :: String -> F Json
255+ parseJSON = case _ of
256+ " " -> pure J .jsonEmptyObject
257+ str -> either (fail <<< ForeignError ) pure (jsonParser str)
258+
259+ fromResponse' :: Foreign -> F a
260+ fromResponse' = case rt of
261+ Response.ArrayBuffer _ -> unsafeReadTagged " ArrayBuffer"
262+ Response.Blob _ -> unsafeReadTagged " Blob"
263+ Response.Document _ -> unsafeReadTagged " Document"
264+ Response.Json coe -> coe <<< parseJSON <=< unsafeReadTagged " String"
265+ Response.String _ -> unsafeReadTagged " String"
266+ Response.Ignore coe -> const $ coe (pure unit)
267+
268+ type AjaxRequest a =
277269 { method :: String
278270 , url :: URL
279271 , headers :: Array { field :: String , value :: String }
280- , content :: Nullable RequestContent
272+ , content :: Nullable Foreign
281273 , responseType :: String
282274 , username :: Nullable String
283275 , password :: Nullable String
284276 , withCredentials :: Boolean
285277 }
286278
287- foreign import _ajax :: forall e . Fn2 (String -> String -> ResponseHeader ) AjaxRequest (AC.EffFnAff (ajax :: AJAX | e ) (AffjaxResponse Foreign ))
279+ foreign import _ajax
280+ :: forall a
281+ . Fn2
282+ (String -> String -> ResponseHeader )
283+ (AjaxRequest a )
284+ (AC.EffectFnAff (AffjaxResponse Foreign ))
0 commit comments