1 | module Pact.Server.Util
5 | import Data.Vect.Quantifiers
12 | import Pact.Server.Core
18 | import IO.Async.Loop.Posix
19 | import IO.Async.Loop.Epoll
20 | import FS.Concurrent
22 | import Control.Monad.Error.Either
36 | matchAPI : (api: API) -> Vect m String -> Request -> Either String (HVect api.types)
37 | matchAPI ((:>) { prf } path ep) segs req = if req.method == ep.method
38 | then matchPath path segs { allprf = prf }
39 | else Left "Method not allowed"
41 | matchRouteItem : {m : Type -> Type} -> RouteItem m -> Vect n String -> Request -> Bool
42 | matchRouteItem (api :=> handler) ss req = case matchAPI api ss req of
47 | findRouteItem : {m : Type -> Type} -> List (RouteItem m) -> Vect n String -> Request -> Maybe (RouteItem m)
48 | findRouteItem [] _ req = Nothing
49 | findRouteItem (item :: routes) segs req = case matchRouteItem item segs req of
51 | False => findRouteItem routes segs req
53 | strToVect : String -> (
n ** Vect n String)
54 | strToVect s = (
length list ** fromList list)
57 | list = filter (/= "") . forget . split (== '/') $
s
59 | findOnRouter : {m : Type -> Type} -> Router m -> Request -> Maybe (RouteItem m)
60 | findOnRouter (MkRouter routes) req = case strToVect req.uri of
61 | (
n ** segs)
=> findRouteItem routes segs req
63 | applyHandler : (api : API) -> (params: HVect api.types) -> Lazy (Either DecodingErr (ApiReqBody api)) -> (handler: GetHandlerType m api) -> Either String (GetEPFromAPI m api)
64 | applyHandler ((StaticPath _) :> ep) [()] _ handler = Right handler
65 | applyHandler ((Capture _ t) :> ep) [param] _ handler = Right $
handler param
66 | applyHandler ((ReqBody reqType) :> ep) _ (Right reqBody) handler = Right $
handler reqBody
67 | applyHandler ((ReqBody reqType) :> ep) _ (Left err) handler = Left $
"parse error: \{show err}"
68 | applyHandler ((:>) ((StaticPath _) :/ path') ep { prf = prf' :: prfs}) (param :: params) reqBody handler = applyHandler {m} (path' :> ep) params reqBody handler
69 | applyHandler ((:>) ((Capture _ t) :/ path') ep { prf = prf' :: prfs}) (param :: params) reqBody handler = applyHandler {m} (path' :> ep) params reqBody $
handler param
71 | applyHandler _ _ _ _ = Left "Invalid request"
73 | applyHandlerWithRequest : {m : Type -> Type} -> (routeItem : RouteItem m) -> (req : Request) -> String -> Either String (GetEndpointTypeFromRouteItem m routeItem)
74 | applyHandlerWithRequest ((:=>) api@(path :> ep) handler { mimeRenderProof } { reqBodyProof }) req reqBody =
76 | (
_ ** segs)
= strToVect uri
77 | eParams = matchPath path segs
78 | reqBody' : Either DecodingErr (PathReqBody path) = delay $
decode reqBody
79 | in case (eParams) of
80 | (Right params) => applyHandler {m} (path :> ep) params reqBody' handler
81 | (Left err) => Left $
"\{err}"
84 | emitResponse : (v: Verb) -> MimeRender (VerbAccept v) (VerbResponse v) => VerbResponse v -> HTTPResponse
85 | emitResponse (MkVerb _ code accept' response') res = emit $
MkResponse code headers (Just bodyStr)
88 | bodyStr = mimeRender { ctype = accept' } res
90 | bodyLen = show $
length bodyStr
91 | contentTypeStr : String
92 | contentTypeStr = show $
contentType { ctype = accept' }
94 | headers = if code == code_204 then emptyHeaders else fromList [("Content-Type", contentTypeStr ), ("Content-Length", bodyLen)]
97 | accumAsString : Pull f ByteString es () -> Pull f q es String
100 | |> map (toString . ByteString.fastConcat . (<>> []))
103 | serve : {m : Type -> Type} -> Hoistable m => Router m -> Request -> HTTPResponse
104 | serve router req = accumAsString req.body >>= hand
106 | hand : String -> HTTPResponse
107 | hand reqBody = case findOnRouter router req of
108 | Just routeItem@((:=>) api@(path :> verb) handler { mimeRenderProof } ) => case (applyHandlerWithRequest routeItem req reqBody) of
109 | Right ioRes => (liftIO . runEitherT . hoist {m} $
ioRes ) >>= \case
110 | Right res => emitResponse verb res
111 | Left err => throw err
112 | Left err => throw InvalidRequest
113 | Nothing => throw InvalidRequest