Add POST handler
This commit is contained in:
+34
-38
@@ -1,4 +1,4 @@
|
|||||||
module App where
|
module App (app) where
|
||||||
|
|
||||||
-- import Types (SqlRow, getRow)
|
-- import Types (SqlRow, getRow)
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import Data.Text hiding (map)
|
|||||||
import Data.Maybe (listToMaybe, fromMaybe)
|
import Data.Maybe (listToMaybe, fromMaybe)
|
||||||
import Text.Regex.TDFA ((=~))
|
import Text.Regex.TDFA ((=~))
|
||||||
import Data.Ord (comparing)
|
import Data.Ord (comparing)
|
||||||
|
import Data.HashMap.Strict (keys, elems, filterWithKey, toList)
|
||||||
-- import Data.Map (intersection, fromList, toList, Map)
|
-- import Data.Map (intersection, fromList, toList, Map)
|
||||||
import Data.List (sortBy)
|
import Data.List (sortBy)
|
||||||
-- import qualified Data.Set as S
|
-- import qualified Data.Set as S
|
||||||
@@ -28,7 +29,7 @@ import Network.Wai
|
|||||||
-- import Network.Wai.Internal
|
-- import Network.Wai.Internal
|
||||||
-- import Network.Wai.Middleware.Cors (CorsResourcePolicy(..))
|
-- import Network.Wai.Middleware.Cors (CorsResourcePolicy(..))
|
||||||
|
|
||||||
import Data.ByteString.Char8 hiding (zip, map)
|
import Data.ByteString.Char8 hiding (zip, map, elem)
|
||||||
import Data.String.Conversions (cs)
|
import Data.String.Conversions (cs)
|
||||||
-- import qualified Data.CaseInsensitive as CI
|
-- import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
@@ -73,7 +74,6 @@ app conn req respond =
|
|||||||
. whereT qq
|
. whereT qq
|
||||||
$ selectStar qt
|
$ selectStar qt
|
||||||
)
|
)
|
||||||
|
|
||||||
row <- listToMaybe <$> uncurry (query conn) select
|
row <- listToMaybe <$> uncurry (query conn) select
|
||||||
let (tableTotal, queryTotal, body) =
|
let (tableTotal, queryTotal, body) =
|
||||||
fromMaybe (0, 0, "" :: ByteString) row
|
fromMaybe (0, 0, "" :: ByteString) row
|
||||||
@@ -86,7 +86,6 @@ app conn req respond =
|
|||||||
. map (join (***) cs)
|
. map (join (***) cs)
|
||||||
. parseSimpleQuery
|
. parseSimpleQuery
|
||||||
$ rawQueryString req
|
$ rawQueryString req
|
||||||
|
|
||||||
return $ responseLBS status
|
return $ responseLBS status
|
||||||
[jsonH, contentRange,
|
[jsonH, contentRange,
|
||||||
("Content-Location",
|
("Content-Location",
|
||||||
@@ -94,6 +93,22 @@ app conn req respond =
|
|||||||
)
|
)
|
||||||
] (cs body)
|
] (cs body)
|
||||||
|
|
||||||
|
([table], "POST") ->
|
||||||
|
handleJsonObj req (\obj -> do
|
||||||
|
let qt = QualifiedTable schema (cs table)
|
||||||
|
_ <- uncurry (execute conn)
|
||||||
|
$ insertInto qt (map cs $ keys obj) (elems obj)
|
||||||
|
primaryKeys <- map cs <$> primaryKeyColumns conn qt
|
||||||
|
let primaries = filterWithKey (const . (`elem` primaryKeys)) obj
|
||||||
|
let params = urlEncodeVars
|
||||||
|
$ map (\t -> (cs $ fst t, "eq." <> cs (encode $ snd t)))
|
||||||
|
$ toList primaries
|
||||||
|
return $ responseLBS status201
|
||||||
|
[ jsonH
|
||||||
|
, (hLocation, "/" <> cs table <> "?" <> cs params)
|
||||||
|
] ""
|
||||||
|
)
|
||||||
|
|
||||||
(_, _) ->
|
(_, _) ->
|
||||||
return $ responseLBS status404 [] ""
|
return $ responseLBS status404 [] ""
|
||||||
|
|
||||||
@@ -133,12 +148,24 @@ requestedSchema hdrs =
|
|||||||
accept = lookup hAccept hdrs :: Maybe ByteString
|
accept = lookup hAccept hdrs :: Maybe ByteString
|
||||||
verStr = (=~ verRegex) <$> accept :: Maybe [[ByteString]]
|
verStr = (=~ verRegex) <$> accept :: Maybe [[ByteString]]
|
||||||
|
|
||||||
parsePayload :: FromJSON j => Request -> IO (Either String j)
|
|
||||||
parsePayload = fmap eitherDecode . strictRequestBody
|
|
||||||
|
|
||||||
jsonH :: Header
|
jsonH :: Header
|
||||||
jsonH = (hContentType, "application/json")
|
jsonH = (hContentType, "application/json")
|
||||||
|
|
||||||
|
handleJsonObj :: Request -> (Object -> IO Response) -> IO Response
|
||||||
|
handleJsonObj req handler = do
|
||||||
|
parse <- fmap eitherDecode . strictRequestBody $ req
|
||||||
|
case parse of
|
||||||
|
Left err ->
|
||||||
|
return $ responseLBS status400 [jsonH] jErr
|
||||||
|
where
|
||||||
|
jErr = encode . object $
|
||||||
|
[("error", String $ "Failed to parse JSON payload. " <> cs err)]
|
||||||
|
Right (Object o) -> handler o
|
||||||
|
Right _ ->
|
||||||
|
return $ responseLBS status400 [jsonH] jErr
|
||||||
|
where
|
||||||
|
jErr = encode . object $
|
||||||
|
[("error", String "Expecting a JSON object")]
|
||||||
|
|
||||||
data TableOptions = TableOptions {
|
data TableOptions = TableOptions {
|
||||||
tblOptcolumns :: [Column]
|
tblOptcolumns :: [Column]
|
||||||
@@ -150,19 +177,6 @@ instance ToJSON TableOptions where
|
|||||||
"columns" .= tblOptcolumns t
|
"columns" .= tblOptcolumns t
|
||||||
, "pkey" .= tblOptpkey t ]
|
, "pkey" .= tblOptpkey t ]
|
||||||
|
|
||||||
-- jsonBodyAction :: Request -> (SqlRow -> IO Response) -> IO Response
|
|
||||||
-- jsonBodyAction req handler = do
|
|
||||||
-- parse <- jsonBody req
|
|
||||||
-- case parse of
|
|
||||||
-- Left err -> return $ responseLBS status400 [jsonContentType] json
|
|
||||||
-- where json = JSON.encode . JSON.object $ [("error", JSON.String $ "Failed to parse JSON payload. " <> cs err) ]
|
|
||||||
-- Right body -> handler body
|
|
||||||
|
|
||||||
|
|
||||||
-- filterByKeys :: Ord a => Map a b -> [a] -> Map a b
|
|
||||||
-- filterByKeys m keys =
|
|
||||||
-- if null keys then m else
|
|
||||||
-- m `intersection` fromList (zip keys $ repeat undefined)
|
|
||||||
|
|
||||||
-- app :: Connection -> Application
|
-- app :: Connection -> Application
|
||||||
-- app conn req respond =
|
-- app conn req respond =
|
||||||
@@ -184,24 +198,6 @@ instance ToJSON TableOptions where
|
|||||||
-- , (hLocation, "/dbapi/users?id=eq." <> cs (userId u))
|
-- , (hLocation, "/dbapi/users?id=eq." <> cs (userId u))
|
||||||
-- ] ""
|
-- ] ""
|
||||||
|
|
||||||
-- ([table], "OPTIONS") ->
|
|
||||||
-- responseLBS status200 [jsonContentType, allOrigins] <$>
|
|
||||||
-- printColumns ver (cs table) conn
|
|
||||||
|
|
||||||
-- ([table], "GET") ->
|
|
||||||
-- if range == Just emptyRange
|
|
||||||
-- then return $ responseLBS status416 [] "HTTP Range error"
|
|
||||||
-- else do
|
|
||||||
-- r <- respondWithRangedResult <$> getRows ver (cs table) qq range conn
|
|
||||||
-- let canonical = urlEncodeVars $ sort $
|
|
||||||
-- map (join (***) cs) $
|
|
||||||
-- parseSimpleQuery $
|
|
||||||
-- rawQueryString req
|
|
||||||
-- return $ addHeaders [
|
|
||||||
-- ("Content-Location",
|
|
||||||
-- "/" <> cs table <> if null canonical then "" else "?" <> cs canonical
|
|
||||||
-- )] r
|
|
||||||
|
|
||||||
-- ([table], "POST") ->
|
-- ([table], "POST") ->
|
||||||
-- jsonBodyAction req (\row -> do
|
-- jsonBodyAction req (\row -> do
|
||||||
-- allvals <- insert ver table row conn
|
-- allvals <- insert ver table row conn
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module PgQuery where
|
|||||||
import RangeQuery
|
import RangeQuery
|
||||||
import Database.PostgreSQL.Simple
|
import Database.PostgreSQL.Simple
|
||||||
import Database.PostgreSQL.Simple.ToField
|
import Database.PostgreSQL.Simple.ToField
|
||||||
|
import Database.PostgreSQL.Simple.Types (Query(..))
|
||||||
import qualified Data.ByteString.Char8 as BS
|
import qualified Data.ByteString.Char8 as BS
|
||||||
import Data.ByteString.Search (split)
|
import Data.ByteString.Search (split)
|
||||||
import qualified Network.HTTP.Types.URI as Net
|
import qualified Network.HTTP.Types.URI as Net
|
||||||
@@ -12,6 +13,7 @@ import Data.Maybe (fromMaybe, mapMaybe)
|
|||||||
import Data.Functor ( (<$>) )
|
import Data.Functor ( (<$>) )
|
||||||
import Control.Monad (join)
|
import Control.Monad (join)
|
||||||
import Data.String.Conversions (cs)
|
import Data.String.Conversions (cs)
|
||||||
|
import Data.Aeson.Types (Value)
|
||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
|
|
||||||
type CompleteQuery = (Query, [Action])
|
type CompleteQuery = (Query, [Action])
|
||||||
@@ -75,6 +77,21 @@ selectStar t =
|
|||||||
("select count(1) from ?.?",
|
("select count(1) from ?.?",
|
||||||
[EscapeIdentifier (qtSchema t), EscapeIdentifier (qtName t)])
|
[EscapeIdentifier (qtSchema t), EscapeIdentifier (qtName t)])
|
||||||
|
|
||||||
|
insertInto :: QualifiedTable -> [BS.ByteString] -> [Value] ->
|
||||||
|
CompleteQuery
|
||||||
|
insertInto t [] _ =
|
||||||
|
("insert into ?.? default values returning *",
|
||||||
|
[EscapeIdentifier (qtSchema t), EscapeIdentifier (qtName t)])
|
||||||
|
insertInto t cols vals =
|
||||||
|
("insert into ?.? (" <>
|
||||||
|
Query (BS.intercalate ", " (map (const "?") cols)) <>
|
||||||
|
") values (" <>
|
||||||
|
Query (BS.intercalate ", " (map (const "?") vals)) <>
|
||||||
|
") returning *"
|
||||||
|
, [EscapeIdentifier (qtSchema t), EscapeIdentifier (qtName t)]
|
||||||
|
++ map EscapeIdentifier cols ++ map toField vals
|
||||||
|
)
|
||||||
|
|
||||||
wherePred :: Net.QueryItem -> CompleteQuery
|
wherePred :: Net.QueryItem -> CompleteQuery
|
||||||
wherePred (col, predicate) =
|
wherePred (col, predicate) =
|
||||||
(" ? ? ? ", [EscapeIdentifier col, Plain op, toField value])
|
(" ? ? ? ", [EscapeIdentifier col, Plain op, toField value])
|
||||||
|
|||||||
Reference in New Issue
Block a user