Skip to content

Commit

Permalink
RPC POST for function w/single unnamed XML param
Browse files Browse the repository at this point in the history
  • Loading branch information
fjf2002 authored and steve-chavez committed Jun 3, 2022
1 parent d14f745 commit 807364b
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #2236, Support POSIX regular expression operators for row filtering - @enote-kane
- #2202, Allow returning XML from RPCs - @fjf2002
- #2269, Allow `limit=0` in the request query to return an empty array - @gautam1168, @laurenceisla
- #2268, Allow returning XML from single-column queries #2268 - @fjf2002
- #2268, Allow returning XML from single-column queries - @fjf2002
- #2300, RPC POST for function w/single unnamed XML param #2300 - @fjf2002

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion src/PostgREST/DbStructure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ procsSqlQuery pgVer = [q|
) ORDER BY idx) AS args,
CASE COUNT(*) - COUNT(name) -- number of unnamed arguments
WHEN 0 THEN true
WHEN 1 THEN (array_agg(type))[1] IN ('bytea'::regtype, 'json'::regtype, 'jsonb'::regtype, 'text'::regtype)
WHEN 1 THEN (array_agg(type))[1] IN ('bytea'::regtype, 'json'::regtype, 'jsonb'::regtype, 'text'::regtype, 'xml'::regtype)
ELSE false
END AS callable
FROM pg_proc,
Expand Down
1 change: 1 addition & 0 deletions src/PostgREST/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ instance JSON.ToJSON ApiRequestError where
(case (hasPreferSingleObject, isInvPost, contentType) of
(True, _, _) -> " function with a single json or jsonb parameter"
(_, True, CTTextPlain) -> " function with a single unnamed text parameter"
(_, True, CTTextXML) -> " function with a single unnamed xml parameter"
(_, True, CTOctetStream) -> " function with a single unnamed bytea parameter"
(_, True, CTApplicationJSON) -> prms <> " function or the " <> schema <> "." <> procName <>" function with a single unnamed json or jsonb parameter"
_ -> prms <> " function") <>
Expand Down
4 changes: 3 additions & 1 deletion src/PostgREST/Request/ApiRequest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ apiRequest conf@AppConfig{..} dbStructure req reqBody queryparams@QueryParams{..
let paramsMap = M.fromList $ (T.decodeUtf8 *** JSON.String . T.decodeUtf8) <$> parseSimpleQuery (LBS.toStrict reqBody) in
Right $ ProcessedJSON (JSON.encode paramsMap) $ S.fromList (M.keys paramsMap)
(CTTextPlain, True) -> Right $ RawPay reqBody
(CTTextXML, True) -> Right $ RawPay reqBody
(CTOctetStream, True) -> Right $ RawPay reqBody
(ct, _) -> Left $ "Content-Type not acceptable: " <> ContentType.toMime ct
topLevelRange = fromMaybe allRange $ M.lookup "limit" ranges -- if no limit is specified, get all the request rows
Expand Down Expand Up @@ -462,6 +463,7 @@ findProc qi argumentsKeys paramsAsSingleObject allProcs contentType isInvPost =
(CTApplicationJSON, "json") -> True
(CTApplicationJSON, "jsonb") -> True
(CTTextPlain, "text") -> True
(CTTextXML, "xml") -> True
(CTOctetStream, "bytea") -> True
_ -> False
hasSingleUnnamedParam _ = False
Expand All @@ -475,7 +477,7 @@ findProc qi argumentsKeys paramsAsSingleObject allProcs contentType isInvPost =
then length params == 1 && (firstType == Just "json" || firstType == Just "jsonb")
-- If the function has no parameters, the arguments keys must be empty as well
else if null params
then null argumentsKeys && not (isInvPost && contentType `elem` [CTTextPlain, CTOctetStream])
then null argumentsKeys && not (isInvPost && contentType `elem` [CTOctetStream, CTTextPlain, CTTextXML])
-- A function has optional and required parameters. Optional parameters have a default value and
-- don't require arguments for the function to be executed, required parameters must have an argument present.
else case L.partition ppReq params of
Expand Down
37 changes: 37 additions & 0 deletions test/spec/Feature/Query/RpcSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,13 @@ spec actualPgVersion =
`shouldRespondWith`
[str|unnamed text arg|]

it "can insert xml directly" $
request methodPost "/rpc/unnamed_xml_param"
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]
`shouldRespondWith`
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]

it "can insert bytea directly" $ do
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png"
r <- request methodPost "/rpc/unnamed_bytea_param"
Expand Down Expand Up @@ -1240,6 +1247,21 @@ spec actualPgVersion =
, matchHeaders = [ matchContentTypeJson ]
}

it "will err when no function with single unnamed text parameter exists and text/xml is specified" $
request methodPost "/rpc/unnamed_int_param"
[("Content-Type", "text/xml")]
[str|a simple text|]
`shouldRespondWith`
[json|{
"hint": "If a new function was created in the database with this name and parameters, try reloading the schema cache.",
"message": "Could not find the test.unnamed_int_param function with a single unnamed xml parameter in the schema cache",
"code":"PGRST202",
"details":null
}|]
{ matchStatus = 404
, matchHeaders = [ matchContentTypeJson ]
}

it "will err when no function with single unnamed bytea parameter exists and application/octet-stream is specified" $
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png" in
request methodPost "/rpc/unnamed_int_param"
Expand Down Expand Up @@ -1329,3 +1351,18 @@ spec actualPgVersion =
{ matchStatus = 300
, matchHeaders = [matchContentTypeJson]
}

it "should fail on /rpc/unnamed_xml_param when posting invalid xml" $ do
request methodPost "/rpc/unnamed_xml_param"
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
[str|<|]
`shouldRespondWith`
[json| {
"hint":null,
"message":"invalid XML content",
"code":"2200N",
"details":"line 1: StartTag: invalid element name\n<\n ^"
}|]
{ matchStatus = 400
, matchHeaders = [matchContentTypeJson]
}
4 changes: 4 additions & 0 deletions test/spec/fixtures/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2312,6 +2312,10 @@ create or replace function test.unnamed_text_param(text) returns text as $$
select $1;
$$ language sql;

create or replace function test.unnamed_xml_param(pg_catalog.xml) returns pg_catalog.xml as $$
select $1;
$$ language sql;

create or replace function test.unnamed_bytea_param(bytea) returns bytea as $$
select $1::bytea;
$$ language sql;
Expand Down

0 comments on commit 807364b

Please sign in to comment.