refactor: rm configSchema ref in postgrest func
* make TargetDefaultSpec take a schema attribute * remove schema param from addJoinConditions
This commit is contained in:
committed by
Steve Chávez
parent
50f2cc16ab
commit
1173bc277b
+13
-10
@@ -63,7 +63,7 @@ data Action = ActionCreate | ActionRead{isHead :: Bool}
|
|||||||
-- | The target db object of a user action
|
-- | The target db object of a user action
|
||||||
data Target = TargetIdent QualifiedIdentifier
|
data Target = TargetIdent QualifiedIdentifier
|
||||||
| TargetProc{tpQi :: QualifiedIdentifier, tpIsRootSpec :: Bool}
|
| TargetProc{tpQi :: QualifiedIdentifier, tpIsRootSpec :: Bool}
|
||||||
| TargetDefaultSpec -- The default spec offered at root "/"
|
| TargetDefaultSpec{tdsSchema :: Schema} -- The default spec offered at root "/"
|
||||||
| TargetUnknown [Text]
|
| TargetUnknown [Text]
|
||||||
deriving Eq
|
deriving Eq
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ data ApiRequest = ApiRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- | Examines HTTP request and translates it into user intent.
|
-- | Examines HTTP request and translates it into user intent.
|
||||||
userApiRequest :: Schema -> Maybe QualifiedIdentifier -> Request -> RequestBody -> Either ApiRequestError ApiRequest
|
userApiRequest :: Schema -> Maybe Text -> Request -> RequestBody -> Either ApiRequestError ApiRequest
|
||||||
userApiRequest schema rootSpec req reqBody
|
userApiRequest schema rootSpec req reqBody
|
||||||
| isTargetingProc && method `notElem` ["HEAD", "GET", "POST"] = Left ActionInappropriate
|
| isTargetingProc && method `notElem` ["HEAD", "GET", "POST"] = Left ActionInappropriate
|
||||||
| topLevelRange == emptyRange = Left InvalidRange
|
| topLevelRange == emptyRange = Left InvalidRange
|
||||||
@@ -178,6 +178,9 @@ userApiRequest schema rootSpec req reqBody
|
|||||||
isTargetingProc = case target of
|
isTargetingProc = case target of
|
||||||
TargetProc _ _ -> True
|
TargetProc _ _ -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
isTargetingDefaultSpec = case target of
|
||||||
|
TargetDefaultSpec _ -> True
|
||||||
|
_ -> False
|
||||||
contentType = decodeContentType . fromMaybe "application/json" $ lookupHeader "content-type"
|
contentType = decodeContentType . fromMaybe "application/json" $ lookupHeader "content-type"
|
||||||
columns
|
columns
|
||||||
| action `elem` [ActionCreate, ActionUpdate, ActionInvoke InvPost] = toS <$> join (lookup "columns" qParams)
|
| action `elem` [ActionCreate, ActionUpdate, ActionInvoke InvPost] = toS <$> join (lookup "columns" qParams)
|
||||||
@@ -209,12 +212,12 @@ userApiRequest schema rootSpec req reqBody
|
|||||||
case method of
|
case method of
|
||||||
-- The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response
|
-- 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
|
-- From https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
|
||||||
"HEAD" | target == TargetDefaultSpec -> ActionInspect{isHead=True}
|
"HEAD" | isTargetingDefaultSpec -> ActionInspect{isHead=True}
|
||||||
| isTargetingProc -> ActionInvoke InvHead
|
| isTargetingProc -> ActionInvoke InvHead
|
||||||
| otherwise -> ActionRead{isHead=True}
|
| otherwise -> ActionRead{isHead=True}
|
||||||
"GET" | target == TargetDefaultSpec -> ActionInspect{isHead=False}
|
"GET" | isTargetingDefaultSpec -> ActionInspect{isHead=False}
|
||||||
| isTargetingProc -> ActionInvoke InvGet
|
| isTargetingProc -> ActionInvoke InvGet
|
||||||
| otherwise -> ActionRead{isHead=False}
|
| otherwise -> ActionRead{isHead=False}
|
||||||
"POST" -> if isTargetingProc
|
"POST" -> if isTargetingProc
|
||||||
then ActionInvoke InvPost
|
then ActionInvoke InvPost
|
||||||
else ActionCreate
|
else ActionCreate
|
||||||
@@ -225,8 +228,8 @@ userApiRequest schema rootSpec req reqBody
|
|||||||
_ -> ActionInspect{isHead=False}
|
_ -> ActionInspect{isHead=False}
|
||||||
target = case path of
|
target = case path of
|
||||||
[] -> case rootSpec of
|
[] -> case rootSpec of
|
||||||
Just rsQi -> TargetProc rsQi True
|
Just pName -> TargetProc (QualifiedIdentifier schema pName) True
|
||||||
Nothing -> TargetDefaultSpec
|
Nothing -> TargetDefaultSpec schema
|
||||||
[table] -> TargetIdent $ QualifiedIdentifier schema table
|
[table] -> TargetIdent $ QualifiedIdentifier schema table
|
||||||
["rpc", proc] -> TargetProc (QualifiedIdentifier schema proc) False
|
["rpc", proc] -> TargetProc (QualifiedIdentifier schema proc) False
|
||||||
other -> TargetUnknown other
|
other -> TargetUnknown other
|
||||||
|
|||||||
+11
-9
@@ -125,8 +125,8 @@ app dbStructure proc cols conf apiRequest =
|
|||||||
Right contentType ->
|
Right contentType ->
|
||||||
case (iAction apiRequest, iTarget apiRequest, iPayload apiRequest) of
|
case (iAction apiRequest, iTarget apiRequest, iPayload apiRequest) of
|
||||||
|
|
||||||
(ActionRead headersOnly, TargetIdent (QualifiedIdentifier _ tName), Nothing) ->
|
(ActionRead headersOnly, TargetIdent (QualifiedIdentifier tSchema tName), Nothing) ->
|
||||||
case readSqlParts tName of
|
case readSqlParts tSchema tName of
|
||||||
Left errorResponse -> return errorResponse
|
Left errorResponse -> return errorResponse
|
||||||
Right (q, cq, bField) -> do
|
Right (q, cq, bField) -> do
|
||||||
let cQuery = if estimatedCount
|
let cQuery = if estimatedCount
|
||||||
@@ -281,9 +281,9 @@ app dbStructure proc cols conf apiRequest =
|
|||||||
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header in
|
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header in
|
||||||
return $ responseLBS status200 [allOrigins, allowH] mempty
|
return $ responseLBS status200 [allOrigins, allowH] mempty
|
||||||
|
|
||||||
(ActionInvoke invMethod, TargetProc qi@(QualifiedIdentifier _ pName) _, Just pJson) ->
|
(ActionInvoke invMethod, TargetProc qi@(QualifiedIdentifier tSchema pName) _, Just pJson) ->
|
||||||
let tName = fromMaybe pName $ procTableName =<< proc in
|
let tName = fromMaybe pName $ procTableName =<< proc in
|
||||||
case readSqlParts tName of
|
case readSqlParts tSchema tName of
|
||||||
Left errorResponse -> return errorResponse
|
Left errorResponse -> return errorResponse
|
||||||
Right (q, cq, bField) -> do
|
Right (q, cq, bField) -> do
|
||||||
let
|
let
|
||||||
@@ -306,7 +306,7 @@ app dbStructure proc cols conf apiRequest =
|
|||||||
return $ responseLBS status ([toHeader contentType, contentRange] ++ toHeaders hs)
|
return $ responseLBS status ([toHeader contentType, contentRange] ++ toHeaders hs)
|
||||||
(if invMethod == InvHead then mempty else toS body)
|
(if invMethod == InvHead then mempty else toS body)
|
||||||
|
|
||||||
(ActionInspect headersOnly, TargetDefaultSpec, Nothing) -> do
|
(ActionInspect headersOnly, TargetDefaultSpec tSchema, Nothing) -> do
|
||||||
let host = configHost conf
|
let host = configHost conf
|
||||||
port = toInteger $ configPort conf
|
port = toInteger $ configPort conf
|
||||||
proxy = pickProxy $ toS <$> configProxyUri conf
|
proxy = pickProxy $ toS <$> configProxyUri conf
|
||||||
@@ -317,14 +317,16 @@ app dbStructure proc cols conf apiRequest =
|
|||||||
toTableInfo = map (\t -> let (s, tn) = (tableSchema t, tableName t) in (t, tableCols dbStructure s tn, tablePKCols dbStructure s tn))
|
toTableInfo = map (\t -> let (s, tn) = (tableSchema t, tableName t) in (t, tableCols dbStructure s tn, tablePKCols dbStructure s tn))
|
||||||
encodeApi ti sd procs = encodeOpenAPI (concat $ M.elems procs) (toTableInfo ti) uri' sd $ dbPrimaryKeys dbStructure
|
encodeApi ti sd procs = encodeOpenAPI (concat $ M.elems procs) (toTableInfo ti) uri' sd $ dbPrimaryKeys dbStructure
|
||||||
|
|
||||||
body <- encodeApi <$> H.statement schema accessibleTables <*> H.statement schema schemaDescription <*> H.statement schema accessibleProcs
|
body <- encodeApi <$>
|
||||||
|
H.statement tSchema accessibleTables <*>
|
||||||
|
H.statement tSchema schemaDescription <*>
|
||||||
|
H.statement tSchema accessibleProcs
|
||||||
return $ responseLBS status200 [toHeader CTOpenAPI] (if headersOnly then mempty else toS body)
|
return $ responseLBS status200 [toHeader CTOpenAPI] (if headersOnly then mempty else toS body)
|
||||||
|
|
||||||
_ -> return notFound
|
_ -> return notFound
|
||||||
|
|
||||||
where
|
where
|
||||||
notFound = responseLBS status404 [] ""
|
notFound = responseLBS status404 [] ""
|
||||||
schema = toS $ configSchema conf
|
|
||||||
maxRows = configMaxRows conf
|
maxRows = configMaxRows conf
|
||||||
exactCount = iPreferCount apiRequest == Just ExactCount
|
exactCount = iPreferCount apiRequest == Just ExactCount
|
||||||
estimatedCount = iPreferCount apiRequest == Just EstimatedCount
|
estimatedCount = iPreferCount apiRequest == Just EstimatedCount
|
||||||
@@ -334,9 +336,9 @@ app dbStructure proc cols conf apiRequest =
|
|||||||
returnsScalar = maybe False procReturnsScalar proc
|
returnsScalar = maybe False procReturnsScalar proc
|
||||||
|
|
||||||
selectQuery = readRequestToQuery False
|
selectQuery = readRequestToQuery False
|
||||||
readSqlParts tableName =
|
readSqlParts s t =
|
||||||
let
|
let
|
||||||
readReq = readRequest schema tableName maxRows (dbRelations dbStructure) apiRequest
|
readReq = readRequest s t maxRows (dbRelations dbStructure) apiRequest
|
||||||
in
|
in
|
||||||
(,,) <$>
|
(,,) <$>
|
||||||
(selectQuery <$> readReq) <*>
|
(selectQuery <$> readReq) <*>
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>))
|
|||||||
|
|
||||||
import PostgREST.Error (ApiRequestError (..))
|
import PostgREST.Error (ApiRequestError (..))
|
||||||
import PostgREST.Parsers (pRoleClaimKey)
|
import PostgREST.Parsers (pRoleClaimKey)
|
||||||
import PostgREST.Types (JSPath, JSPathExp (..),
|
import PostgREST.Types (JSPath, JSPathExp (..))
|
||||||
QualifiedIdentifier (..))
|
|
||||||
import Protolude hiding (concat, hPutStrLn, intercalate, null,
|
import Protolude hiding (concat, hPutStrLn, intercalate, null,
|
||||||
take, (<>))
|
take, (<>))
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ data AppConfig = AppConfig {
|
|||||||
, configRoleClaimKey :: Either ApiRequestError JSPath
|
, configRoleClaimKey :: Either ApiRequestError JSPath
|
||||||
, configExtraSearchPath :: [Text]
|
, configExtraSearchPath :: [Text]
|
||||||
|
|
||||||
, configRootSpec :: Maybe QualifiedIdentifier
|
, configRootSpec :: Maybe Text
|
||||||
, configRawMediaTypes :: [B.ByteString]
|
, configRawMediaTypes :: [B.ByteString]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,13 +146,12 @@ readOptions = do
|
|||||||
return appConf
|
return appConf
|
||||||
|
|
||||||
where
|
where
|
||||||
dbSchema = reqString "db-schema"
|
|
||||||
parseConfig =
|
parseConfig =
|
||||||
AppConfig
|
AppConfig
|
||||||
<$> reqString "db-uri"
|
<$> reqString "db-uri"
|
||||||
<*> reqString "db-anon-role"
|
<*> reqString "db-anon-role"
|
||||||
<*> optString "server-proxy-uri"
|
<*> optString "server-proxy-uri"
|
||||||
<*> dbSchema
|
<*> reqString "db-schema"
|
||||||
<*> (fromMaybe "!4" <$> optString "server-host")
|
<*> (fromMaybe "!4" <$> optString "server-host")
|
||||||
<*> (fromMaybe 3000 <$> optInt "server-port")
|
<*> (fromMaybe 3000 <$> optInt "server-port")
|
||||||
<*> optString "server-unix-socket"
|
<*> optString "server-unix-socket"
|
||||||
@@ -168,7 +166,7 @@ readOptions = do
|
|||||||
<*> (fmap (fmap coerceText) <$> C.subassocs "app.settings" C.value)
|
<*> (fmap (fmap coerceText) <$> C.subassocs "app.settings" C.value)
|
||||||
<*> (maybe (Right [JSPKey "role"]) parseRoleClaimKey <$> optValue "role-claim-key")
|
<*> (maybe (Right [JSPKey "role"]) parseRoleClaimKey <$> optValue "role-claim-key")
|
||||||
<*> (maybe ["public"] splitOnCommas <$> optValue "db-extra-search-path")
|
<*> (maybe ["public"] splitOnCommas <$> optValue "db-extra-search-path")
|
||||||
<*> ((\x y -> QualifiedIdentifier x <$> y) <$> dbSchema <*> optString "root-spec")
|
<*> optString "root-spec"
|
||||||
<*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types")
|
<*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types")
|
||||||
|
|
||||||
parseJwtAudience :: C.Key -> C.Parser C.Config (Maybe StringOrURI)
|
parseJwtAudience :: C.Key -> C.Parser C.Config (Maybe StringOrURI)
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ treeRestrictRange maxRows request = pure $ nodeRestrictRange maxRows <$> request
|
|||||||
nodeRestrictRange :: Maybe Integer -> ReadNode -> ReadNode
|
nodeRestrictRange :: Maybe Integer -> ReadNode -> ReadNode
|
||||||
nodeRestrictRange m (q@Select {range_=r}, i) = (q{range_=restrictRange m r }, i)
|
nodeRestrictRange m (q@Select {range_=r}, i) = (q{range_=restrictRange m r }, i)
|
||||||
|
|
||||||
augumentRequestWithJoin :: Schema -> [Relation] -> ReadRequest -> Either ApiRequestError ReadRequest
|
augumentRequestWithJoin :: Schema -> [Relation] -> ReadRequest -> Either ApiRequestError ReadRequest
|
||||||
augumentRequestWithJoin schema allRels request =
|
augumentRequestWithJoin schema allRels request =
|
||||||
addRelations schema allRels Nothing request
|
addRelations schema allRels Nothing request
|
||||||
>>= addJoinConditions schema Nothing
|
>>= addJoinConditions Nothing
|
||||||
|
|
||||||
addRelations :: Schema -> [Relation] -> Maybe ReadRequest -> ReadRequest -> Either ApiRequestError ReadRequest
|
addRelations :: Schema -> [Relation] -> Maybe ReadRequest -> ReadRequest -> Either ApiRequestError ReadRequest
|
||||||
addRelations schema allRelations parentNode (Node (query@Select{from=tbl}, (nodeName, _, alias, relationDetail, depth)) forest) =
|
addRelations schema allRelations parentNode (Node (query@Select{from=tbl}, (nodeName, _, alias, relationDetail, depth)) forest) =
|
||||||
@@ -199,8 +199,8 @@ findRelation schema allRelations nodeTableName parentNodeTableName relationDetai
|
|||||||
) allRelations
|
) allRelations
|
||||||
|
|
||||||
-- previousAlias is only used for the case of self joins
|
-- previousAlias is only used for the case of self joins
|
||||||
addJoinConditions :: Schema -> Maybe Alias -> ReadRequest -> Either ApiRequestError ReadRequest
|
addJoinConditions :: Maybe Alias -> ReadRequest -> Either ApiRequestError ReadRequest
|
||||||
addJoinConditions schema previousAlias (Node node@(query@Select{from=tbl}, nodeProps@(_, relation, _, _, depth)) forest) =
|
addJoinConditions previousAlias (Node node@(query@Select{from=tbl}, nodeProps@(_, relation, _, _, depth)) forest) =
|
||||||
case relation of
|
case relation of
|
||||||
Just Relation{relType=Root} -> Node node <$> updatedForest -- this is the root node
|
Just Relation{relType=Root} -> Node node <$> updatedForest -- this is the root node
|
||||||
Just rel@Relation{relType=Parent} -> Node (augmentQuery rel, nodeProps) <$> updatedForest
|
Just rel@Relation{relType=Parent} -> Node (augmentQuery rel, nodeProps) <$> updatedForest
|
||||||
@@ -220,7 +220,7 @@ addJoinConditions schema previousAlias (Node node@(query@Select{from=tbl}, nodeP
|
|||||||
(\jc rq@Select{joinConditions=jcs} -> rq{joinConditions=jc:jcs})
|
(\jc rq@Select{joinConditions=jcs} -> rq{joinConditions=jc:jcs})
|
||||||
query{fromAlias=newAlias}
|
query{fromAlias=newAlias}
|
||||||
(getJoinConditions previousAlias newAlias rel)
|
(getJoinConditions previousAlias newAlias rel)
|
||||||
updatedForest = mapM (addJoinConditions schema newAlias) forest
|
updatedForest = mapM (addJoinConditions newAlias) forest
|
||||||
|
|
||||||
-- previousAlias and newAlias are used in the case of self joins
|
-- previousAlias and newAlias are used in the case of self joins
|
||||||
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relation -> [JoinCondition]
|
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relation -> [JoinCondition]
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ import Test.Hspec.Wai
|
|||||||
import Text.Heredoc
|
import Text.Heredoc
|
||||||
|
|
||||||
import PostgREST.Config (AppConfig (..))
|
import PostgREST.Config (AppConfig (..))
|
||||||
import PostgREST.Types (JSPathExp (..), QualifiedIdentifier (..))
|
import PostgREST.Types (JSPathExp (..))
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
matchContentTypeJson :: MatchHeader
|
matchContentTypeJson :: MatchHeader
|
||||||
@@ -131,7 +131,7 @@ testCfgExtraSearchPath :: Text -> AppConfig
|
|||||||
testCfgExtraSearchPath testDbConn = (testCfg testDbConn) { configExtraSearchPath = ["public", "extensions"] }
|
testCfgExtraSearchPath testDbConn = (testCfg testDbConn) { configExtraSearchPath = ["public", "extensions"] }
|
||||||
|
|
||||||
testCfgRootSpec :: Text -> AppConfig
|
testCfgRootSpec :: Text -> AppConfig
|
||||||
testCfgRootSpec testDbConn = (testCfg testDbConn) { configRootSpec = Just $ QualifiedIdentifier "test" "root"}
|
testCfgRootSpec testDbConn = (testCfg testDbConn) { configRootSpec = Just "root"}
|
||||||
|
|
||||||
testCfgHtmlRawOutput :: Text -> AppConfig
|
testCfgHtmlRawOutput :: Text -> AppConfig
|
||||||
testCfgHtmlRawOutput testDbConn = (testCfg testDbConn) { configRawMediaTypes = ["text/html"] }
|
testCfgHtmlRawOutput testDbConn = (testCfg testDbConn) { configRawMediaTypes = ["text/html"] }
|
||||||
|
|||||||
Reference in New Issue
Block a user