Skip to content

Commit 2e9a918

Browse files
authored
Merge pull request #107 from garyb/remove-classes
Remove `Requestable` and `Respondable` classes
2 parents f3bdc05 + cece99b commit 2e9a918

File tree

8 files changed

+233
-337
lines changed

8 files changed

+233
-337
lines changed

bower.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@
2323
"package.json"
2424
],
2525
"dependencies": {
26-
"purescript-aff": "^4.0.0",
27-
"purescript-argonaut-core": "^3.0.0",
28-
"purescript-arraybuffer-types": "^1.0.0",
29-
"purescript-dom": "^4.0.0",
30-
"purescript-foreign": "^4.0.0",
31-
"purescript-form-urlencoded": "^3.0.0",
32-
"purescript-http-methods": "^3.0.0",
33-
"purescript-integers": "^3.0.0",
34-
"purescript-math": "^2.0.0",
35-
"purescript-media-types": "^3.0.0",
36-
"purescript-nullable": "^3.0.0",
37-
"purescript-refs": "^3.0.0",
38-
"purescript-unsafe-coerce": "^3.0.0"
26+
"purescript-aff": "^5.0.0",
27+
"purescript-argonaut-core": "^4.0.0",
28+
"purescript-arraybuffer-types": "^2.0.0",
29+
"purescript-web-xhr": "^2.0.0",
30+
"purescript-foreign": "^5.0.0",
31+
"purescript-form-urlencoded": "^4.0.0",
32+
"purescript-http-methods": "^4.0.0",
33+
"purescript-integers": "^4.0.0",
34+
"purescript-math": "^2.1.1",
35+
"purescript-media-types": "^4.0.0",
36+
"purescript-nullable": "^4.0.0",
37+
"purescript-refs": "^4.1.0",
38+
"purescript-unsafe-coerce": "^4.0.0"
3939
},
4040
"devDependencies": {
41-
"purescript-console": "^3.0.0"
41+
"purescript-console": "^4.1.0"
4242
}
4343
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"express": "^4.14.0",
1212
"pulp": "^11.0.0",
1313
"purescript-psa": "^0.5.0",
14-
"purescript": "^0.11.0",
14+
"purescript": "slamdata/node-purescript#0.12",
1515
"rimraf": "^2.5.4",
1616
"xhr2": "^0.1.3"
1717
}

src/Network/HTTP/Affjax.purs

Lines changed: 103 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
module 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)
2720
import Control.Monad.Except (runExcept, throwError)
2821
import Control.Parallel (parOneOf)
29-
22+
import Data.Argonaut.Core (Json)
23+
import Data.Argonaut.Core as J
3024
import Data.Argonaut.Parser (jsonParser)
25+
import Data.Array (intercalate)
3126
import Data.Array as Arr
3227
import Data.Either (Either(..), either)
3328
import Data.Foldable (any)
34-
import Data.Foreign (F, Foreign, ForeignError(JSONError), fail, readString, toForeign)
29+
import Data.FormURLEncoded as FormURLEncoded
3530
import Data.Function (on)
3631
import Data.Function.Uncurried (Fn2, runFn2)
3732
import Data.HTTP.Method (Method(..), CustomMethod)
3833
import Data.HTTP.Method as Method
3934
import Data.Int (toNumber)
4035
import Data.Maybe (Maybe(..))
41-
import Data.MediaType (MediaType)
4236
import Data.Nullable (Nullable, toNullable)
4337
import 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
5047
import Network.HTTP.RequestHeader (RequestHeader(..), requestHeaderName, requestHeaderValue)
5148
import Network.HTTP.ResponseHeader (ResponseHeader, responseHeader)
5249
import 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
7165
defaultRequest =
7266
{ method: Left GET
7367
, url: "/"
@@ -89,70 +83,70 @@ type AffjaxResponse a =
8983
type 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.
158152
type RetryDelayCurve = Int -> Milliseconds
@@ -168,39 +162,34 @@ type RetryPolicy =
168162
defaultRetryPolicy :: RetryPolicy
169163
defaultRetryPolicy =
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.
176170
type 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
185174
retry 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

Comments
 (0)