diff --git a/CHANGELOG.md b/CHANGELOG.md index 647ec3304..e6905ba01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #887, #601, Allow specifying dictionary and plain/phrase tsquery in full text search - @steve-chavez - #328, Allow doing GET on rpc - @steve-chavez +- #917, Add ability to map RAISE errorcode/message to http status - @steve-chavez ### Fixed diff --git a/src/PostgREST/Error.hs b/src/PostgREST/Error.hs index dec3c6cf8..126a2d166 100644 --- a/src/PostgREST/Error.hs +++ b/src/PostgREST/Error.hs @@ -22,6 +22,7 @@ import Network.HTTP.Types.Header import qualified Network.HTTP.Types.Status as HT import Network.Wai (Response, responseLBS) import PostgREST.Types +import Text.Read (readMaybe) apiRequestError :: ApiRequestError -> Response apiRequestError err = @@ -109,11 +110,17 @@ instance JSON.ToJSON P.UsageError where toJSON (P.SessionError e) = JSON.toJSON e -- H.Error instance JSON.ToJSON H.Error where - toJSON (H.ResultError (H.ServerError c m d h)) = JSON.object [ - "code" .= (toS c::Text), - "message" .= (toS m::Text), - "details" .= (fmap toS d::Maybe Text), - "hint" .= (fmap toS h::Maybe Text)] + toJSON (H.ResultError (H.ServerError c m d h)) = case toS c of + 'P':'T':_ -> + JSON.object [ + "details" .= (fmap toS d::Maybe Text), + "hint" .= (fmap toS h::Maybe Text)] + _ -> + JSON.object [ + "code" .= (toS c::Text), + "message" .= (toS m::Text), + "details" .= (fmap toS d::Maybe Text), + "hint" .= (fmap toS h::Maybe Text)] toJSON (H.ResultError (H.UnexpectedResult m)) = JSON.object [ "message" .= (m::Text)] toJSON (H.ResultError (H.RowError i H.EndOfInput)) = JSON.object [ @@ -138,7 +145,7 @@ instance JSON.ToJSON H.Error where httpStatus :: Bool -> P.UsageError -> HT.Status httpStatus _ (P.ConnectionError _) = HT.status503 -httpStatus authed (P.SessionError (H.ResultError (H.ServerError c _ _ _))) = +httpStatus authed (P.SessionError (H.ResultError (H.ServerError c m _ _))) = case toS c of '0':'8':_ -> HT.status503 -- pg connection err '0':'9':_ -> HT.status500 -- triggered action exception @@ -166,6 +173,7 @@ httpStatus authed (P.SessionError (H.ResultError (H.ServerError c _ _ _))) = "42883" -> HT.status404 -- undefined function "42P01" -> HT.status404 -- undefined table "42501" -> if authed then HT.status403 else HT.status401 -- insufficient privilege + 'P':'T':n -> fromMaybe HT.status500 (HT.mkStatus <$> readMaybe n <*> pure m) _ -> HT.status400 httpStatus _ (P.SessionError (H.ResultError _)) = HT.status500 httpStatus _ (P.SessionError (H.ClientError _)) = HT.status503 diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index 5e9959fc6..b6f03a616 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -276,6 +276,16 @@ spec = get "/rpc/many_inout_params?num=1&str=two" `shouldRespondWith` [json| [{"num":1,"str":"two","b":true}]|] { matchHeaders = [matchContentTypeJson] } + it "can map a RAISE error code and message to a http status" $ + get "/rpc/raise_pt402" + `shouldRespondWith` [json|{ "hint": "Upgrade your plan", "details": "Quota exceeded" }|] + { matchStatus = 402 + , matchHeaders = [matchContentTypeJson] + } + + it "defaults to status 500 if RAISE code is PT not followed by a number" $ + get "/rpc/raise_bad_pt" `shouldRespondWith` 500 + context "only for POST rpc" $ do context "expects a single json object" $ do it "does not expand posted json into parameters" $ diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 056e7f6d8..0227345db 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -1292,6 +1292,20 @@ $$ language sql; create function test.many_inout_params(INOUT num int, INOUT str text, INOUT b bool DEFAULT true) AS $$ select num, str, b; $$ language sql; + +create or replace function test.raise_pt402() returns void as $$ +begin + raise sqlstate 'PT402' using message = 'Payment Required', + detail = 'Quota exceeded', + hint = 'Upgrade your plan'; +end; +$$ language plpgsql; + +create or replace function test.raise_bad_pt() returns void as $$ +begin + raise sqlstate 'PT40A' using message = 'Wrong'; +end; +$$ language plpgsql; -- -- PostgreSQL database dump complete --