suggested cleaup by @begriffs

This commit is contained in:
Ruslan Talpa
2016-05-31 14:55:31 +03:00
parent 7c83edc402
commit dc727f900d
8 changed files with 38 additions and 51 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Accept posts from HTML forms - @begriffs - Accept posts from HTML forms - @begriffs
- Ability to order embedded entities - @ruslantalpa - Ability to order embedded entities - @ruslantalpa
- Ability to paginate using &limit and &offset parameters - @ruslantalpa - Ability to paginate using &limit and &offset parameters - @ruslantalpa
- Ability to apply limits to embedded entities and enforce --max-rows on all levels - @ruslantalpa - Ability to apply limits to embedded entities and enforce --max-rows on all levels - @ruslantalpa, @begriffs
### Fixed ### Fixed
- Return 401 or 403 for access denied rather than 404 - @begriffs - Return 401 or 403 for access denied rather than 404 - @begriffs
+1 -1
View File
@@ -174,7 +174,7 @@ GET /people?order=age.desc.nullslast
To order the embedded items, you need to specify the tree path for the order param like so. To order the embedded items, you need to specify the tree path for the order param like so.
```HTTP ```HTTP
GET /projects?select=id,name,tasks{id,name}&order=id.ask&tasks.order=name.ask GET /projects?select=id,name,tasks{id,name}&order=id.asc&tasks.order=name.asc
``` ```
+11 -12
View File
@@ -15,17 +15,18 @@ import Data.Monoid ((<>))
import Data.Ord (comparing) import Data.Ord (comparing)
import Data.String.Conversions (cs) import Data.String.Conversions (cs)
import qualified Data.Text as T import qualified Data.Text as T
import Text.Read (readMaybe)
import qualified Data.Vector as V import qualified Data.Vector as V
import Network.HTTP.Base (urlEncodeVars) import Network.HTTP.Base (urlEncodeVars)
import Network.HTTP.Types.Header (hAuthorization) import Network.HTTP.Types.Header (hAuthorization)
import Network.HTTP.Types.URI (parseSimpleQuery) import Network.HTTP.Types.URI (parseSimpleQuery)
import Network.Wai (Request (..)) import Network.Wai (Request (..))
import Network.Wai.Parse (parseHttpAccept) import Network.Wai.Parse (parseHttpAccept)
import PostgREST.RangeQuery (NonnegRange, rangeRequested, limitToRange, toRange ) import PostgREST.RangeQuery (NonnegRange, rangeRequested, restrictRange, rangeGeq, allRange)
import PostgREST.Types (QualifiedIdentifier (..), import PostgREST.Types (QualifiedIdentifier (..),
Schema, Payload(..), Schema, Payload(..),
UniformObjects(..)) UniformObjects(..))
import Data.Ranged.Ranges (singletonRange) import Data.Ranged.Ranges (singletonRange, rangeIntersection)
type RequestBody = BL.ByteString type RequestBody = BL.ByteString
@@ -141,9 +142,8 @@ userApiRequest schema req reqBody =
ApiRequest { ApiRequest {
iAction = action iAction = action
, iTarget = target , iTarget = target
, iRange = setTopLevelRange headerRange $ , iRange = M.insert "limit" (rangeIntersection headerRange urlRange) $
setTopLevelRange urlRange $ M.fromList [ (cs k, restrictRange (readMaybe =<< v) allRange) | (k,v) <- qParams, isJust v, endingIn ["limit"] k ]
M.fromList [(cs k, limitToRange $ cs $ fromJust v) | (k,v) <- qParams, isJust v, endingIn ["limit"] k ]
, iAccepts = pickContentType $ lookupHeader "accept" , iAccepts = pickContentType $ lookupHeader "accept"
, iPayload = relevantPayload , iPayload = relevantPayload
, iPreferRepresentation = representation , iPreferRepresentation = representation
@@ -187,13 +187,12 @@ userApiRequest schema req reqBody =
endingIn xx key = lastWord `elem` xx endingIn xx key = lastWord `elem` xx
where lastWord = last $ T.split (=='.') key where lastWord = last $ T.split (=='.') key
headerRange = if singular then Just (singletonRange 0) else rangeRequested hdrs headerRange = if singular then singletonRange 0 else rangeRequested hdrs
urlRange = toRange (join $ lookup "limit" qParams) (join $ lookup "offset" qParams) urlOffsetRange = rangeGeq . fromMaybe (0::Integer) $
readMaybe =<< join (lookup "offset" qParams)
setTopLevelRange :: Maybe NonnegRange -> M.HashMap String NonnegRange -> M.HashMap String NonnegRange urlRange = restrictRange
setTopLevelRange Nothing ranges = ranges (readMaybe =<< join (lookup "limit" qParams))
setTopLevelRange (Just r) ranges = M.insert "limit" r ranges urlOffsetRange
-- PRIVATE --------------------------------------------------------------- -- PRIVATE ---------------------------------------------------------------
+3 -3
View File
@@ -47,7 +47,7 @@ import PostgREST.Config (AppConfig (..))
import PostgREST.DbStructure import PostgREST.DbStructure
import PostgREST.Error (errResponse, pgErrResponse) import PostgREST.Error (errResponse, pgErrResponse)
import PostgREST.Parsers import PostgREST.Parsers
import PostgREST.RangeQuery import PostgREST.RangeQuery (NonnegRange, allRange, rangeOffset, restrictRange)
import PostgREST.Middleware import PostgREST.Middleware
import PostgREST.QueryBuilder ( callProc import PostgREST.QueryBuilder ( callProc
, addJoinConditions , addJoinConditions
@@ -221,7 +221,7 @@ app dbStructure conf apiRequest =
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
schema = cs $ configSchema conf schema = cs $ configSchema conf
shouldCount = iPreferCount apiRequest shouldCount = iPreferCount apiRequest
topLevelRange = fromMaybe (rangeGeq 0) $ M.lookup "limit" $ iRange apiRequest topLevelRange = fromMaybe allRange $ M.lookup "limit" $ iRange apiRequest
readDbRequest = DbRead <$> buildReadRequest (configMaxRows conf) (dbRelations dbStructure) apiRequest readDbRequest = DbRead <$> buildReadRequest (configMaxRows conf) (dbRelations dbStructure) apiRequest
mutateDbRequest = DbMutate <$> buildMutateRequest apiRequest mutateDbRequest = DbMutate <$> buildMutateRequest apiRequest
selectQuery = requestToQuery schema <$> readDbRequest selectQuery = requestToQuery schema <$> readDbRequest
@@ -379,7 +379,7 @@ addOrder :: (Path, [OrderTerm]) -> ReadRequest -> ReadRequest
addOrder = addProperty addOrderToNode addOrder = addProperty addOrderToNode
addRangeToNode :: NonnegRange -> ReadRequest -> ReadRequest addRangeToNode :: NonnegRange -> ReadRequest -> ReadRequest
addRangeToNode r (Node (q,i) f) = Node (q{range_=Just r}, i) f addRangeToNode r (Node (q,i) f) = Node (q{range_=r}, i) f
addRange :: (Path, NonnegRange) -> ReadRequest -> ReadRequest addRange :: (Path, NonnegRange) -> ReadRequest -> ReadRequest
addRange = addProperty addRangeToNode addRange = addProperty addRangeToNode
+3 -4
View File
@@ -11,15 +11,14 @@ import Data.Tree
import PostgREST.QueryBuilder (operators) import PostgREST.QueryBuilder (operators)
import PostgREST.Types import PostgREST.Types
import Text.ParserCombinators.Parsec hiding (many, (<|>)) import Text.ParserCombinators.Parsec hiding (many, (<|>))
import PostgREST.RangeQuery (NonnegRange) import PostgREST.RangeQuery (NonnegRange,allRange)
pRequestSelect :: Text -> Parser ReadRequest pRequestSelect :: Text -> Parser ReadRequest
pRequestSelect rootNodeName = do pRequestSelect rootNodeName = do
fieldTree <- pFieldForest fieldTree <- pFieldForest
return $ foldr treeEntry (Node (readQuery, (rootNodeName, Nothing, Nothing)) []) fieldTree return $ foldr treeEntry (Node (readQuery, (rootNodeName, Nothing, Nothing)) []) fieldTree
where where
readQuery = Select [] [rootNodeName] [] Nothing Nothing readQuery = Select [] [rootNodeName] [] Nothing allRange
treeEntry :: Tree SelectItem -> ReadRequest -> ReadRequest treeEntry :: Tree SelectItem -> ReadRequest -> ReadRequest
treeEntry (Node fld@((fn, _),_,alias) fldForest) (Node (q, i) rForest) = treeEntry (Node fld@((fn, _),_,alias) fldForest) (Node (q, i) rForest) =
case fldForest of case fldForest of
@@ -27,7 +26,7 @@ pRequestSelect rootNodeName = do
_ -> Node (q, i) newForest _ -> Node (q, i) newForest
where where
newForest = newForest =
foldr treeEntry (Node (Select [] [fn] [] Nothing Nothing, (fn, Nothing, alias)) []) fldForest:rForest foldr treeEntry (Node (Select [] [fn] [] Nothing allRange, (fn, Nothing, alias)) []) fldForest:rForest
pRequestFilter :: (String, String) -> Either ParseError (Path, Filter) pRequestFilter :: (String, String) -> Either ParseError (Path, Filter)
pRequestFilter (k, v) = (,) <$> path <*> (Filter <$> fld <*> op <*> val) pRequestFilter (k, v) = (,) <$> path <*> (Filter <$> fld <*> op <*> val)
+5 -3
View File
@@ -35,7 +35,7 @@ import qualified Hasql.Decoders as HD
import qualified Data.Aeson as JSON import qualified Data.Aeson as JSON
import Data.Int (Int64) import Data.Int (Int64)
import PostgREST.RangeQuery (NonnegRange, rangeLimit, rangeOffset) import PostgREST.RangeQuery (NonnegRange, rangeLimit, rangeOffset, allRange)
import Control.Error (note, fromMaybe) import Control.Error (note, fromMaybe)
import Data.Functor.Contravariant (contramap) import Data.Functor.Contravariant (contramap)
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
@@ -289,7 +289,7 @@ requestToQuery schema (DbRead (Node (Select colSelects tbls conditions ord range
unwords joins, unwords joins,
("WHERE " <> intercalate " AND " ( map (pgFmtCondition qi ) conditions )) `emptyOnNull` conditions, ("WHERE " <> intercalate " AND " ( map (pgFmtCondition qi ) conditions )) `emptyOnNull` conditions,
orderF (fromMaybe [] ord), orderF (fromMaybe [] ord),
fromMaybe "" $ limitF <$> range limitF range
] ]
orderF ts = orderF ts =
if null ts if null ts
@@ -409,7 +409,9 @@ locationF pKeys =
) <> ")" ) <> ")"
limitF :: NonnegRange -> SqlFragment limitF :: NonnegRange -> SqlFragment
limitF r = "LIMIT " <> limit <> " OFFSET " <> offset limitF r = if r == allRange
then ""
else "LIMIT " <> limit <> " OFFSET " <> offset
where where
limit = maybe "ALL" (cs . show) $ rangeLimit r limit = maybe "ALL" (cs . show) $ rangeLimit r
offset = cs . show $ rangeOffset r offset = cs . show $ rangeOffset r
+13 -26
View File
@@ -5,15 +5,13 @@ module PostgREST.RangeQuery (
, rangeOffset , rangeOffset
, restrictRange , restrictRange
, rangeGeq , rangeGeq
, allRange
, NonnegRange , NonnegRange
, limitToRange
, toRange
) where ) where
import Control.Applicative import Control.Applicative
import Network.HTTP.Types.Header import Network.HTTP.Types.Header
import Data.Monoid ((<>))
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import Data.Ranged.Boundaries import Data.Ranged.Boundaries
@@ -37,19 +35,19 @@ rangeParse range = do
Just parsedRange -> Just parsedRange ->
let [_, from, to] = readMaybe . cs <$> parsedRange let [_, from, to] = readMaybe . cs <$> parsedRange
lower = fromMaybe emptyRange (rangeGeq <$> from) lower = fromMaybe emptyRange (rangeGeq <$> from)
upper = fromMaybe (rangeGeq 0) (rangeLeq <$> to) in upper = fromMaybe allRange (rangeLeq <$> to) in
rangeIntersection lower upper rangeIntersection lower upper
Nothing -> rangeGeq 0 Nothing -> allRange
rangeRequested :: RequestHeaders -> Maybe NonnegRange rangeRequested :: RequestHeaders -> NonnegRange
rangeRequested headers = rangeParse <$> lookup hRange headers rangeRequested headers = fromMaybe allRange $
rangeParse <$> lookup hRange headers
restrictRange :: Maybe Integer -> Maybe NonnegRange -> Maybe NonnegRange restrictRange :: Maybe Integer -> NonnegRange -> NonnegRange
restrictRange Nothing r = r restrictRange Nothing r = r
restrictRange (Just limit) Nothing = Just $ rangeIntersection (rangeGeq 0) (rangeLeq (limit - 1)) restrictRange (Just limit) r =
restrictRange (Just limit) (Just r) = Just $ rangeIntersection r $
rangeIntersection r $ Range BoundaryBelowAll (BoundaryAbove $ rangeOffset r + limit - 1)
Range BoundaryBelowAll (BoundaryAbove $ rangeOffset r + limit - 1)
rangeLimit :: NonnegRange -> Maybe Integer rangeLimit :: NonnegRange -> Maybe Integer
rangeLimit range = rangeLimit range =
@@ -67,20 +65,9 @@ rangeGeq :: Integer -> NonnegRange
rangeGeq n = rangeGeq n =
Range (BoundaryBelow n) BoundaryAboveAll Range (BoundaryBelow n) BoundaryAboveAll
allRange :: NonnegRange
allRange = rangeGeq 0
rangeLeq :: Integer -> NonnegRange rangeLeq :: Integer -> NonnegRange
rangeLeq n = rangeLeq n =
Range BoundaryBelowAll (BoundaryAbove n) Range BoundaryBelowAll (BoundaryAbove n)
limitToRange :: BS.ByteString -> NonnegRange
limitToRange l = rangeParse ("0-" <> cs (show (l' - 1)))
where l' = fromMaybe 0 (readMaybe $ cs l)::Integer
toRange :: Maybe String -> Maybe String -> Maybe NonnegRange
toRange Nothing Nothing = Nothing
toRange Nothing (Just o) = Just $ rangeParse $ cs $ show o' <> "-"
where o' = fromMaybe 0 (readMaybe $ cs o)::Integer
toRange (Just l) Nothing = Just $ limitToRange $ cs l
toRange (Just l) (Just o) = Just $ rangeParse $ cs $ show o' <> "-" <> show (o' + l' - 1)
where
l' = fromMaybe 0 (readMaybe $ cs l)::Integer
o' = fromMaybe 0 (readMaybe $ cs o)::Integer
+1 -1
View File
@@ -112,7 +112,7 @@ type Cast = Text
type NodeName = Text type NodeName = Text
type SelectItem = (Field, Maybe Cast, Maybe Alias) type SelectItem = (Field, Maybe Cast, Maybe Alias)
type Path = [Text] type Path = [Text]
data ReadQuery = Select { select::[SelectItem], from::[TableName], flt_::[Filter], order::Maybe [OrderTerm], range_::Maybe NonnegRange } deriving (Show, Eq) data ReadQuery = Select { select::[SelectItem], from::[TableName], flt_::[Filter], order::Maybe [OrderTerm], range_::NonnegRange } deriving (Show, Eq)
data MutateQuery = Insert { in_::TableName, qPayload::Payload } data MutateQuery = Insert { in_::TableName, qPayload::Payload }
| Delete { in_::TableName, where_::[Filter] } | Delete { in_::TableName, where_::[Filter] }
| Update { in_::TableName, qPayload::Payload, where_::[Filter] } deriving (Show, Eq) | Update { in_::TableName, qPayload::Payload, where_::[Filter] } deriving (Show, Eq)