Compare commits

..
5 Commits
Author SHA1 Message Date
steve-chavez 272e2e7535 bump version to 13.0.6 2025-08-30 13:55:34 -05:00
Laurence IslaandWolfgang Walther ea153523d1 fix: empty enum in 'preferParams' openAPI parameter 2025-08-30 08:31:21 +02:00
Taimoor ZaeemandWolfgang Walther bf0a1173b5 fix: format of IPv6 address logged at PostgREST startup
The IPv6 address logged at the startup like `::1:80` was
wrong because the port isn't clearly separated. This commit
corrects it, now logging as `[::1]:80`.

This is done in accordance to RFC 3986. In short, we did this
have a clear separation between the port and host because
the components of an IPv6 are separated with the ':' character.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
2025-08-29 22:31:01 +02:00
renovate[bot]andWolfgang Walther 3a28968f3c chore(deps): update nixbuild/nix-quick-install-action action to v33 2025-08-25 15:08:25 +00:00
Laurence IslaandWolfgang Walther 86aac1ead5 fix: logging the Haskell type instead of the listener error message directly
Previously: Just "connection error..."
Now: connection error...
2025-08-25 10:25:48 +00:00
9 changed files with 71 additions and 19 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ inputs:
runs:
using: composite
steps:
- uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v32
- uses: nixbuild/nix-quick-install-action@1f095fee853b33114486cfdeae62fa099cda35a9 # v33
with:
nix_conf: |-
always-allow-substitutes = true
+8
View File
@@ -5,6 +5,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
## [13.0.6] - 2025-08-30
### Fixed
- Fix logging the Haskell type instead of the listener error message directly by @laurenceisla in #3588
- Fix format of `IPv6` address logged at PostgREST startup by @taimoorzaeem in #4291
- Fix empty enum in `preferParams` OpenAPI parameter by @laurenceisla in #4292
## [13.0.5] - 2025-08-24
### Fixed
+1 -1
View File
@@ -1,5 +1,5 @@
name: postgrest
version: 13.0.5
version: 13.0.6
synopsis: REST API for any Postgres database
description: Reads the schema of a PostgreSQL database and creates RESTful routes
for tables, views, and functions, supporting all HTTP methods that security
+5 -1
View File
@@ -13,5 +13,9 @@ resolveHost sock = do
sn <- NS.getSocketName sock
case sn of
NS.SockAddrInet _ hostAddr -> pure $ Just $ fromString $ show $ fromHostAddress hostAddr
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ show $ fromHostAddress6 hostAddr6
-- The IPv6 addresses are wrapped in [] brackets. This is done in accordance
-- to RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2).
-- In short, we did this to have a clear separation between the port and host
-- because the components of an IPv6 are separated with the ':' character.
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ "[" ++ show (fromHostAddress6 hostAddr6) ++ "]"
_ -> pure Nothing
+10 -10
View File
@@ -108,11 +108,8 @@ observationMessage = \case
DBListenStart channel -> do
"Listening for database notifications on the " <> show channel <> " channel"
DBListenFail channel listenErr ->
"Failed listening for database notifications on the " <> show channel <> " channel. " <> (
case listenErr of
Left err -> show err
Right err -> showListenerError err
)
"Failed listening for database notifications on the " <> show channel <> " channel. " <>
either showListenerConnError showListenerException listenErr
DBListenRetry delay ->
"Retrying listening for database notifications in " <> (show delay::Text) <> " seconds..."
DBListenerGotSCacheMsg channel ->
@@ -157,8 +154,11 @@ observationMessage = \case
jsonMessage err = T.decodeUtf8 . LBS.toStrict . Error.errorPayload $ Error.PgError False err
showListenerError :: Either SomeException () -> Text
showListenerError (Right _) = "Failed getting notifications" -- should not happen as the listener will never finish (hasql-notifications uses `forever` internally) with a Right result
showListenerError (Left e) =
let showOnSingleLine txt = T.intercalate " " $ T.filter (/= '\t') <$> T.lines txt in -- the errors from hasql-notifications come intercalated with "\t\n"
showOnSingleLine $ show e
showOnSingleLine txt = T.intercalate " " $ T.filter (/= '\t') <$> T.lines txt -- the errors from hasql-notifications come intercalated with "\t\n"
showListenerConnError :: SQL.ConnectionError -> Text
showListenerConnError = maybe "Connection error" (showOnSingleLine . T.decodeUtf8)
showListenerException :: Either SomeException () -> Text
showListenerException (Right _) = "Failed getting notifications" -- should not happen as the listener will never finish (hasql-notifications uses `forever` internally) with a Right result
showListenerException (Left e) = showOnSingleLine $ show e
+2 -1
View File
@@ -171,8 +171,9 @@ makePreferParam ts =
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamHeader
& type_ ?~ SwaggerString
& enum_ .~ JSON.decode (JSON.encode $ foldl (<>) [] (val <$> ts)))
& enum_ .~ if null enu then Nothing else JSON.decode (JSON.encode enu))
where
enu = foldl (<>) [] (val <$> ts)
val :: Text -> [Text]
val = \case
"count" -> ["count=none"]
+13 -2
View File
@@ -96,7 +96,9 @@ def run(
if port:
env["PGRST_SERVER_PORT"] = str(port)
env["PGRST_SERVER_HOST"] = host or "localhost"
baseurl = f"http://localhost:{port}"
# When constructing IPv6 address, host address should be bracketed like [host]
apihost = f"[{host}]" if host and is_ipv6(host) else "localhost"
baseurl = f"http://{apihost}:{port}"
else:
socketfile = pathlib.Path(tmpdir) / "postgrest.sock"
env["PGRST_SERVER_UNIX_SOCKET"] = str(socketfile)
@@ -104,7 +106,8 @@ def run(
adminport = freeport(port)
env["PGRST_ADMIN_SERVER_PORT"] = str(adminport)
adminurl = f"http://localhost:{adminport}"
adminhost = f"[{host}]" if host and is_ipv6(host) else "localhost"
adminurl = f"http://{adminhost}:{adminport}"
command = [POSTGREST_BIN]
env["HPCTIXFILE"] = hpctixfile()
@@ -218,3 +221,11 @@ def sleep_pool_connection(url, seconds):
session.get(url + f"/rpc/sleep?seconds={seconds}", timeout=0.1)
except requests.exceptions.ReadTimeout:
pass
def is_ipv6(addr):
try:
socket.inet_pton(socket.AF_INET6, addr)
return True
except OSError:
return False
+24 -3
View File
@@ -1355,9 +1355,9 @@ def test_log_postgrest_version(defaultenv):
assert "Starting PostgREST %s..." % version in output[0]
def test_log_postgrest_host_and_port(defaultenv):
@pytest.mark.parametrize("host", ["127.0.0.1", "::1"])
def test_log_postgrest_host_and_port(host, defaultenv):
"PostgREST should output the host and port it is bound to."
host = "127.0.0.1"
port = freeport()
with run(
@@ -1365,7 +1365,10 @@ def test_log_postgrest_host_and_port(defaultenv):
) as postgrest:
output = postgrest.read_stdout(nlines=10)
assert f"API server listening on {host}:{port}" in output[2] # output-sensitive
if is_ipv6(host): # IPv6
assert f"API server listening on [{host}]:{port}" in output[2]
else: # IPv4
assert f"API server listening on {host}:{port}" in output[2]
def test_succeed_w_role_having_superuser_settings(defaultenv):
@@ -1930,3 +1933,21 @@ def test_schema_cache_error_observation(defaultenv):
"Failed to load the schema cache using db-schemas=public and db-extra-search-path=x"
in output[7]
)
def test_log_listener_connection_errors(defaultenv):
"The logs should show the listener connection error message in a single line"
env = {
**defaultenv,
"PGHOST": "no_host",
"PGRST_DB_CHANNEL_ENABLED": "true",
}
with run(env=env, no_startup_stdout=False, wait_for_readiness=False) as postgrest:
output = postgrest.read_stdout(nlines=5)
assert any(
'Failed listening for database notifications on the "pgrst" channel. could not translate host name "no_host" to address:'
in line
for line in output
)
+7
View File
@@ -1085,6 +1085,13 @@ spec = describe "OpenAPI" $ do
immutableGet `shouldNotBe` Nothing
immutablePost `shouldNotBe` Nothing
it "does not include empty enum in the preferParams parameter" $ do
r <- simpleBody <$> get "/"
let preferParams = r ^? key "parameters" . key "preferParams" . key "enum"
liftIO $ do
preferParams `shouldBe` Nothing
describe "Security" $
it "does not include security or security definitions by default" $ do
r <- simpleBody <$> get "/"