refactor: getSchema in ApiRequest

This commit is contained in:
steve-chavez
2022-10-01 11:05:49 -05:00
committed by Steve Chavez
parent 46307ca64a
commit 858e4405ec
3 changed files with 37 additions and 33 deletions
+1 -1
View File
@@ -285,7 +285,7 @@ handleInvoke invMethod proc context@RequestContext{..} = do
handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response
handleOpenApi headersOnly tSchema (RequestContext conf dbStructure apiRequest pgVer) = do handleOpenApi headersOnly tSchema (RequestContext conf dbStructure apiRequest pgVer) = do
oaiResult <- Query.openApiQuery dbStructure pgVer conf tSchema oaiResult <- Query.openApiQuery dbStructure pgVer conf tSchema
pure $ Response.openApiResponse headersOnly oaiResult conf dbStructure $ iProfile apiRequest pure $ Response.openApiResponse headersOnly oaiResult conf dbStructure (iSchema apiRequest) (iNegotiatedByProfile apiRequest)
writeRequest :: Mutation -> QualifiedIdentifier -> RequestContext -> [FieldName] -> Either Error (MutateRequest.MutateRequest, ReadRequest) writeRequest :: Mutation -> QualifiedIdentifier -> RequestContext -> [FieldName] -> Either Error (MutateRequest.MutateRequest, ReadRequest)
writeRequest mutation identifier@QualifiedIdentifier{..} context@RequestContext{..} pkCols = do writeRequest mutation identifier@QualifiedIdentifier{..} context@RequestContext{..} pkCols = do
+26 -25
View File
@@ -35,7 +35,6 @@ import qualified Data.Vector as V
import Control.Arrow ((***)) import Control.Arrow ((***))
import Data.Aeson.Types (emptyArray, emptyObject) import Data.Aeson.Types (emptyArray, emptyObject)
import Data.List (lookup, union) import Data.List (lookup, union)
import Data.Maybe (fromJust)
import Data.Ranged.Ranges (emptyRange, rangeIntersection, import Data.Ranged.Ranges (emptyRange, rangeIntersection,
rangeIsEmpty) rangeIsEmpty)
import Network.HTTP.Types.Header (hCookie, RequestHeaders) import Network.HTTP.Types.Header (hCookie, RequestHeaders)
@@ -171,8 +170,8 @@ data ApiRequest = ApiRequest {
, iCookies :: [(ByteString, ByteString)] -- ^ Request Cookies , iCookies :: [(ByteString, ByteString)] -- ^ Request Cookies
, iPath :: ByteString -- ^ Raw request path , iPath :: ByteString -- ^ Raw request path
, iMethod :: ByteString -- ^ Raw request method , iMethod :: ByteString -- ^ Raw request method
, iProfile :: Maybe Schema -- ^ The request profile for enabling use of multiple schemas. Follows the spec in hhttps://www.w3.org/TR/dx-prof-conneg/ttps://www.w3.org/TR/dx-prof-conneg/. , iSchema :: Schema -- ^ The request schema. Can vary depending on profile headers.
, iSchema :: Schema -- ^ The request schema. Can vary depending on iProfile. , iNegotiatedByProfile :: Bool -- ^ If schema was was chosen according to the profile spec https://www.w3.org/TR/dx-prof-conneg/
, iAcceptMediaType :: MediaType , iAcceptMediaType :: MediaType
} }
@@ -183,7 +182,8 @@ userApiRequest conf dbStructure req reqBody = do
pInfo <- getPathInfo conf $ pathInfo req pInfo <- getPathInfo conf $ pathInfo req
act <- getAction pInfo $ requestMethod req act <- getAction pInfo $ requestMethod req
mediaTypes <- getMediaTypes conf (requestHeaders req) act pInfo mediaTypes <- getMediaTypes conf (requestHeaders req) act pInfo
apiRequest conf dbStructure req reqBody qPrms pInfo act mediaTypes negotiatedSchema <- getSchema conf (requestHeaders req) (requestMethod req)
apiRequest dbStructure req reqBody qPrms pInfo act mediaTypes negotiatedSchema
getPathInfo :: AppConfig -> [Text] -> Either ApiRequestError PathInfo getPathInfo :: AppConfig -> [Text] -> Either ApiRequestError PathInfo
getPathInfo AppConfig{configOpenApiMode, configDbRootSpec} path = getPathInfo AppConfig{configOpenApiMode, configDbRootSpec} path =
@@ -226,9 +226,27 @@ getMediaTypes conf hdrs action path = do
contentMediaType = maybe MTApplicationJSON MediaType.decodeMediaType $ lookupHeader "content-type" contentMediaType = maybe MTApplicationJSON MediaType.decodeMediaType $ lookupHeader "content-type"
lookupHeader = flip lookup hdrs lookupHeader = flip lookup hdrs
apiRequest :: AppConfig -> DbStructure -> Request -> RequestBody -> QueryParams.QueryParams -> PathInfo -> Action -> (MediaType, MediaType) -> Either ApiRequestError ApiRequest getSchema :: AppConfig -> RequestHeaders -> ByteString -> Either ApiRequestError (Schema, Bool)
apiRequest AppConfig{configDbSchemas} dbStructure req reqBody queryparams@QueryParams{..} PathInfo{pathName, pathIsProc, pathIsRootSpec, pathIsDefSpec} action (acceptMediaType, contentMediaType) getSchema AppConfig{configDbSchemas} hdrs method = do
| isJust profile && fromJust profile `notElem` configDbSchemas = Left $ UnacceptableSchema $ toList configDbSchemas case profile of
Just p | p `notElem` configDbSchemas -> Left $ UnacceptableSchema $ toList configDbSchemas
| otherwise -> Right (p, True)
Nothing -> Right (defaultSchema, length configDbSchemas /= 1) -- if we have many schemas, assume the default schema was negotiated
where
defaultSchema = NonEmptyList.head configDbSchemas
profile = case method of
-- POST/PATCH/PUT/DELETE don't use the same header as per the spec
"DELETE" -> contentProfile
"PATCH" -> contentProfile
"POST" -> contentProfile
"PUT" -> contentProfile
_ -> acceptProfile
contentProfile = T.decodeUtf8 <$> lookupHeader "Content-Profile"
acceptProfile = T.decodeUtf8 <$> lookupHeader "Accept-Profile"
lookupHeader = flip lookup hdrs
apiRequest :: DbStructure -> Request -> RequestBody -> QueryParams.QueryParams -> PathInfo -> Action -> (MediaType, MediaType) -> (Schema, Bool) -> Either ApiRequestError ApiRequest
apiRequest dbStructure req reqBody queryparams@QueryParams{..} PathInfo{pathName, pathIsProc, pathIsRootSpec, pathIsDefSpec} action (acceptMediaType, contentMediaType) (schema, negotiatedByProfile)
| isInvalidRange = Left $ InvalidRange (if rangeIsEmpty headerRange then LowerGTUpper else NegativeLimit) | isInvalidRange = Left $ InvalidRange (if rangeIsEmpty headerRange then LowerGTUpper else NegativeLimit)
| shouldParsePayload && isLeft payload = either (Left . InvalidBody) witness payload | shouldParsePayload && isLeft payload = either (Left . InvalidBody) witness payload
| not expectParams && not (L.null qsParams) = Left $ ParseRequestError "Unexpected param or filter missing operator" ("Failed to parse " <> show qsParams) | not expectParams && not (L.null qsParams) = Left $ ParseRequestError "Unexpected param or filter missing operator" ("Failed to parse " <> show qsParams)
@@ -253,8 +271,8 @@ apiRequest AppConfig{configDbSchemas} dbStructure req reqBody queryparams@QueryP
, iCookies = maybe [] parseCookies $ lookupHeader "Cookie" , iCookies = maybe [] parseCookies $ lookupHeader "Cookie"
, iPath = rawPathInfo req , iPath = rawPathInfo req
, iMethod = method , iMethod = method
, iProfile = profile
, iSchema = schema , iSchema = schema
, iNegotiatedByProfile = negotiatedByProfile
, iAcceptMediaType = acceptMediaType , iAcceptMediaType = acceptMediaType
} }
where where
@@ -296,23 +314,6 @@ apiRequest AppConfig{configDbSchemas} dbStructure req reqBody queryparams@QueryP
(ct, _) -> Left $ "Content-Type not acceptable: " <> MediaType.toMime ct (ct, _) -> Left $ "Content-Type not acceptable: " <> MediaType.toMime ct
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
defaultSchema = NonEmptyList.head configDbSchemas
profile
| length configDbSchemas <= 1 -- only enable content negotiation by profile when there are multiple schemas specified in the config
= Nothing
| otherwise = case method of
-- POST/PATCH/PUT/DELETE don't use the same header as per the spec
"DELETE" -> contentProfile
"PATCH" -> contentProfile
"POST" -> contentProfile
"PUT" -> contentProfile
_ -> acceptProfile
where
contentProfile = Just $ maybe defaultSchema T.decodeUtf8 $ lookupHeader "Content-Profile"
acceptProfile = Just $ maybe defaultSchema T.decodeUtf8 $ lookupHeader "Accept-Profile"
schema = fromMaybe defaultSchema profile
target target
| pathIsProc = (`TargetProc` pathIsRootSpec) <$> callFindProc schema pathName | pathIsProc = (`TargetProc` pathIsRootSpec) <$> callFindProc schema pathName
| pathIsDefSpec = Right $ TargetDefaultSpec schema | pathIsDefSpec = Right $ TargetDefaultSpec schema
+10 -7
View File
@@ -215,10 +215,10 @@ invokeResponse invMethod proc ctxApiRequest@ApiRequest{..} resultSet = case resu
RSPlan plan -> RSPlan plan ->
Wai.responseLBS HTTP.status200 (contentTypeHeaders ctxApiRequest) $ LBS.fromStrict plan Wai.responseLBS HTTP.status200 (contentTypeHeaders ctxApiRequest) $ LBS.fromStrict plan
openApiResponse :: Bool -> Maybe (TablesMap, ProcsMap, Maybe Text) -> AppConfig -> DbStructure -> Maybe Schema -> Wai.Response openApiResponse :: Bool -> Maybe (TablesMap, ProcsMap, Maybe Text) -> AppConfig -> DbStructure -> Schema -> Bool -> Wai.Response
openApiResponse headersOnly body conf dbStructure iProfile = openApiResponse headersOnly body conf dbStructure schema negotiatedByProfile =
Wai.responseLBS HTTP.status200 Wai.responseLBS HTTP.status200
(MediaType.toContentType MTOpenAPI : maybeToList (profileHeader iProfile)) (MediaType.toContentType MTOpenAPI : maybeToList (profileHeader schema negotiatedByProfile))
(maybe mempty (\(x, y, z) -> if headersOnly then mempty else OpenAPI.encode conf dbStructure x y z) body) (maybe mempty (\(x, y, z) -> if headersOnly then mempty else OpenAPI.encode conf dbStructure x y z) body)
-- | Response with headers and status overridden from GUCs. -- | Response with headers and status overridden from GUCs.
@@ -245,11 +245,14 @@ decodeGucStatus =
contentTypeHeaders :: ApiRequest -> [HTTP.Header] contentTypeHeaders :: ApiRequest -> [HTTP.Header]
contentTypeHeaders ApiRequest{..} = contentTypeHeaders ApiRequest{..} =
MediaType.toContentType iAcceptMediaType : maybeToList (profileHeader iProfile) MediaType.toContentType iAcceptMediaType : maybeToList (profileHeader iSchema iNegotiatedByProfile)
profileHeader :: Maybe Schema -> Maybe HTTP.Header profileHeader :: Schema -> Bool -> Maybe HTTP.Header
profileHeader iProfile = profileHeader schema negotiatedByProfile =
(,) "Content-Profile" <$> (toS <$> iProfile) if negotiatedByProfile
then Just $ (,) "Content-Profile" (toS schema)
else
Nothing
addRetryHint :: Int -> Wai.Response -> Wai.Response addRetryHint :: Int -> Wai.Response -> Wai.Response
addRetryHint delay response = do addRetryHint delay response = do