Applies range headers to /rpc calls using LIMIT/OFFSET.
This commit is contained in:
+14
-13
@@ -71,13 +71,10 @@ app dbStructure conf reqBody req =
|
|||||||
case readSqlParts of
|
case readSqlParts of
|
||||||
Left e -> return $ responseLBS status400 [jsonH] $ cs e
|
Left e -> return $ responseLBS status400 [jsonH] $ cs e
|
||||||
Right (q, cq) -> do
|
Right (q, cq) -> do
|
||||||
let range = restrictRange (configMaxRows conf) $ iRange apiRequest
|
let singular = iPreferSingular apiRequest
|
||||||
singular = iPreferSingular apiRequest
|
|
||||||
stm = createReadStatement q cq range singular
|
stm = createReadStatement q cq range singular
|
||||||
(iPreferCount apiRequest) (contentType == TextCSV)
|
(iPreferCount apiRequest) (contentType == TextCSV)
|
||||||
if range == emptyRange
|
respondToRange $ do
|
||||||
then return $ errResponse status416 "HTTP Range error"
|
|
||||||
else do
|
|
||||||
row <- H.query () stm
|
row <- H.query () stm
|
||||||
let (tableTotal, queryTotal, _ , body) = row
|
let (tableTotal, queryTotal, _ , body) = row
|
||||||
if singular
|
if singular
|
||||||
@@ -165,14 +162,14 @@ app dbStructure conf reqBody req =
|
|||||||
then do
|
then do
|
||||||
let p = V.head payload
|
let p = V.head payload
|
||||||
jwtSecret = configJwtSecret conf
|
jwtSecret = configJwtSecret conf
|
||||||
|
respondToRange $ do
|
||||||
bodyJson <- H.query () (callProc qi p)
|
bodyJson <- H.query () (callProc qi p range)
|
||||||
returnJWT <- H.query qi doesProcReturnJWT
|
returnJWT <- H.query qi doesProcReturnJWT
|
||||||
return $ responseLBS status200 [jsonH]
|
return $ responseLBS status200 [jsonH]
|
||||||
(let body = fromMaybe emptyArray bodyJson in
|
(let body = fromMaybe emptyArray bodyJson in
|
||||||
if returnJWT
|
if returnJWT
|
||||||
then "{\"token\":\"" <> cs (tokenJWT jwtSecret body) <> "\"}"
|
then "{\"token\":\"" <> cs (tokenJWT jwtSecret body) <> "\"}"
|
||||||
else cs $ encode body)
|
else cs $ encode body)
|
||||||
else return notFound
|
else return notFound
|
||||||
|
|
||||||
(ActionRead, TargetRoot, Nothing) -> do
|
(ActionRead, TargetRoot, Nothing) -> do
|
||||||
@@ -196,6 +193,7 @@ app dbStructure conf reqBody req =
|
|||||||
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
|
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
|
||||||
schema = cs $ configSchema conf
|
schema = cs $ configSchema conf
|
||||||
apiRequest = userApiRequest schema req reqBody
|
apiRequest = userApiRequest schema req reqBody
|
||||||
|
range = restrictRange (configMaxRows conf) $ iRange apiRequest
|
||||||
readDbRequest = DbRead <$> buildReadRequest (dbRelations dbStructure) apiRequest
|
readDbRequest = DbRead <$> buildReadRequest (dbRelations dbStructure) apiRequest
|
||||||
mutateDbRequest = DbMutate <$> buildMutateRequest apiRequest
|
mutateDbRequest = DbMutate <$> buildMutateRequest apiRequest
|
||||||
selectQuery = requestToQuery schema <$> readDbRequest
|
selectQuery = requestToQuery schema <$> readDbRequest
|
||||||
@@ -203,6 +201,9 @@ app dbStructure conf reqBody req =
|
|||||||
mutateQuery = requestToQuery schema <$> mutateDbRequest
|
mutateQuery = requestToQuery schema <$> mutateDbRequest
|
||||||
readSqlParts = (,) <$> selectQuery <*> countQuery
|
readSqlParts = (,) <$> selectQuery <*> countQuery
|
||||||
mutateSqlParts = (,) <$> selectQuery <*> mutateQuery
|
mutateSqlParts = (,) <$> selectQuery <*> mutateQuery
|
||||||
|
respondToRange response = if range == emptyRange
|
||||||
|
then return $ errResponse status416 "HTTP Range error"
|
||||||
|
else response
|
||||||
|
|
||||||
rangeStatus :: Integer -> Integer -> Maybe Integer -> Status
|
rangeStatus :: Integer -> Integer -> Maybe Integer -> Status
|
||||||
rangeStatus _ _ Nothing = status200
|
rangeStatus _ _ Nothing = status200
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import System.IO (BufferMode (..),
|
|||||||
hSetBuffering, stderr,
|
hSetBuffering, stderr,
|
||||||
stdin, stdout)
|
stdin, stdout)
|
||||||
import Web.JWT (secret)
|
import Web.JWT (secret)
|
||||||
|
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import System.Posix.Signals
|
import System.Posix.Signals
|
||||||
import Control.Concurrent (myThreadId)
|
import Control.Concurrent (myThreadId)
|
||||||
@@ -64,7 +63,6 @@ main = do
|
|||||||
Prelude.putStrLn $ "Listening on port " ++
|
Prelude.putStrLn $ "Listening on port " ++
|
||||||
(show $ configPort conf :: String)
|
(show $ configPort conf :: String)
|
||||||
|
|
||||||
|
|
||||||
pool <- P.acquire (configPool conf, 10, pgSettings)
|
pool <- P.acquire (configPool conf, 10, pgSettings)
|
||||||
|
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
|
|||||||
@@ -203,8 +203,8 @@ addJoinConditions schema (Node (query, (n, r)) forest) =
|
|||||||
updatedForest = mapM (addJoinConditions schema) forest
|
updatedForest = mapM (addJoinConditions schema) forest
|
||||||
addCond query' con = query'{flt_=con ++ flt_ query'}
|
addCond query' con = query'{flt_=con ++ flt_ query'}
|
||||||
|
|
||||||
callProc :: QualifiedIdentifier -> JSON.Object -> H.Query () (Maybe JSON.Value)
|
callProc :: QualifiedIdentifier -> JSON.Object -> NonnegRange -> H.Query () (Maybe JSON.Value)
|
||||||
callProc qi params =
|
callProc qi params range =
|
||||||
H.statement sql HE.unit decodeObj True
|
H.statement sql HE.unit decodeObj True
|
||||||
where
|
where
|
||||||
sql = [qc| SELECT array_to_json(
|
sql = [qc| SELECT array_to_json(
|
||||||
@@ -213,7 +213,7 @@ callProc qi params =
|
|||||||
from ({_callSql}) t |]
|
from ({_callSql}) t |]
|
||||||
_args = intercalate "," $ map _assignment (HM.toList params)
|
_args = intercalate "," $ map _assignment (HM.toList params)
|
||||||
_assignment (n,v) = pgFmtIdent n <> ":=" <> insertableValue v
|
_assignment (n,v) = pgFmtIdent n <> ":=" <> insertableValue v
|
||||||
_callSql = [qc| select * from {fromQi qi}({_args}) |] :: BS.ByteString
|
_callSql = [qc| select * from {fromQi qi}({_args}) {limitF range} |] :: BS.ByteString
|
||||||
decodeObj = HD.maybeRow (HD.value HD.json)
|
decodeObj = HD.maybeRow (HD.value HD.json)
|
||||||
|
|
||||||
operators :: [(Text, SqlFragment)]
|
operators :: [(Text, SqlFragment)]
|
||||||
|
|||||||
@@ -370,7 +370,10 @@ spec = do
|
|||||||
[json| [{"data": {"id": 1, "foo": {"bar": "baz"}}}] |]
|
[json| [{"data": {"id": 1, "foo": {"bar": "baz"}}}] |]
|
||||||
|
|
||||||
describe "remote procedure call" $ do
|
describe "remote procedure call" $ do
|
||||||
context "a proc that returns a set" $
|
context "a proc that returns a set" $ do
|
||||||
|
it "returns paginated results" $
|
||||||
|
request methodPost "/rpc/getitemrange" (rangeHdrs (ByteRangeFromTo 0 0)) [json| { "min": 2, "max": 4 } |] `shouldRespondWith`
|
||||||
|
[json| [ {"id": 3} ] |]
|
||||||
it "returns proper json" $
|
it "returns proper json" $
|
||||||
post "/rpc/getitemrange" [json| { "min": 2, "max": 4 } |] `shouldRespondWith`
|
post "/rpc/getitemrange" [json| { "min": 2, "max": 4 } |] `shouldRespondWith`
|
||||||
[json| [ {"id": 3}, {"id":4} ] |]
|
[json| [ {"id": 3}, {"id":4} ] |]
|
||||||
|
|||||||
Reference in New Issue
Block a user