add: config client-error-verbosity to set error verbosity
Set error verbosity using this config. The verbosity can be set to `verbose` or `minimal` for client error responses. This only affects client side HTTP responses, server side logs are not affected by this config. Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
This commit is contained in:
committed by
Steve Chavez
parent
2edc44c352
commit
83dc082acf
@@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. From versio
|
||||
- Log host, port and pg version of listener database connection by @mkleczek in #4617 #4618
|
||||
- Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957
|
||||
+ Removed unnecessary double count when building the `Content-Range`.
|
||||
- Add config `client_error_verbosity` to customize error verbosity by @taimoorzaeem in #4088
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -195,6 +195,53 @@ app.settings.*
|
||||
|
||||
The :code:`current_setting` function has `an optional boolean second <https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-SET>`_ argument to avoid it from raising an error if the value was not defined. Default values to :code:`app.settings` can then be given by combining this argument with :code:`coalesce` and :code:`nullif` : :code:`coalesce(nullif(current_setting('app.settings.my_custom_variable', true), ''), 'default value')`. The use of :code:`nullif` is necessary because if set in a transaction, the setting is sometimes not "rolled back" to :code:`null`. See also :ref:`this section <guc_req_headers_cookies_claims>` for more information on this behaviour.
|
||||
|
||||
.. _client-error-verbosity:
|
||||
|
||||
client-error-verbosity
|
||||
----------------------
|
||||
|
||||
=============== =======================
|
||||
**Type** String
|
||||
**Default** verbose
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_CLIENT_ERROR_VERBOSITY
|
||||
**In-Database** pgrst.client_error_verbosity
|
||||
=============== =======================
|
||||
|
||||
Specifies the verbosity of PostgREST errors.
|
||||
|
||||
With ``verbose``, ``code``, ``message``, ``details`` and ``hint`` are returned.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
curl "localhost:3000/itemsxx"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache",
|
||||
"details": "Perhaps you meant the table 'public.items'",
|
||||
"hint": null
|
||||
}
|
||||
|
||||
With ``minimal``, just ``code`` and ``message`` are returned.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
curl "localhost:3000/itemsxx"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache"
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
This setting only affects client side error messages. Server side logs are not affected by this setting.
|
||||
|
||||
.. _db-aggregates-enabled:
|
||||
|
||||
db-aggregates-enabled
|
||||
|
||||
+23
-22
@@ -119,30 +119,31 @@ postgrest logLevel appState connWorker =
|
||||
Logger.middleware logLevel Auth.getRole $
|
||||
-- fromJust can be used, because the auth middleware will **always** add
|
||||
-- some AuthResult to the vault.
|
||||
\req respond -> case fromJust $ Auth.getResult req of
|
||||
Left err -> respond $ Error.errorResponseFor err
|
||||
Right authResult -> do
|
||||
appConf <- AppState.getConfig appState -- the config must be read again because it can reload
|
||||
maybeSchemaCache <- AppState.getSchemaCache appState
|
||||
\req respond -> do
|
||||
appConf@AppConfig{..} <- AppState.getConfig appState -- the config must be read again because it can reload
|
||||
case fromJust $ Auth.getResult req of
|
||||
Left err -> respond $ Error.errorResponseFor configClientErrorVerbosity err
|
||||
Right authResult -> do
|
||||
maybeSchemaCache <- AppState.getSchemaCache appState
|
||||
|
||||
let
|
||||
eitherResponse :: IO (Either Error Wai.Response)
|
||||
eitherResponse =
|
||||
runExceptT $ postgrestResponse appState appConf maybeSchemaCache authResult req
|
||||
let
|
||||
eitherResponse :: IO (Either Error Wai.Response)
|
||||
eitherResponse =
|
||||
runExceptT $ postgrestResponse appState appConf maybeSchemaCache authResult req
|
||||
|
||||
response <- either Error.errorResponseFor identity <$> eitherResponse
|
||||
-- Launch the connWorker when the connection is down. The postgrest
|
||||
-- function can respond successfully (with a stale schema cache) before
|
||||
-- the connWorker is done. However, when there's an empty schema cache
|
||||
-- postgrest responds with the error `PGRST002`; this means that the schema
|
||||
-- cache is still loading, so we don't launch the connWorker here because
|
||||
-- it would duplicate the loading process, e.g. https://github.com/PostgREST/postgrest/issues/3704
|
||||
-- TODO: this process may be unnecessary when the Listener is enabled. Revisit once https://github.com/PostgREST/postgrest/issues/1766 is done
|
||||
when (isServiceUnavailable response && isJust maybeSchemaCache) connWorker
|
||||
resp <- do
|
||||
delay <- AppState.getNextDelay appState
|
||||
return $ addRetryHint delay response
|
||||
respond resp
|
||||
response <- either (Error.errorResponseFor configClientErrorVerbosity) identity <$> eitherResponse
|
||||
-- Launch the connWorker when the connection is down. The postgrest
|
||||
-- function can respond successfully (with a stale schema cache) before
|
||||
-- the connWorker is done. However, when there's an empty schema cache
|
||||
-- postgrest responds with the error `PGRST002`; this means that the schema
|
||||
-- cache is still loading, so we don't launch the connWorker here because
|
||||
-- it would duplicate the loading process, e.g. https://github.com/PostgREST/postgrest/issues/3704
|
||||
-- TODO: this process may be unnecessary when the Listener is enabled. Revisit once https://github.com/PostgREST/postgrest/issues/1766 is done
|
||||
when (isServiceUnavailable response && isJust maybeSchemaCache) connWorker
|
||||
resp <- do
|
||||
delay <- AppState.getNextDelay appState
|
||||
return $ addRetryHint delay response
|
||||
respond resp
|
||||
|
||||
postgrestResponse
|
||||
:: AppState.AppState
|
||||
|
||||
+25
-1
@@ -29,6 +29,7 @@ module PostgREST.Config
|
||||
, addTargetSessionAttrs
|
||||
, exampleConfigFile
|
||||
, audMatchesCfg
|
||||
, Verbosity (..)
|
||||
) where
|
||||
|
||||
import qualified Data.Aeson as JSON
|
||||
@@ -73,6 +74,7 @@ audMatchesCfg = maybe (const True) (==) . configJwtAudience
|
||||
|
||||
data AppConfig = AppConfig
|
||||
{ configAppSettings :: [(Text, Text)]
|
||||
, configClientErrorVerbosity :: Verbosity
|
||||
, configDbAggregates :: Bool
|
||||
, configDbAnonRole :: Maybe BS.ByteString
|
||||
, configDbChannel :: Text
|
||||
@@ -134,6 +136,15 @@ dumpLogLevel = \case
|
||||
LogInfo -> "info"
|
||||
LogDebug -> "debug"
|
||||
|
||||
data Verbosity
|
||||
= Minimal
|
||||
| Verbose
|
||||
|
||||
dumpClientErrorVerbosity :: Verbosity -> Text
|
||||
dumpClientErrorVerbosity = \case
|
||||
Minimal -> "minimal"
|
||||
Verbose -> "verbose"
|
||||
|
||||
data OpenAPIMode = OAFollowPriv | OAIgnorePriv | OADisabled
|
||||
deriving Eq
|
||||
|
||||
@@ -150,7 +161,8 @@ toText conf =
|
||||
where
|
||||
-- apply conf to all pgrst settings
|
||||
pgrstSettings = (\(k, v) -> (k, v conf)) <$>
|
||||
[("db-aggregates-enabled", T.toLower . show . configDbAggregates)
|
||||
[("client-error-verbosity", q . dumpClientErrorVerbosity . configClientErrorVerbosity)
|
||||
,("db-aggregates-enabled", T.toLower . show . configDbAggregates)
|
||||
,("db-anon-role", q . T.decodeUtf8 . fromMaybe "" . configDbAnonRole)
|
||||
,("db-channel", q . configDbChannel)
|
||||
,("db-channel-enabled", T.toLower . show . configDbChannelEnabled)
|
||||
@@ -254,6 +266,7 @@ parser :: Maybe FilePath -> Environment -> [(Text, Text)] -> RoleSettings -> Rol
|
||||
parser optPath env dbSettings roleSettings roleIsolationLvl =
|
||||
AppConfig
|
||||
<$> parseAppSettings "app.settings"
|
||||
<*> parseErrorVerbosity "client-error-verbosity"
|
||||
<*> (fromMaybe False <$> optBool "db-aggregates-enabled")
|
||||
<*> (fmap encodeUtf8 <$> optString "db-anon-role")
|
||||
<*> (fromMaybe "pgrst" <$> optString "db-channel")
|
||||
@@ -310,6 +323,14 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
||||
<*> optInt "internal-schema-cache-load-sleep"
|
||||
<*> optInt "internal-schema-cache-relationship-load-sleep"
|
||||
where
|
||||
parseErrorVerbosity :: C.Key -> C.Parser C.Config Verbosity
|
||||
parseErrorVerbosity k =
|
||||
optString k >>= \case
|
||||
Nothing -> pure Verbose -- default
|
||||
Just "minimal" -> pure Minimal
|
||||
Just "verbose" -> pure Verbose
|
||||
Just _ -> fail "Invalid client-error-verbosity. Check your configuration."
|
||||
|
||||
parseAppSettings :: C.Key -> C.Parser C.Config [(Text, Text)]
|
||||
parseAppSettings key = addFromEnv . fmap (fmap coerceText) <$> C.subassocs key C.value
|
||||
where
|
||||
@@ -642,6 +663,9 @@ exampleConfigFile = S.unlines
|
||||
[ "## Admin server used for checks. It's disabled by default unless a port is specified."
|
||||
, "# admin-server-port = 3001"
|
||||
, ""
|
||||
, "# PostgREST error json verbosity config"
|
||||
, "# client-error-verbosity = \"verbose\""
|
||||
, ""
|
||||
, "## The database role to use when no client authentication is provided"
|
||||
, "# db-anon-role = \"anon\""
|
||||
, ""
|
||||
|
||||
@@ -46,6 +46,7 @@ dbSettingsNames :: [Text]
|
||||
dbSettingsNames =
|
||||
(prefix <>) <$>
|
||||
["db_aggregates_enabled"
|
||||
,"client_error_verbosity"
|
||||
,"db_anon_role"
|
||||
,"db_pre_config"
|
||||
,"db_extra_search_path"
|
||||
|
||||
+12
-7
@@ -42,6 +42,7 @@ import Network.HTTP.Types.Header (Header)
|
||||
import PostgREST.MediaType (MediaType (..))
|
||||
import qualified PostgREST.MediaType as MediaType
|
||||
|
||||
import PostgREST.Config (Verbosity (..))
|
||||
import PostgREST.SchemaCache (SchemaCache (SchemaCache, dbTablesFuzzyIndex))
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
|
||||
Schema)
|
||||
@@ -57,26 +58,30 @@ import PostgREST.Error.Types
|
||||
import Protolude
|
||||
|
||||
-- | Encode Error to ByteString
|
||||
errorPayload :: (ErrorBody a, ErrorHeaders a) => a -> LByteString
|
||||
errorPayload = JSON.encode . toJsonPgrstError
|
||||
errorPayload :: (ErrorBody a, ErrorHeaders a) => Verbosity -> a -> LByteString
|
||||
errorPayload verb = JSON.encode . toJsonPgrstError verb
|
||||
where
|
||||
toJsonPgrstError :: (ErrorBody a, ErrorHeaders a) => a -> JSON.Value
|
||||
toJsonPgrstError err = JSON.object [
|
||||
toJsonPgrstError :: (ErrorBody a, ErrorHeaders a) => Verbosity -> a -> JSON.Value
|
||||
toJsonPgrstError Verbose err = JSON.object [
|
||||
"code" .= code err
|
||||
, "message" .= message err
|
||||
, "details" .= details err
|
||||
, "hint" .= hint err
|
||||
]
|
||||
toJsonPgrstError Minimal err = JSON.object [
|
||||
"code" .= code err
|
||||
, "message" .= message err
|
||||
]
|
||||
|
||||
-- | Create HTTP response from Error
|
||||
errorResponseFor :: (ErrorBody a, ErrorHeaders a) => a -> Response
|
||||
errorResponseFor err =
|
||||
errorResponseFor :: (ErrorBody a, ErrorHeaders a) => Verbosity -> a -> Response
|
||||
errorResponseFor verb err =
|
||||
let
|
||||
baseHeader = MediaType.toContentType MTApplicationJSON
|
||||
cLHeader body = (,) "Content-Length" (show $ LBS.length body) :: Header
|
||||
pSHeader code' = ("Proxy-Status", "PostgREST; error=" <> T.encodeUtf8 code')
|
||||
in
|
||||
responseLBS (status err) (baseHeader : cLHeader (errorPayload err) : pSHeader (code err) : headers err) $ errorPayload err
|
||||
responseLBS (status err) (baseHeader : cLHeader (errorPayload verb err) : pSHeader (code err) : headers err) $ errorPayload verb err
|
||||
|
||||
class ErrorHeaders a where
|
||||
status :: a -> HTTP.Status
|
||||
|
||||
@@ -25,7 +25,6 @@ import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||
import PostgREST.SchemaCache.Relationship (Relationship (..),
|
||||
RelationshipsMap)
|
||||
import PostgREST.SchemaCache.Routine (Routine (..))
|
||||
|
||||
import Protolude
|
||||
|
||||
data Error
|
||||
|
||||
@@ -24,6 +24,7 @@ import qualified Hasql.Pool as SQL
|
||||
import qualified Hasql.Pool.Observation as SQL
|
||||
import Network.HTTP.Types.Status (Status)
|
||||
import Numeric (showFFloat)
|
||||
import PostgREST.Config (Verbosity (..))
|
||||
import PostgREST.Config.PgVersion
|
||||
import qualified PostgREST.Error as Error
|
||||
import PostgREST.Query (MainQuery)
|
||||
@@ -94,7 +95,7 @@ observationMessage = \case
|
||||
ExitDBFatalError ServerError08P01 usageErr ->
|
||||
"Connection poolers in statement mode are not supported." <> jsonMessage usageErr
|
||||
SchemaCacheEmptyObs ->
|
||||
T.decodeUtf8 . LBS.toStrict . Error.errorPayload $ Error.NoSchemaCacheError
|
||||
T.decodeUtf8 . LBS.toStrict . Error.errorPayload Verbose $ Error.NoSchemaCacheError
|
||||
SchemaCacheErrorObs dbSchemas extraPaths usageErr ->
|
||||
"Failed to load the schema cache using "
|
||||
<> "db-schemas=" <> T.intercalate "," (toList dbSchemas)
|
||||
@@ -167,7 +168,7 @@ observationMessage = \case
|
||||
showMillis :: Double -> Text
|
||||
showMillis x = toS $ showFFloat (Just 1) x ""
|
||||
|
||||
jsonMessage err = T.decodeUtf8 . LBS.toStrict . Error.errorPayload $ Error.PgError False err
|
||||
jsonMessage err = T.decodeUtf8 . LBS.toStrict . Error.errorPayload Verbose $ Error.PgError False err
|
||||
|
||||
|
||||
showListenerConnError :: SQL.ConnectionError -> Text
|
||||
|
||||
@@ -62,7 +62,7 @@ data PgrstResponse = PgrstResponse {
|
||||
|
||||
actionResponse :: DbResult -> ApiRequest -> (Text, Text) -> AppConfig -> SchemaCache -> Schema -> Bool -> Either Error.Error PgrstResponse
|
||||
|
||||
actionResponse (DbCrudResult plan@WrappedReadPlan{pMedia, wrHdrsOnly=headersOnly, crudQi=identifier} RSStandard{..}) ctxApiRequest@ApiRequest{..} _ _ _ _ _ = do
|
||||
actionResponse (DbCrudResult plan@WrappedReadPlan{pMedia, wrHdrsOnly=headersOnly, crudQi=identifier} RSStandard{..}) ctxApiRequest@ApiRequest{..} _ AppConfig{..} _ _ _ = do
|
||||
let
|
||||
(status, contentRange) = RangeQuery.rangeStatusHeader iTopLevelRange rsQueryTotal rsTableTotal
|
||||
cLHeader = if headersOnly then mempty else [ contentLengthHeader bod ]
|
||||
@@ -79,7 +79,7 @@ actionResponse (DbCrudResult plan@WrappedReadPlan{pMedia, wrHdrsOnly=headersOnly
|
||||
++ cLHeader
|
||||
++ contentTypeHeaders pMedia ctxApiRequest
|
||||
++ prefHeader
|
||||
bod | status == HTTP.status416 = Error.errorPayload $ Error.ApiRequestErr $ Error.InvalidRange $
|
||||
bod | status == HTTP.status416 = Error.errorPayload configClientErrorVerbosity $ Error.ApiRequestErr $ Error.InvalidRange $
|
||||
Error.OutOfBounds (show $ RangeQuery.rangeOffset iTopLevelRange) (maybe "0" show rsTableTotal)
|
||||
| headersOnly = mempty
|
||||
| otherwise = LBS.fromStrict rsBody
|
||||
@@ -178,12 +178,12 @@ actionResponse (DbCrudResult plan@MutateReadPlan{mrMutation=MutationDelete, pMed
|
||||
|
||||
Right $ PgrstResponse ovStatus ovHeaders body
|
||||
|
||||
actionResponse (DbCrudResult plan@CallReadPlan{pMedia, crInvMthd=invMethod, crProc=proc} RSStandard {..}) ctxApiRequest@ApiRequest{..} _ _ _ _ _ = do
|
||||
actionResponse (DbCrudResult plan@CallReadPlan{pMedia, crInvMthd=invMethod, crProc=proc} RSStandard {..}) ctxApiRequest@ApiRequest{..} _ AppConfig{..} _ _ _ = do
|
||||
let
|
||||
(status, contentRange) =
|
||||
RangeQuery.rangeStatusHeader iTopLevelRange rsQueryTotal rsTableTotal
|
||||
rsOrErrBody = if status == HTTP.status416
|
||||
then Error.errorPayload $ Error.ApiRequestErr $ Error.InvalidRange
|
||||
then Error.errorPayload configClientErrorVerbosity $ Error.ApiRequestErr $ Error.InvalidRange
|
||||
$ Error.OutOfBounds (show $ RangeQuery.rangeOffset iTopLevelRange) (maybe "0" show rsTableTotal)
|
||||
else LBS.fromStrict rsBody
|
||||
isHeadMethod = invMethod == InvRead True
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "minimal"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = "pre_config_role"
|
||||
db-channel = "postgrest"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "minimal"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = "anonymous"
|
||||
db-channel = "postgrest"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "minimal"
|
||||
db-aggregates-enabled = true
|
||||
db-anon-role = "root"
|
||||
db-channel = "postgrest"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "verbose"
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = ""
|
||||
db-channel = "pgrst"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
PGRST_APP_SETTINGS_test2: test
|
||||
PGRST_APP_SETTINGS_test: test
|
||||
PGRST_CLIENT_ERROR_VERBOSITY: minimal
|
||||
PGRST_DB_AGGREGATES_ENABLED: true
|
||||
PGRST_DB_ANON_ROLE: root
|
||||
PGRST_DB_CHANNEL: postgrest
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
client-error-verbosity = "minimal"
|
||||
db-aggregates-enabled = true
|
||||
db-anon-role = "root"
|
||||
db-channel = "postgrest"
|
||||
|
||||
@@ -2,6 +2,7 @@ CREATE ROLE db_config_authenticator LOGIN NOINHERIT;
|
||||
|
||||
-- reloadable config options
|
||||
-- these settings will override the values in configs/no-defaults.config, so they must be different
|
||||
ALTER ROLE db_config_authenticator SET pgrst.client_error_verbosity = 'minimal';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_aggregates_enabled = 'false';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_anon_role = 'anonymous';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.db_extra_search_path = 'public, extensions';
|
||||
@@ -56,6 +57,7 @@ ALTER ROLE db_config_authenticator SET pgrst.server_unix_socket_mode = 'ignored'
|
||||
-- other authenticator reloadable config options
|
||||
-- these settings will override the values in configs/no-defaults.config, so they must be different
|
||||
CREATE ROLE other_authenticator LOGIN NOINHERIT;
|
||||
ALTER ROLE other_authenticator SET pgrst.client_error_verbosity = 'minimal';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_aggregates_enabled = 'false';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100';
|
||||
|
||||
@@ -286,6 +286,17 @@ def test_jwt_secret_min_length(defaultenv):
|
||||
assert "The JWT secret must be at least 32 characters long." in error
|
||||
|
||||
|
||||
def test_invalid_client_error_verbosity(defaultenv):
|
||||
"Given an invalid value for client-error-verbosity, Postgrest should exit with a non-zero exit code."
|
||||
env = {
|
||||
**defaultenv,
|
||||
"PGRST_CLIENT_ERROR_VERBOSITY": "invalid",
|
||||
}
|
||||
|
||||
error = cli(["--dump-config"], env=env, expect_error=True)
|
||||
assert "Invalid client-error-verbosity. Check your configuration." in error
|
||||
|
||||
|
||||
@pytest.mark.parametrize("restricted_schema", FIXTURES["restrictedschemas"])
|
||||
def test_restricted_db_schemas(restricted_schema, defaultenv):
|
||||
"Should print error when db-schemas config contain pg_catalog or information_schema"
|
||||
|
||||
@@ -1757,3 +1757,35 @@ def test_server_timing_transaction_duration(defaultenv, metapostgrest):
|
||||
]
|
||||
|
||||
assert 2000 <= response_dur < 3000
|
||||
|
||||
|
||||
def test_client_error_verbosity_config(defaultenv):
|
||||
"Test PostgREST errors with different error verbosity settings"
|
||||
|
||||
env = {
|
||||
**defaultenv,
|
||||
"PGRST_CLIENT_ERROR_VERBOSITY": "minimal", # hide details and hint
|
||||
}
|
||||
|
||||
with run(env=env) as postgrest:
|
||||
response = postgrest.session.get("/itemsxx")
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache",
|
||||
}
|
||||
|
||||
env = {
|
||||
**defaultenv,
|
||||
"PGRST_CLIENT_ERROR_VERBOSITY": "verbose",
|
||||
}
|
||||
|
||||
with run(env=env) as postgrest:
|
||||
response = postgrest.session.get("/itemsxx")
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache",
|
||||
"details": None,
|
||||
"hint": "Perhaps you meant the table 'public.items'",
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import PostgREST.Config (AppConfig (..),
|
||||
JSPathExp (..),
|
||||
LogLevel (..),
|
||||
OpenAPIMode (..),
|
||||
parseSecret)
|
||||
Verbosity (..), parseSecret)
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||
import Prometheus (Counter, getCounter)
|
||||
import Protolude hiding (get, toS)
|
||||
@@ -121,6 +121,7 @@ baseCfg :: AppConfig
|
||||
baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
||||
AppConfig {
|
||||
configAppSettings = [ ("app.settings.app_host", "localhost") , ("app.settings.external_api_secret", "0123456789abcdef") ]
|
||||
, configClientErrorVerbosity = Verbose
|
||||
, configDbAggregates = False
|
||||
, configDbAnonRole = Just "postgrest_test_anonymous"
|
||||
, configDbChannel = mempty
|
||||
|
||||
Reference in New Issue
Block a user