Add POST handler

This commit is contained in:
Joe Nelson
2014-12-06 17:42:17 -08:00
parent b5f054d976
commit 8894400f5c
2 changed files with 51 additions and 38 deletions
+34 -38
View File
@@ -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
+17
View File
@@ -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])