2 | import Control.Monad.Either.Extra
3 | import Derive.Prelude
11 | %language ElabReflection
18 | Cast String JS.ByteString.ByteString where
22 | Cast JS.ByteString.ByteString String where
31 | data Method = GET | POST
33 | %runElab derive "Method" [Show,Eq,Ord]
38 | Header = (String,String)
42 | data Part : Type where
43 | StringPart : (name, value : String) -> Part
44 | FilePart : (name : String) -> (file : File) -> Part
48 | data RequestBody : Type where
50 | StringBody : (mimeType : String) -> (content : String) -> RequestBody
51 | JSONBody : ToJSON a => a -> RequestBody
52 | FormBody : List Part -> RequestBody
56 | data HTTPError : Type where
58 | NetworkError : HTTPError
59 | BadStatus : Bits16 -> HTTPError
60 | JSONError : String -> DecodingErr -> HTTPError
67 | data Expect : Type -> Type where
68 | ExpectJSON : FromJSON a => (Either HTTPError a -> r) -> Expect r
69 | ExpectString : (Either HTTPError String -> r) -> Expect r
70 | ExpectAny : (Either HTTPError () -> r) -> Expect r
72 | bodyHeaders : RequestBody -> List Header
73 | bodyHeaders Empty = []
74 | bodyHeaders (StringBody m _) = [("Content-Type", m)]
75 | bodyHeaders (JSONBody x) = [("Content-Type", "application/json")]
76 | bodyHeaders (FormBody x) = []
78 | append : FormData -> Part -> JSIO ()
79 | append fd (StringPart name value) = FormData.append fd name value
80 | append fd (FilePart name file) = FormData.append1 fd name file
82 | parameters {0 r : Type}
84 | onerror : (r -> JSIO ()) -> Expect r -> HTTPError -> JSIO ()
85 | onerror h (ExpectJSON f) err = h (f $
Left err)
86 | onerror h (ExpectString f) err = h (f $
Left err)
87 | onerror h (ExpectAny f) err = h (f $
Left err)
89 | onsuccess : (r -> JSIO ()) -> Expect r -> XMLHttpRequest -> JSIO ()
90 | onsuccess h (ExpectString f) x = responseText x >>= h . f . Right
91 | onsuccess h (ExpectAny f) x = h (f $
Right ())
92 | onsuccess h (ExpectJSON {a} f) x = do
94 | h . f . mapFst (JSONError s) $
decode s
96 | onload : (r -> JSIO ()) -> Expect r -> XMLHttpRequest -> JSIO ()
99 | case st >= 200 && st < 300 of
100 | False => onerror h exp (BadStatus st)
101 | True => onsuccess h exp x
103 | xsend : RequestBody -> XMLHttpRequest -> JSIO ()
104 | xsend Empty x = XMLHttpRequest.send x
105 | xsend (StringBody _ s) x = XMLHttpRequest.send' x (Def . Just $
inject s)
106 | xsend (JSONBody d) x = XMLHttpRequest.send' x (Def . Just $
inject $
encode d)
107 | xsend (FormBody ps) x = do
109 | traverseList_ (append fd) ps
110 | XMLHttpRequest.send' x (Def . Just $
inject fd)
118 | -> (headers : List Header)
120 | -> (body : RequestBody)
121 | -> (expect : Expect r)
122 | -> (timeout : Maybe Bits32)
124 | request m headers url body exp tout = C $
\h => Prelude.do
126 | x <- XMLHttpRequest.new
129 | XMLHttpRequestEventTarget.onerror x ?> onerror h exp NetworkError
130 | XMLHttpRequestEventTarget.onload x ?> onload h exp x
131 | XMLHttpRequestEventTarget.ontimeout x ?> onerror h exp Timeout
134 | open_ x (cast $
show m) url
137 | let hs := bodyHeaders body ++ headers
138 | traverseList_ (\(n,h) => setRequestHeader x (cast n) (cast h)) hs
141 | traverse_ (set (timeout x)) tout
148 | get : (url : String) -> (expect : Expect r) -> Cmd r
149 | get u e = request GET [] u Empty e Nothing
153 | getText : (url : String) -> (f : Either HTTPError String -> r) -> Cmd r
154 | getText u = get u . ExpectString
159 | getJSON : FromJSON a => (url : String) -> (f : Either HTTPError a -> r) -> Cmd r
160 | getJSON u = get u . ExpectJSON
164 | post : (url : String) -> (body : RequestBody) -> (expect : Expect r) -> Cmd r
165 | post u b e = request POST [] u b e Nothing