Allow calling variadic functions with repeated query params or JSON array in body
This commit is contained in:
committed by
Steve Chavez
parent
18cc214c04
commit
302d4e15ad
@@ -179,10 +179,14 @@ userApiRequest confSchemas rootSpec dbStructure req reqBody
|
||||
| otherwise = Nothing
|
||||
parsedColumns = pRequestColumns columns
|
||||
payloadColumns =
|
||||
case (relevantPayload, fromRight Nothing parsedColumns) of
|
||||
(Just ProcessedJSON{pjKeys}, _) -> pjKeys
|
||||
(Just RawJSON{}, Just cls) -> cls
|
||||
_ -> S.empty
|
||||
case (contentType, action) of
|
||||
(_, ActionInvoke InvGet) -> S.fromList $ fst <$> rpcQParams
|
||||
(_, ActionInvoke InvHead) -> S.fromList $ fst <$> rpcQParams
|
||||
(CTOther "application/x-www-form-urlencoded", _) -> S.fromList $ map (toS . fst) $ parseSimpleQuery $ toS reqBody
|
||||
_ -> case (relevantPayload, fromRight Nothing parsedColumns) of
|
||||
(Just ProcessedJSON{pjKeys}, _) -> pjKeys
|
||||
(Just RawJSON{}, Just cls) -> cls
|
||||
_ -> S.empty
|
||||
payload =
|
||||
case (contentType, action) of
|
||||
(_, ActionInvoke InvGet) -> Right rpcPrmsToJson
|
||||
@@ -198,12 +202,18 @@ userApiRequest confSchemas rootSpec dbStructure req reqBody
|
||||
json <- csvToJson <$> CSV.decodeByName reqBody
|
||||
note "All lines must have same number of fields" $ payloadAttributes (JSON.encode json) json
|
||||
(CTOther "application/x-www-form-urlencoded", _) ->
|
||||
let json = M.fromList . map (toS *** JSON.String . toS) . parseSimpleQuery $ toS reqBody
|
||||
let json = paramsFromList . map (toS *** toS) . parseSimpleQuery $ toS reqBody
|
||||
keys = S.fromList $ M.keys json in
|
||||
Right $ ProcessedJSON (JSON.encode json) keys
|
||||
(ct, _) ->
|
||||
Left $ toS $ "Content-Type not acceptable: " <> toMime ct
|
||||
rpcPrmsToJson = ProcessedJSON (JSON.encode $ M.fromList $ second JSON.toJSON <$> rpcQParams) (S.fromList $ fst <$> rpcQParams)
|
||||
rpcPrmsToJson = ProcessedJSON (JSON.encode $ paramsFromList rpcQParams) (S.fromList $ fst <$> rpcQParams)
|
||||
paramsFromList ls = M.fromListWith mergeParams $ toRpcParamsWith isVariadic ls
|
||||
where
|
||||
isVariadic k =
|
||||
case target of
|
||||
TargetProc{tProc} -> argIsVariadic tProc k
|
||||
_ -> False
|
||||
topLevelRange = fromMaybe allRange $ M.lookup "limit" ranges -- if no limit is specified, get all the request rows
|
||||
action =
|
||||
case method of
|
||||
|
||||
@@ -149,14 +149,16 @@ decodeProcs =
|
||||
parseArgs = mapMaybe parseArg . filter (not . isPrefixOf "OUT" . toS) . map strip . split (==',')
|
||||
|
||||
parseArg :: Text -> Maybe PgArg
|
||||
parseArg a =
|
||||
let arg = lastDef "" $ splitOn "INOUT " a
|
||||
(body, def) = breakOn " DEFAULT " arg
|
||||
parseArg arg =
|
||||
let isVariadic = isPrefixOf "VARIADIC " $ toS arg
|
||||
-- argmode can be IN, OUT, INOUT, or VARIADIC
|
||||
argNoMode = lastDef "" $ splitOn (if isVariadic then "VARIADIC " else "INOUT ") arg
|
||||
(body, def) = breakOn " DEFAULT " argNoMode
|
||||
(name, typ) = breakOn " " body in
|
||||
if T.null typ
|
||||
then Nothing
|
||||
else Just $
|
||||
PgArg (dropAround (== '"') name) (strip typ) (T.null def)
|
||||
PgArg (dropAround (== '"') name) (strip typ) (T.null def) isVariadic
|
||||
|
||||
parseRetType :: Text -> Text -> Bool -> Char -> RetType
|
||||
parseRetType schema name isSetOf typ
|
||||
|
||||
@@ -96,7 +96,7 @@ makeProcSchema pd =
|
||||
& required .~ map pgaName (filter pgaReq (pdArgs pd))
|
||||
|
||||
makeProcProperty :: PgArg -> (Text, Referenced Schema)
|
||||
makeProcProperty (PgArg n t _) = (n, Inline s)
|
||||
makeProcProperty (PgArg n t _ _) = (n, Inline s)
|
||||
where
|
||||
s = (mempty :: Schema)
|
||||
& type_ ?~ toSwaggerType t
|
||||
|
||||
@@ -137,15 +137,18 @@ requestToCallProcQuery qi pgArgs returnsScalar preferParams returnings =
|
||||
BS.unwords [
|
||||
normalizedBody <> ",",
|
||||
"pgrst_args AS (",
|
||||
"SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtArgs (\a -> " " <> encodeUtf8 (pgaType a)) <> ")",
|
||||
"SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtArgs (const mempty) (\a -> " " <> encodeUtf8 (pgaType a)) <> ")",
|
||||
")"]
|
||||
, if paramsAsMultipleObjects
|
||||
then fmtArgs (\a -> " := pgrst_args." <> pgFmtIdent (pgaName a))
|
||||
else fmtArgs (\a -> " := (SELECT " <> pgFmtIdent (pgaName a) <> " FROM pgrst_args LIMIT 1)")
|
||||
then fmtArgs varadicPrefix (\a -> " := pgrst_args." <> pgFmtIdent (pgaName a))
|
||||
else fmtArgs varadicPrefix (\a -> " := (SELECT " <> pgFmtIdent (pgaName a) <> " FROM pgrst_args LIMIT 1)")
|
||||
)
|
||||
|
||||
fmtArgs :: (PgArg -> SqlFragment) -> SqlFragment
|
||||
fmtArgs argFrag = BS.intercalate ", " ((\a -> pgFmtIdent (pgaName a) <> argFrag a) <$> pgArgs)
|
||||
fmtArgs :: (PgArg -> SqlFragment) -> (PgArg -> SqlFragment) -> SqlFragment
|
||||
fmtArgs argFragPre argFragSuf = BS.intercalate ", " ((\a -> argFragPre a <> pgFmtIdent (pgaName a) <> argFragSuf a) <$> pgArgs)
|
||||
|
||||
varadicPrefix :: PgArg -> SqlFragment
|
||||
varadicPrefix a = if pgaVar a then "VARIADIC " else mempty
|
||||
|
||||
sourceBody :: SqlFragment
|
||||
sourceBody
|
||||
|
||||
+29
-3
@@ -4,6 +4,7 @@ Description : PostgREST common types and functions used by the rest of the modul
|
||||
-}
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
|
||||
module PostgREST.Types where
|
||||
|
||||
@@ -123,6 +124,7 @@ data PgArg = PgArg {
|
||||
pgaName :: Text
|
||||
, pgaType :: Text
|
||||
, pgaReq :: Bool
|
||||
, pgaVar :: Bool
|
||||
} deriving (Show, Eq, Ord)
|
||||
|
||||
data PgType = Scalar QualifiedIdentifier | Composite QualifiedIdentifier deriving (Eq, Show, Ord)
|
||||
@@ -180,7 +182,7 @@ findProc qi payloadKeys paramsAsSingleObject allProcs = fromMaybe fallback bestM
|
||||
-}
|
||||
specifiedProcArgs :: S.Set FieldName -> ProcDescription -> [PgArg]
|
||||
specifiedProcArgs keys proc =
|
||||
(\k -> fromMaybe (PgArg k "text" True) (find ((==) k . pgaName) (pdArgs proc))) <$> S.toList keys
|
||||
(\k -> fromMaybe (PgArg k "text" True False) (find ((==) k . pgaName) (pdArgs proc))) <$> S.toList keys
|
||||
|
||||
procReturnsScalar :: ProcDescription -> Bool
|
||||
procReturnsScalar proc = case proc of
|
||||
@@ -193,6 +195,12 @@ procTableName proc = case pdReturnType proc of
|
||||
Single (Composite qi) -> Just $ qiName qi
|
||||
_ -> Nothing
|
||||
|
||||
argIsVariadic :: ProcDescription -> Text -> Bool
|
||||
argIsVariadic proc arg =
|
||||
case find (\PgArg{pgaName} -> pgaName == arg) $ pdArgs proc of
|
||||
Just PgArg{pgaVar} -> pgaVar
|
||||
_ -> False
|
||||
|
||||
type Schema = Text
|
||||
type TableName = Text
|
||||
|
||||
@@ -399,8 +407,26 @@ type Alias = Text
|
||||
type Cast = Text
|
||||
type NodeName = Text
|
||||
|
||||
-- Rpc query param, only used for GET rpcs
|
||||
type RpcQParam = (Text, Text)
|
||||
-- RPC query param, used for POST of form-data and GET requests
|
||||
data RpcParamValue = Fixed Text | Variadic [Text]
|
||||
|
||||
mergeParams :: RpcParamValue -> RpcParamValue -> RpcParamValue
|
||||
mergeParams (Variadic a) (Variadic b) = Variadic $ b ++ a
|
||||
-- repeated params for non-variadic arguments are not merged
|
||||
mergeParams _ v = v
|
||||
|
||||
instance JSON.ToJSON RpcParamValue where
|
||||
toJSON (Fixed v) = JSON.toJSON v
|
||||
toJSON (Variadic v) = JSON.toJSON v
|
||||
|
||||
type RpcParams = [(Text, RpcParamValue)]
|
||||
|
||||
toRpcParamsWith :: (Text -> Bool) -> [(Text, Text)] -> RpcParams
|
||||
toRpcParamsWith isVariadic ls = toRpcParamValue <$> ls
|
||||
where
|
||||
toRpcParamValue (k, v)
|
||||
| isVariadic k = (k, Variadic [v])
|
||||
| otherwise = (k, Fixed v)
|
||||
|
||||
{-|
|
||||
Custom guc header, it's obtained by parsing the json in a:
|
||||
|
||||
Reference in New Issue
Block a user