refactor: remove unreacheable 404
This commit is contained in:
committed by
Steve Chavez
parent
f1f01f1f5c
commit
a5bb20bbf8
+90
-85
@@ -2,10 +2,8 @@
|
|||||||
Module : PostgREST.Request.ApiRequest
|
Module : PostgREST.Request.ApiRequest
|
||||||
Description : PostgREST functions to translate HTTP request to a domain type called ApiRequest.
|
Description : PostgREST functions to translate HTTP request to a domain type called ApiRequest.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE MultiWayIf #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
|
|
||||||
module PostgREST.ApiRequest
|
module PostgREST.ApiRequest
|
||||||
( ApiRequest(..)
|
( ApiRequest(..)
|
||||||
@@ -13,7 +11,9 @@ module PostgREST.ApiRequest
|
|||||||
, Mutation(..)
|
, Mutation(..)
|
||||||
, MediaType(..)
|
, MediaType(..)
|
||||||
, Action(..)
|
, Action(..)
|
||||||
, Target(..)
|
, ActionRelation(..)
|
||||||
|
, ActionRoutine(..)
|
||||||
|
, ActionSchema(..)
|
||||||
, Payload(..)
|
, Payload(..)
|
||||||
, userApiRequest
|
, userApiRequest
|
||||||
) where
|
) where
|
||||||
@@ -84,29 +84,31 @@ data Payload
|
|||||||
| RawJSON { payRaw :: LBS.ByteString }
|
| RawJSON { payRaw :: LBS.ByteString }
|
||||||
| RawPay { payRaw :: LBS.ByteString }
|
| RawPay { payRaw :: LBS.ByteString }
|
||||||
|
|
||||||
data InvokeMethod = InvHead | InvGet | InvPost deriving Eq
|
data InvokeMethod = Inv | InvRead Bool deriving Eq
|
||||||
data Mutation = MutationCreate | MutationDelete | MutationSingleUpsert | MutationUpdate deriving Eq
|
data Mutation = MutationCreate | MutationDelete | MutationSingleUpsert | MutationUpdate deriving Eq
|
||||||
|
|
||||||
-- | Types of things a user wants to do to tables/views/procs
|
data Resource
|
||||||
|
= ResourceRelation Text
|
||||||
|
| ResourceRoutine Text
|
||||||
|
| ResourceSchema
|
||||||
|
|
||||||
|
data ActionRelation
|
||||||
|
= ActRead Bool
|
||||||
|
| ActMutate Mutation
|
||||||
|
| ActRelInfo
|
||||||
|
|
||||||
|
data ActionRoutine
|
||||||
|
= ActInvoke InvokeMethod
|
||||||
|
| ActRoutInfo
|
||||||
|
|
||||||
|
data ActionSchema
|
||||||
|
= ActSchemaRead Bool
|
||||||
|
| ActSchemaInfo
|
||||||
|
|
||||||
data Action
|
data Action
|
||||||
= ActionMutate Mutation
|
= ActRelation QualifiedIdentifier ActionRelation
|
||||||
| ActionRead {isHead :: Bool}
|
| ActRoutine QualifiedIdentifier ActionRoutine
|
||||||
| ActionInvoke InvokeMethod
|
| ActSchema Schema ActionSchema
|
||||||
| ActionInfo
|
|
||||||
| ActionInspect {isHead :: Bool}
|
|
||||||
deriving Eq
|
|
||||||
-- | The path info that will be mapped to a target (used to handle validations and errors before defining the Target)
|
|
||||||
data PathInfo
|
|
||||||
= PathInfo
|
|
||||||
{ pathName :: Text
|
|
||||||
, pathIsProc :: Bool
|
|
||||||
, pathIsDefSpec :: Bool
|
|
||||||
, pathIsRootSpec :: Bool
|
|
||||||
}
|
|
||||||
-- | The target db object of a user action
|
|
||||||
data Target = TargetIdent QualifiedIdentifier
|
|
||||||
| TargetProc{tProc :: QualifiedIdentifier, tpIsRootSpec :: Bool}
|
|
||||||
| TargetDefaultSpec{tdsSchema :: Schema} -- The default spec offered at root "/"
|
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
Describes what the user wants to do. This data type is a
|
Describes what the user wants to do. This data type is a
|
||||||
@@ -116,10 +118,9 @@ data Target = TargetIdent QualifiedIdentifier
|
|||||||
if it is an action we are able to perform.
|
if it is an action we are able to perform.
|
||||||
-}
|
-}
|
||||||
data ApiRequest = ApiRequest {
|
data ApiRequest = ApiRequest {
|
||||||
iAction :: Action -- ^ Similar but not identical to HTTP method, e.g. Create/Invoke both POST
|
iAction :: Action -- ^ Action on the resource
|
||||||
, iRange :: HM.HashMap Text NonnegRange -- ^ Requested range of rows within response
|
, iRange :: HM.HashMap Text NonnegRange -- ^ Requested range of rows within response
|
||||||
, iTopLevelRange :: NonnegRange -- ^ Requested range of rows from the top level
|
, iTopLevelRange :: NonnegRange -- ^ Requested range of rows from the top level
|
||||||
, iTarget :: Target -- ^ The target, be it calling a proc or accessing a table
|
|
||||||
, iPayload :: Maybe Payload -- ^ Data sent by client and used for mutation actions
|
, iPayload :: Maybe Payload -- ^ Data sent by client and used for mutation actions
|
||||||
, iPreferences :: Preferences.Preferences -- ^ Prefer header values
|
, iPreferences :: Preferences.Preferences -- ^ Prefer header values
|
||||||
, iQueryParams :: QueryParams.QueryParams
|
, iQueryParams :: QueryParams.QueryParams
|
||||||
@@ -137,17 +138,14 @@ data ApiRequest = ApiRequest {
|
|||||||
-- | Examines HTTP request and translates it into user intent.
|
-- | Examines HTTP request and translates it into user intent.
|
||||||
userApiRequest :: AppConfig -> Request -> RequestBody -> SchemaCache -> Either ApiRequestError ApiRequest
|
userApiRequest :: AppConfig -> Request -> RequestBody -> SchemaCache -> Either ApiRequestError ApiRequest
|
||||||
userApiRequest conf req reqBody sCache = do
|
userApiRequest conf req reqBody sCache = do
|
||||||
pInfo@PathInfo{..} <- getPathInfo conf $ pathInfo req
|
resource <- getResource conf $ pathInfo req
|
||||||
act <- getAction pInfo method
|
|
||||||
qPrms <- first QueryParamError $ QueryParams.parse (pathIsProc && act `elem` [ActionInvoke InvGet, ActionInvoke InvHead]) $ rawQueryString req
|
|
||||||
(schema, negotiatedByProfile) <- getSchema conf hdrs method
|
(schema, negotiatedByProfile) <- getSchema conf hdrs method
|
||||||
|
act <- getAction resource schema method
|
||||||
|
qPrms <- first QueryParamError $ QueryParams.parse (actIsInvokeSafe act) $ rawQueryString req
|
||||||
(topLevelRange, ranges) <- getRanges method qPrms hdrs
|
(topLevelRange, ranges) <- getRanges method qPrms hdrs
|
||||||
(payload, columns) <- getPayload reqBody contentMediaType qPrms act pInfo
|
(payload, columns) <- getPayload reqBody contentMediaType qPrms act
|
||||||
return $ ApiRequest {
|
return $ ApiRequest {
|
||||||
iAction = act
|
iAction = act
|
||||||
, iTarget = if | pathIsProc -> TargetProc (QualifiedIdentifier schema pathName) pathIsRootSpec
|
|
||||||
| pathIsDefSpec -> TargetDefaultSpec schema
|
|
||||||
| otherwise -> TargetIdent $ QualifiedIdentifier schema pathName
|
|
||||||
, iRange = ranges
|
, iRange = ranges
|
||||||
, iTopLevelRange = topLevelRange
|
, iTopLevelRange = topLevelRange
|
||||||
, iPayload = payload
|
, iPayload = payload
|
||||||
@@ -170,38 +168,43 @@ userApiRequest conf req reqBody sCache = do
|
|||||||
iHdrs = [ (CI.foldedCase k, v) | (k,v) <- hdrs, k /= hCookie]
|
iHdrs = [ (CI.foldedCase k, v) | (k,v) <- hdrs, k /= hCookie]
|
||||||
iCkies = maybe [] parseCookies $ lookupHeader "Cookie"
|
iCkies = maybe [] parseCookies $ lookupHeader "Cookie"
|
||||||
contentMediaType = maybe MTApplicationJSON MediaType.decodeMediaType $ lookupHeader "content-type"
|
contentMediaType = maybe MTApplicationJSON MediaType.decodeMediaType $ lookupHeader "content-type"
|
||||||
|
actIsInvokeSafe x = case x of {ActRoutine _ (ActInvoke (InvRead _)) -> True; _ -> False}
|
||||||
|
|
||||||
getPathInfo :: AppConfig -> [Text] -> Either ApiRequestError PathInfo
|
getResource :: AppConfig -> [Text] -> Either ApiRequestError Resource
|
||||||
getPathInfo AppConfig{configOpenApiMode, configDbRootSpec} path =
|
getResource AppConfig{configOpenApiMode, configDbRootSpec} = \case
|
||||||
case path of
|
[] -> case configDbRootSpec of
|
||||||
[] -> case configDbRootSpec of
|
Just (QualifiedIdentifier _ pathName) -> Right $ ResourceRoutine pathName
|
||||||
Just (QualifiedIdentifier _ pathName) -> Right $ PathInfo pathName True False True
|
Nothing | configOpenApiMode == OADisabled -> Left NotFound
|
||||||
Nothing | configOpenApiMode == OADisabled -> Left NotFound
|
| otherwise -> Right ResourceSchema
|
||||||
| otherwise -> Right $ PathInfo mempty False True False
|
[table] -> Right $ ResourceRelation table
|
||||||
[table] -> Right $ PathInfo table False False False
|
["rpc", pName] -> Right $ ResourceRoutine pName
|
||||||
["rpc", pName] -> Right $ PathInfo pName True False False
|
_ -> Left NotFound
|
||||||
_ -> Left NotFound
|
|
||||||
|
getAction :: Resource -> Schema -> ByteString -> Either ApiRequestError Action
|
||||||
|
getAction resource schema method =
|
||||||
|
case (resource, method) of
|
||||||
|
(ResourceRoutine rout, "HEAD") -> Right $ ActRoutine (qi rout) $ ActInvoke $ InvRead True
|
||||||
|
(ResourceRoutine rout, "GET") -> Right $ ActRoutine (qi rout) $ ActInvoke $ InvRead False
|
||||||
|
(ResourceRoutine rout, "POST") -> Right $ ActRoutine (qi rout) $ ActInvoke Inv
|
||||||
|
(ResourceRoutine rout, "OPTIONS") -> Right $ ActRoutine (qi rout) ActRoutInfo
|
||||||
|
(ResourceRoutine _, _) -> Left $ InvalidRpcMethod method
|
||||||
|
|
||||||
|
(ResourceRelation rel, "HEAD") -> Right $ ActRelation (qi rel) $ ActRead True
|
||||||
|
(ResourceRelation rel, "GET") -> Right $ ActRelation (qi rel) $ ActRead False
|
||||||
|
(ResourceRelation rel, "POST") -> Right $ ActRelation (qi rel) $ ActMutate MutationCreate
|
||||||
|
(ResourceRelation rel, "PUT") -> Right $ ActRelation (qi rel) $ ActMutate MutationSingleUpsert
|
||||||
|
(ResourceRelation rel, "PATCH") -> Right $ ActRelation (qi rel) $ ActMutate MutationUpdate
|
||||||
|
(ResourceRelation rel, "DELETE") -> Right $ ActRelation (qi rel) $ ActMutate MutationDelete
|
||||||
|
(ResourceRelation rel, "OPTIONS") -> Right $ ActRelation (qi rel) ActRelInfo
|
||||||
|
|
||||||
|
(ResourceSchema, "HEAD") -> Right $ ActSchema schema $ ActSchemaRead True
|
||||||
|
(ResourceSchema, "GET") -> Right $ ActSchema schema $ ActSchemaRead False
|
||||||
|
(ResourceSchema, "OPTIONS") -> Right $ ActSchema schema ActSchemaInfo
|
||||||
|
|
||||||
|
_ -> Left $ UnsupportedMethod method
|
||||||
|
where
|
||||||
|
qi = QualifiedIdentifier schema
|
||||||
|
|
||||||
getAction :: PathInfo -> ByteString -> Either ApiRequestError Action
|
|
||||||
getAction PathInfo{pathIsProc, pathIsDefSpec} method =
|
|
||||||
if pathIsProc && method `notElem` ["HEAD", "GET", "POST", "OPTIONS"]
|
|
||||||
then Left $ InvalidRpcMethod method
|
|
||||||
else case method of
|
|
||||||
-- The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response
|
|
||||||
-- From https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
|
|
||||||
"HEAD" | pathIsDefSpec -> Right $ ActionInspect{isHead=True}
|
|
||||||
| pathIsProc -> Right $ ActionInvoke InvHead
|
|
||||||
| otherwise -> Right $ ActionRead{isHead=True}
|
|
||||||
"GET" | pathIsDefSpec -> Right $ ActionInspect{isHead=False}
|
|
||||||
| pathIsProc -> Right $ ActionInvoke InvGet
|
|
||||||
| otherwise -> Right $ ActionRead{isHead=False}
|
|
||||||
"POST" | pathIsProc -> Right $ ActionInvoke InvPost
|
|
||||||
| otherwise -> Right $ ActionMutate MutationCreate
|
|
||||||
"PATCH" -> Right $ ActionMutate MutationUpdate
|
|
||||||
"PUT" -> Right $ ActionMutate MutationSingleUpsert
|
|
||||||
"DELETE" -> Right $ ActionMutate MutationDelete
|
|
||||||
"OPTIONS" -> Right ActionInfo
|
|
||||||
_ -> Left $ UnsupportedMethod method
|
|
||||||
|
|
||||||
getSchema :: AppConfig -> RequestHeaders -> ByteString -> Either ApiRequestError (Schema, Bool)
|
getSchema :: AppConfig -> RequestHeaders -> ByteString -> Either ApiRequestError (Schema, Bool)
|
||||||
getSchema AppConfig{configDbSchemas} hdrs method = do
|
getSchema AppConfig{configDbSchemas} hdrs method = do
|
||||||
@@ -241,8 +244,8 @@ getRanges method QueryParams{qsOrder,qsRanges} hdrs
|
|||||||
isInvalidRange = topLevelRange == emptyRange && not (hasLimitZero limitRange)
|
isInvalidRange = topLevelRange == emptyRange && not (hasLimitZero limitRange)
|
||||||
topLevelRange = fromMaybe allRange $ HM.lookup "limit" ranges -- if no limit is specified, get all the request rows
|
topLevelRange = fromMaybe allRange $ HM.lookup "limit" ranges -- if no limit is specified, get all the request rows
|
||||||
|
|
||||||
getPayload :: RequestBody -> MediaType -> QueryParams.QueryParams -> Action -> PathInfo -> Either ApiRequestError (Maybe Payload, S.Set FieldName)
|
getPayload :: RequestBody -> MediaType -> QueryParams.QueryParams -> Action -> Either ApiRequestError (Maybe Payload, S.Set FieldName)
|
||||||
getPayload reqBody contentMediaType QueryParams{qsColumns} action PathInfo{pathIsProc}= do
|
getPayload reqBody contentMediaType QueryParams{qsColumns} action = do
|
||||||
checkedPayload <- if shouldParsePayload then payload else Right Nothing
|
checkedPayload <- if shouldParsePayload then payload else Right Nothing
|
||||||
let cols = case (checkedPayload, columns) of
|
let cols = case (checkedPayload, columns) of
|
||||||
(Just ProcessedJSON{payKeys}, _) -> payKeys
|
(Just ProcessedJSON{payKeys}, _) -> payKeys
|
||||||
@@ -252,12 +255,12 @@ getPayload reqBody contentMediaType QueryParams{qsColumns} action PathInfo{pathI
|
|||||||
return (checkedPayload, cols)
|
return (checkedPayload, cols)
|
||||||
where
|
where
|
||||||
payload :: Either ApiRequestError (Maybe Payload)
|
payload :: Either ApiRequestError (Maybe Payload)
|
||||||
payload = mapBoth InvalidBody Just $ case (contentMediaType, pathIsProc) of
|
payload = mapBoth InvalidBody Just $ case (contentMediaType, isProc) of
|
||||||
(MTApplicationJSON, _) ->
|
(MTApplicationJSON, _) ->
|
||||||
if isJust columns
|
if isJust columns
|
||||||
then Right $ RawJSON reqBody
|
then Right $ RawJSON reqBody
|
||||||
else note "All object keys must match" . payloadAttributes reqBody
|
else note "All object keys must match" . payloadAttributes reqBody
|
||||||
=<< if LBS.null reqBody && pathIsProc
|
=<< if LBS.null reqBody && isProc
|
||||||
then Right emptyObject
|
then Right emptyObject
|
||||||
else first BS.pack $
|
else first BS.pack $
|
||||||
-- Drop parsing error message in favor of generic one (https://github.com/PostgREST/postgrest/issues/2344)
|
-- Drop parsing error message in favor of generic one (https://github.com/PostgREST/postgrest/issues/2344)
|
||||||
@@ -265,30 +268,32 @@ getPayload reqBody contentMediaType QueryParams{qsColumns} action PathInfo{pathI
|
|||||||
(MTTextCSV, _) -> do
|
(MTTextCSV, _) -> do
|
||||||
json <- csvToJson <$> first BS.pack (CSV.decodeByName reqBody)
|
json <- csvToJson <$> first BS.pack (CSV.decodeByName reqBody)
|
||||||
note "All lines must have same number of fields" $ payloadAttributes (JSON.encode json) json
|
note "All lines must have same number of fields" $ payloadAttributes (JSON.encode json) json
|
||||||
(MTUrlEncoded, isProc) -> do
|
(MTUrlEncoded, True) ->
|
||||||
let params = (T.decodeUtf8 *** T.decodeUtf8) <$> parseSimpleQuery (LBS.toStrict reqBody)
|
Right $ ProcessedUrlEncoded params (S.fromList $ fst <$> params)
|
||||||
if isProc
|
(MTUrlEncoded, False) ->
|
||||||
then Right $ ProcessedUrlEncoded params (S.fromList $ fst <$> params)
|
let paramsMap = HM.fromList $ (identity *** JSON.String) <$> params in
|
||||||
else
|
Right $ ProcessedJSON (JSON.encode paramsMap) $ S.fromList (HM.keys paramsMap)
|
||||||
let paramsMap = HM.fromList $ (identity *** JSON.String) <$> params in
|
|
||||||
Right $ ProcessedJSON (JSON.encode paramsMap) $ S.fromList (HM.keys paramsMap)
|
|
||||||
(MTTextPlain, True) -> Right $ RawPay reqBody
|
(MTTextPlain, True) -> Right $ RawPay reqBody
|
||||||
(MTTextXML, True) -> Right $ RawPay reqBody
|
(MTTextXML, True) -> Right $ RawPay reqBody
|
||||||
(MTOctetStream, True) -> Right $ RawPay reqBody
|
(MTOctetStream, True) -> Right $ RawPay reqBody
|
||||||
(ct, _) -> Left $ "Content-Type not acceptable: " <> MediaType.toMime ct
|
(ct, _) -> Left $ "Content-Type not acceptable: " <> MediaType.toMime ct
|
||||||
|
|
||||||
shouldParsePayload = case (action, contentMediaType) of
|
shouldParsePayload = case action of
|
||||||
(ActionMutate MutationCreate, _) -> True
|
ActRelation _ (ActMutate MutationDelete) -> False
|
||||||
(ActionInvoke InvPost, _) -> True
|
ActRelation _ (ActMutate _) -> True
|
||||||
(ActionMutate MutationSingleUpsert, _) -> True
|
ActRoutine _ (ActInvoke Inv) -> True
|
||||||
(ActionMutate MutationUpdate, _) -> True
|
_ -> False
|
||||||
_ -> False
|
|
||||||
|
|
||||||
columns = case action of
|
columns = case action of
|
||||||
ActionMutate MutationCreate -> qsColumns
|
ActRelation _ (ActMutate MutationCreate) -> qsColumns
|
||||||
ActionMutate MutationUpdate -> qsColumns
|
ActRelation _ (ActMutate MutationUpdate) -> qsColumns
|
||||||
ActionInvoke InvPost -> qsColumns
|
ActRoutine _ (ActInvoke Inv) -> qsColumns
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
|
isProc = case action of
|
||||||
|
ActRoutine _ _ -> True
|
||||||
|
_ -> False
|
||||||
|
params = (T.decodeUtf8 *** T.decodeUtf8) <$> parseSimpleQuery (LBS.toStrict reqBody)
|
||||||
|
|
||||||
type CsvData = V.Vector (M.Map Text LBS.ByteString)
|
type CsvData = V.Vector (M.Map Text LBS.ByteString)
|
||||||
|
|
||||||
|
|||||||
@@ -112,12 +112,12 @@ data QueryParams =
|
|||||||
-- >>> qsFilters <$> parse False "a.b=noop.0"
|
-- >>> qsFilters <$> parse False "a.b=noop.0"
|
||||||
-- Left (QPError "\"failed to parse filter (noop.0)\" (line 1, column 1)" "unexpected \"o\" expecting \"not\" or operator (eq, gt, ...)")
|
-- Left (QPError "\"failed to parse filter (noop.0)\" (line 1, column 1)" "unexpected \"o\" expecting \"not\" or operator (eq, gt, ...)")
|
||||||
parse :: Bool -> ByteString -> Either QPError QueryParams
|
parse :: Bool -> ByteString -> Either QPError QueryParams
|
||||||
parse isRpcGet qs = do
|
parse isRpcRead qs = do
|
||||||
rOrd <- pRequestOrder `traverse` order
|
rOrd <- pRequestOrder `traverse` order
|
||||||
rLogic <- pRequestLogicTree `traverse` logic
|
rLogic <- pRequestLogicTree `traverse` logic
|
||||||
rCols <- pRequestColumns columns
|
rCols <- pRequestColumns columns
|
||||||
rSel <- pRequestSelect select
|
rSel <- pRequestSelect select
|
||||||
(rFlts, params) <- L.partition hasOp <$> pRequestFilter isRpcGet `traverse` filters
|
(rFlts, params) <- L.partition hasOp <$> pRequestFilter isRpcRead `traverse` filters
|
||||||
(rFltsRoot, rFltsNotRoot) <- pure $ L.partition hasRootFilter rFlts
|
(rFltsRoot, rFltsNotRoot) <- pure $ L.partition hasRootFilter rFlts
|
||||||
rOnConflict <- pRequestOnConflict `traverse` onConflict
|
rOnConflict <- pRequestOnConflict `traverse` onConflict
|
||||||
|
|
||||||
@@ -226,11 +226,11 @@ pRequestOnConflict oncStr =
|
|||||||
-- >>> pRequestFilter True ("id", "val")
|
-- >>> pRequestFilter True ("id", "val")
|
||||||
-- Right ([],Filter {field = ("id",[]), opExpr = NoOpExpr "val"})
|
-- Right ([],Filter {field = ("id",[]), opExpr = NoOpExpr "val"})
|
||||||
pRequestFilter :: Bool -> (Text, Text) -> Either QPError (EmbedPath, Filter)
|
pRequestFilter :: Bool -> (Text, Text) -> Either QPError (EmbedPath, Filter)
|
||||||
pRequestFilter isRpcGet (k, v) = mapError $ (,) <$> path <*> (Filter <$> fld <*> oper)
|
pRequestFilter isRpcRead (k, v) = mapError $ (,) <$> path <*> (Filter <$> fld <*> oper)
|
||||||
where
|
where
|
||||||
treePath = P.parse pTreePath ("failed to parse tree path (" ++ toS k ++ ")") $ toS k
|
treePath = P.parse pTreePath ("failed to parse tree path (" ++ toS k ++ ")") $ toS k
|
||||||
oper = P.parse parseFlt ("failed to parse filter (" ++ toS v ++ ")") $ toS v
|
oper = P.parse parseFlt ("failed to parse filter (" ++ toS v ++ ")") $ toS v
|
||||||
parseFlt = if isRpcGet
|
parseFlt = if isRpcRead
|
||||||
then pOpExpr pSingleVal <|> pure (NoOpExpr v)
|
then pOpExpr pSingleVal <|> pure (NoOpExpr v)
|
||||||
else pOpExpr pSingleVal
|
else pOpExpr pSingleVal
|
||||||
path = fst <$> treePath
|
path = fst <$> treePath
|
||||||
|
|||||||
+28
-30
@@ -30,21 +30,23 @@ import qualified Hasql.Transaction.Sessions as SQL
|
|||||||
import qualified Network.Wai as Wai
|
import qualified Network.Wai as Wai
|
||||||
import qualified Network.Wai.Handler.Warp as Warp
|
import qualified Network.Wai.Handler.Warp as Warp
|
||||||
|
|
||||||
import qualified PostgREST.Admin as Admin
|
import qualified PostgREST.Admin as Admin
|
||||||
import qualified PostgREST.ApiRequest as ApiRequest
|
import qualified PostgREST.ApiRequest as ApiRequest
|
||||||
import qualified PostgREST.ApiRequest.Types as ApiRequestTypes
|
import qualified PostgREST.AppState as AppState
|
||||||
import qualified PostgREST.AppState as AppState
|
import qualified PostgREST.Auth as Auth
|
||||||
import qualified PostgREST.Auth as Auth
|
import qualified PostgREST.Cors as Cors
|
||||||
import qualified PostgREST.Cors as Cors
|
import qualified PostgREST.Error as Error
|
||||||
import qualified PostgREST.Error as Error
|
import qualified PostgREST.Logger as Logger
|
||||||
import qualified PostgREST.Logger as Logger
|
import qualified PostgREST.Plan as Plan
|
||||||
import qualified PostgREST.Plan as Plan
|
import qualified PostgREST.Query as Query
|
||||||
import qualified PostgREST.Query as Query
|
import qualified PostgREST.Response as Response
|
||||||
import qualified PostgREST.Response as Response
|
import qualified PostgREST.Unix as Unix (installSignalHandlers)
|
||||||
import qualified PostgREST.Unix as Unix (installSignalHandlers)
|
|
||||||
|
|
||||||
import PostgREST.ApiRequest (Action (..), ApiRequest (..),
|
import PostgREST.ApiRequest (Action (..),
|
||||||
Mutation (..), Target (..))
|
ActionRelation (..),
|
||||||
|
ActionRoutine (..),
|
||||||
|
ActionSchema (..),
|
||||||
|
ApiRequest (..), Mutation (..))
|
||||||
import PostgREST.AppState (AppState)
|
import PostgREST.AppState (AppState)
|
||||||
import PostgREST.Auth (AuthResult (..))
|
import PostgREST.Auth (AuthResult (..))
|
||||||
import PostgREST.Config (AppConfig (..))
|
import PostgREST.Config (AppConfig (..))
|
||||||
@@ -170,66 +172,62 @@ runDbHandler appState config isoLvl mode authenticated prepared observer handler
|
|||||||
handleRequest :: AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> PgVersion -> ApiRequest -> SchemaCache ->
|
handleRequest :: AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> PgVersion -> ApiRequest -> SchemaCache ->
|
||||||
Maybe Double -> Maybe Double -> (Observation -> IO ()) -> Handler IO Wai.Response
|
Maybe Double -> Maybe Double -> (Observation -> IO ()) -> Handler IO Wai.Response
|
||||||
handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@ApiRequest{..} sCache jwtTime parseTime observer =
|
handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@ApiRequest{..} sCache jwtTime parseTime observer =
|
||||||
case (iAction, iTarget) of
|
case iAction of
|
||||||
(ActionRead headersOnly, TargetIdent identifier) -> do
|
ActRelation identifier (ActRead headersOnly) -> do
|
||||||
(planTime', wrPlan) <- withTiming $ liftEither $ Plan.wrappedReadPlan identifier conf sCache apiReq
|
(planTime', wrPlan) <- withTiming $ liftEither $ Plan.wrappedReadPlan identifier conf sCache apiReq
|
||||||
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.wrTxMode wrPlan) $ Query.readQuery wrPlan conf apiReq
|
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.wrTxMode wrPlan) $ Query.readQuery wrPlan conf apiReq
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.readResponse wrPlan headersOnly identifier apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.readResponse wrPlan headersOnly identifier apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionMutate MutationCreate, TargetIdent identifier) -> do
|
ActRelation identifier (ActMutate MutationCreate) -> do
|
||||||
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationCreate apiReq identifier conf sCache
|
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationCreate apiReq identifier conf sCache
|
||||||
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.createQuery mrPlan apiReq conf
|
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.createQuery mrPlan apiReq conf
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.createResponse identifier mrPlan apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.createResponse identifier mrPlan apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionMutate MutationUpdate, TargetIdent identifier) -> do
|
ActRelation identifier (ActMutate MutationUpdate) -> do
|
||||||
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationUpdate apiReq identifier conf sCache
|
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationUpdate apiReq identifier conf sCache
|
||||||
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.updateQuery mrPlan apiReq conf
|
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.updateQuery mrPlan apiReq conf
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.updateResponse mrPlan apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.updateResponse mrPlan apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionMutate MutationSingleUpsert, TargetIdent identifier) -> do
|
ActRelation identifier (ActMutate MutationSingleUpsert) -> do
|
||||||
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationSingleUpsert apiReq identifier conf sCache
|
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationSingleUpsert apiReq identifier conf sCache
|
||||||
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.singleUpsertQuery mrPlan apiReq conf
|
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.singleUpsertQuery mrPlan apiReq conf
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.singleUpsertResponse mrPlan apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.singleUpsertResponse mrPlan apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionMutate MutationDelete, TargetIdent identifier) -> do
|
ActRelation identifier (ActMutate MutationDelete) -> do
|
||||||
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationDelete apiReq identifier conf sCache
|
(planTime', mrPlan) <- withTiming $ liftEither $ Plan.mutateReadPlan MutationDelete apiReq identifier conf sCache
|
||||||
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.deleteQuery mrPlan apiReq conf
|
(txTime', resultSet) <- withTiming $ runQuery roleIsoLvl mempty (Plan.mrTxMode mrPlan) $ Query.deleteQuery mrPlan apiReq conf
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.deleteResponse mrPlan apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.deleteResponse mrPlan apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionInvoke invMethod, TargetProc identifier _) -> do
|
ActRoutine identifier (ActInvoke invMethod) -> do
|
||||||
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq invMethod
|
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq invMethod
|
||||||
(txTime', resultSet) <- withTiming $ runQuery (fromMaybe roleIsoLvl $ pdIsoLvl (Plan.crProc cPlan)) (pdFuncSettings $ Plan.crProc cPlan) (Plan.crTxMode cPlan) $ Query.invokeQuery (Plan.crProc cPlan) cPlan apiReq conf pgVer
|
(txTime', resultSet) <- withTiming $ runQuery (fromMaybe roleIsoLvl $ pdIsoLvl (Plan.crProc cPlan)) (pdFuncSettings $ Plan.crProc cPlan) (Plan.crTxMode cPlan) $ Query.invokeQuery (Plan.crProc cPlan) cPlan apiReq conf pgVer
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.invokeResponse cPlan invMethod (Plan.crProc cPlan) apiReq resultSet
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.invokeResponse cPlan invMethod (Plan.crProc cPlan) apiReq resultSet
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionInspect headersOnly, TargetDefaultSpec tSchema) -> do
|
ActSchema tSchema (ActSchemaRead headersOnly) -> do
|
||||||
(planTime', iPlan) <- withTiming $ liftEither $ Plan.inspectPlan apiReq
|
(planTime', iPlan) <- withTiming $ liftEither $ Plan.inspectPlan apiReq
|
||||||
(txTime', oaiResult) <- withTiming $ runQuery roleIsoLvl mempty (Plan.ipTxmode iPlan) $ Query.openApiQuery sCache pgVer conf tSchema
|
(txTime', oaiResult) <- withTiming $ runQuery roleIsoLvl mempty (Plan.ipTxmode iPlan) $ Query.openApiQuery sCache pgVer conf tSchema
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.openApiResponse (T.decodeUtf8 prettyVersion, docsVersion) headersOnly oaiResult conf sCache iSchema iNegotiatedByProfile
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.openApiResponse (T.decodeUtf8 prettyVersion, docsVersion) headersOnly oaiResult conf sCache iSchema iNegotiatedByProfile
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' txTime' respTime') pgrst
|
||||||
|
|
||||||
(ActionInfo, TargetIdent identifier) -> do
|
ActRelation identifier ActRelInfo -> do
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoIdentResponse identifier sCache
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoIdentResponse identifier sCache
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst
|
||||||
|
|
||||||
(ActionInfo, TargetProc identifier _) -> do
|
ActRoutine identifier ActRoutInfo -> do
|
||||||
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq ApiRequest.InvHead
|
(planTime', cPlan) <- withTiming $ liftEither $ Plan.callReadPlan identifier conf sCache apiReq $ ApiRequest.InvRead True
|
||||||
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoProcResponse (Plan.crProc cPlan)
|
(respTime', pgrst) <- withTiming $ liftEither $ Response.infoProcResponse (Plan.crProc cPlan)
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' Nothing respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime planTime' Nothing respTime') pgrst
|
||||||
|
|
||||||
(ActionInfo, TargetDefaultSpec _) -> do
|
ActSchema _ ActSchemaInfo -> do
|
||||||
(respTime', pgrst) <- withTiming $ liftEither Response.infoRootResponse
|
(respTime', pgrst) <- withTiming $ liftEither Response.infoRootResponse
|
||||||
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst
|
return $ pgrstResponse (ServerTiming jwtTime parseTime Nothing Nothing respTime') pgrst
|
||||||
|
|
||||||
_ ->
|
|
||||||
-- This is unreachable as the ApiRequest.hs rejects it before
|
|
||||||
-- TODO Refactor the Action/Target types to remove this line
|
|
||||||
throwError $ Error.ApiRequestError ApiRequestTypes.NotFound
|
|
||||||
where
|
where
|
||||||
roleSettings = fromMaybe mempty (HM.lookup authRole $ configRoleSettings conf)
|
roleSettings = fromMaybe mempty (HM.lookup authRole $ configRoleSettings conf)
|
||||||
roleIsoLvl = HM.findWithDefault SQL.ReadCommitted authRole $ configRoleIsoLvl conf
|
roleIsoLvl = HM.findWithDefault SQL.ReadCommitted authRole $ configRoleIsoLvl conf
|
||||||
|
|||||||
+28
-29
@@ -38,6 +38,8 @@ import Data.List (delete, lookup)
|
|||||||
import Data.Tree (Tree (..))
|
import Data.Tree (Tree (..))
|
||||||
|
|
||||||
import PostgREST.ApiRequest (Action (..),
|
import PostgREST.ApiRequest (Action (..),
|
||||||
|
ActionRelation (..),
|
||||||
|
ActionRoutine (..),
|
||||||
ApiRequest (..),
|
ApiRequest (..),
|
||||||
InvokeMethod (..),
|
InvokeMethod (..),
|
||||||
Mutation (..),
|
Mutation (..),
|
||||||
@@ -139,24 +141,21 @@ mutateReadPlan mutation apiRequest@ApiRequest{iPreferences=Preferences{..},..}
|
|||||||
callReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> InvokeMethod -> Either Error CallReadPlan
|
callReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> InvokeMethod -> Either Error CallReadPlan
|
||||||
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} invMethod = do
|
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} invMethod = do
|
||||||
let paramKeys = case invMethod of
|
let paramKeys = case invMethod of
|
||||||
InvGet -> S.fromList $ fst <$> qsParams'
|
InvRead _ -> S.fromList $ fst <$> qsParams'
|
||||||
InvHead -> S.fromList $ fst <$> qsParams'
|
Inv -> iColumns
|
||||||
InvPost -> iColumns
|
|
||||||
proc@Function{..} <- mapLeft ApiRequestError $
|
proc@Function{..} <- mapLeft ApiRequestError $
|
||||||
findProc identifier paramKeys (preferParameters == Just SingleObject) (dbRoutines sCache) iContentMediaType (invMethod == InvPost)
|
findProc identifier paramKeys (preferParameters == Just SingleObject) (dbRoutines sCache) iContentMediaType (invMethod == Inv)
|
||||||
let relIdentifier = QualifiedIdentifier pdSchema (fromMaybe pdName $ Routine.funcTableName proc) -- done so a set returning function can embed other relations
|
let relIdentifier = QualifiedIdentifier pdSchema (fromMaybe pdName $ Routine.funcTableName proc) -- done so a set returning function can embed other relations
|
||||||
rPlan <- readPlan relIdentifier conf sCache apiRequest
|
rPlan <- readPlan relIdentifier conf sCache apiRequest
|
||||||
let args = case (invMethod, iContentMediaType) of
|
let args = case (invMethod, iContentMediaType) of
|
||||||
(InvGet, _) -> jsonRpcParams proc qsParams'
|
(InvRead _, _) -> jsonRpcParams proc qsParams'
|
||||||
(InvHead, _) -> jsonRpcParams proc qsParams'
|
(Inv, MTUrlEncoded) -> maybe mempty (jsonRpcParams proc . payArray) iPayload
|
||||||
(InvPost, MTUrlEncoded) -> maybe mempty (jsonRpcParams proc . payArray) iPayload
|
(Inv, _) -> maybe mempty payRaw iPayload
|
||||||
(InvPost, _) -> maybe mempty payRaw iPayload
|
|
||||||
txMode = case (invMethod, pdVolatility) of
|
txMode = case (invMethod, pdVolatility) of
|
||||||
(InvGet, _) -> SQL.Read
|
(InvRead _, _) -> SQL.Read
|
||||||
(InvHead, _) -> SQL.Read
|
(Inv, Routine.Stable) -> SQL.Read
|
||||||
(InvPost, Routine.Stable) -> SQL.Read
|
(Inv, Routine.Immutable) -> SQL.Read
|
||||||
(InvPost, Routine.Immutable) -> SQL.Read
|
(Inv, Routine.Volatile) -> SQL.Write
|
||||||
(InvPost, Routine.Volatile) -> SQL.Write
|
|
||||||
cPlan = callPlan proc apiRequest paramKeys args rPlan
|
cPlan = callPlan proc apiRequest paramKeys args rPlan
|
||||||
(handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest relIdentifier iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan)
|
(handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest relIdentifier iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan)
|
||||||
if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right ()
|
if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right ()
|
||||||
@@ -425,7 +424,7 @@ expandStarsForTable ctx@ResolverContext{representations, outputType} hasAgg rp@R
|
|||||||
|
|
||||||
-- | Enforces the `max-rows` config on the result
|
-- | Enforces the `max-rows` config on the result
|
||||||
treeRestrictRange :: Maybe Integer -> Action -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
treeRestrictRange :: Maybe Integer -> Action -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
||||||
treeRestrictRange _ (ActionMutate _) request = Right request
|
treeRestrictRange _ (ActRelation _ (ActMutate _)) request = Right request
|
||||||
treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> request
|
treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> request
|
||||||
where
|
where
|
||||||
nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan
|
nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan
|
||||||
@@ -462,9 +461,9 @@ addRels schema action allRels parentNode (Node rPlan@ReadPlan{relName,relHint,re
|
|||||||
newReadPlan = case action of
|
newReadPlan = case action of
|
||||||
-- the CTE for mutations/rpc is used as WITH sourceCTEName .. SELECT .. FROM sourceCTEName as alias,
|
-- the CTE for mutations/rpc is used as WITH sourceCTEName .. SELECT .. FROM sourceCTEName as alias,
|
||||||
-- we use the table name as an alias so findRel can find the right relationship.
|
-- we use the table name as an alias so findRel can find the right relationship.
|
||||||
ActionMutate _ -> rPlan{from=newFrom, fromAlias=newAlias}
|
ActRelation _ (ActMutate _) -> rPlan{from=newFrom, fromAlias=newAlias}
|
||||||
ActionInvoke _ -> rPlan{from=newFrom, fromAlias=newAlias}
|
ActRoutine _ _ -> rPlan{from=newFrom, fromAlias=newAlias}
|
||||||
_ -> rPlan
|
_ -> rPlan
|
||||||
in
|
in
|
||||||
Node newReadPlan <$> updateForest (Just $ Node newReadPlan forest)
|
Node newReadPlan <$> updateForest (Just $ Node newReadPlan forest)
|
||||||
where
|
where
|
||||||
@@ -702,9 +701,9 @@ addFilters ctx ApiRequest{..} rReq =
|
|||||||
QueryParams.QueryParams{..} = iQueryParams
|
QueryParams.QueryParams{..} = iQueryParams
|
||||||
flts =
|
flts =
|
||||||
case iAction of
|
case iAction of
|
||||||
ActionInvoke _ -> qsFilters
|
ActRelation _ (ActRead _) -> qsFilters
|
||||||
ActionRead _ -> qsFilters
|
ActRoutine _ _ -> qsFilters
|
||||||
_ -> qsFiltersNotRoot
|
_ -> qsFiltersNotRoot
|
||||||
|
|
||||||
addFilterToNode :: (EmbedPath, Filter) -> Either ApiRequestError ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
addFilterToNode :: (EmbedPath, Filter) -> Either ApiRequestError ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
||||||
addFilterToNode =
|
addFilterToNode =
|
||||||
@@ -713,8 +712,8 @@ addFilters ctx ApiRequest{..} rReq =
|
|||||||
addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
||||||
addOrders ctx ApiRequest{..} rReq =
|
addOrders ctx ApiRequest{..} rReq =
|
||||||
case iAction of
|
case iAction of
|
||||||
ActionMutate _ -> Right rReq
|
ActRelation _ (ActMutate _) -> Right rReq
|
||||||
_ -> foldr addOrderToNode (Right rReq) qsOrder
|
_ -> foldr addOrderToNode (Right rReq) qsOrder
|
||||||
where
|
where
|
||||||
QueryParams.QueryParams{..} = iQueryParams
|
QueryParams.QueryParams{..} = iQueryParams
|
||||||
|
|
||||||
@@ -834,8 +833,8 @@ addNullEmbedFilters (Node rp@ReadPlan{where_=curLogic} forest) = do
|
|||||||
addRanges :: ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
addRanges :: ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
||||||
addRanges ApiRequest{..} rReq =
|
addRanges ApiRequest{..} rReq =
|
||||||
case iAction of
|
case iAction of
|
||||||
ActionMutate _ -> Right rReq
|
ActRelation _ (ActMutate _) -> Right rReq
|
||||||
_ -> foldr addRangeToNode (Right rReq) =<< ranges
|
_ -> foldr addRangeToNode (Right rReq) =<< ranges
|
||||||
where
|
where
|
||||||
ranges :: Either ApiRequestError [(EmbedPath, NonnegRange)]
|
ranges :: Either ApiRequestError [(EmbedPath, NonnegRange)]
|
||||||
ranges = first QueryParamError $ QueryParams.pRequestRange `traverse` HM.toList iRange
|
ranges = first QueryParamError $ QueryParams.pRequestRange `traverse` HM.toList iRange
|
||||||
@@ -997,13 +996,13 @@ addFilterToLogicForest flt lf = CoercibleStmnt flt : lf
|
|||||||
negotiateContent :: AppConfig -> ApiRequest -> QualifiedIdentifier -> [MediaType] -> MediaHandlerMap -> Bool -> Either ApiRequestError ResolvedHandler
|
negotiateContent :: AppConfig -> ApiRequest -> QualifiedIdentifier -> [MediaType] -> MediaHandlerMap -> Bool -> Either ApiRequestError ResolvedHandler
|
||||||
negotiateContent conf ApiRequest{iAction=act, iPreferences=Preferences{preferRepresentation=rep}} identifier accepts produces defaultSelect =
|
negotiateContent conf ApiRequest{iAction=act, iPreferences=Preferences{preferRepresentation=rep}} identifier accepts produces defaultSelect =
|
||||||
case (act, firstAcceptedPick) of
|
case (act, firstAcceptedPick) of
|
||||||
(_, Nothing) -> Left . MediaTypeError $ map MediaType.toMime accepts
|
(_, Nothing) -> Left . MediaTypeError $ map MediaType.toMime accepts
|
||||||
(ActionMutate _, Just (x, mt)) -> Right (if rep == Just Full then x else NoAgg, mt)
|
(ActRelation _ (ActMutate _), Just (x, mt)) -> Right (if rep == Just Full then x else NoAgg, mt)
|
||||||
-- no need for an aggregate on HEAD https://github.com/PostgREST/postgrest/issues/2849
|
-- no need for an aggregate on HEAD https://github.com/PostgREST/postgrest/issues/2849
|
||||||
-- TODO: despite no aggregate, these are responding with a Content-Type, which is not correct.
|
-- TODO: despite no aggregate, these are responding with a Content-Type, which is not correct.
|
||||||
(ActionRead True, Just (_, mt)) -> Right (NoAgg, mt)
|
(ActRelation _ (ActRead True), Just (_, mt)) -> Right (NoAgg, mt)
|
||||||
(ActionInvoke InvHead, Just (_, mt)) -> Right (NoAgg, mt)
|
(ActRoutine _ (ActInvoke (InvRead True)), Just (_, mt)) -> Right (NoAgg, mt)
|
||||||
(_, Just (x, mt)) -> Right (x, mt)
|
(_, Just (x, mt)) -> Right (x, mt)
|
||||||
where
|
where
|
||||||
firstAcceptedPick = listToMaybe $ mapMaybe matchMT accepts -- If there are multiple accepted media types, pick the first. This is usual in content negotiation.
|
firstAcceptedPick = listToMaybe $ mapMaybe matchMT accepts -- If there are multiple accepted media types, pick the first. This is usual in content negotiation.
|
||||||
matchMT mt = case mt of
|
matchMT mt = case mt of
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ invokeResponse CallReadPlan{crMedia} invMethod proc ctxApiRequest@ApiRequest{iPr
|
|||||||
else
|
else
|
||||||
(status,
|
(status,
|
||||||
headers ++ contentTypeHeaders crMedia ctxApiRequest,
|
headers ++ contentTypeHeaders crMedia ctxApiRequest,
|
||||||
if invMethod == InvHead then mempty else rsOrErrBody)
|
if invMethod == InvRead True then mempty else rsOrErrBody)
|
||||||
|
|
||||||
(ovStatus, ovHeaders) <- overrideStatusHeaders rsGucStatus rsGucHeaders status' headers'
|
(ovStatus, ovHeaders) <- overrideStatusHeaders rsGucStatus rsGucHeaders status' headers'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user