Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
272e2e7535 | ||
|
|
ea153523d1 | ||
|
|
bf0a1173b5 | ||
|
|
3a28968f3c | ||
|
|
86aac1ead5 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 "/"
|
||||
|
||||
Reference in New Issue
Block a user