fix: Dump media handlers and timezones with --dump-schema

Those were left out of the schema dump when the features were introduced, probably
because ByteString doesn't have a toJSON instance. Changing the type to Text solves
this easily.

Resolves #3237
This commit is contained in:
Wolfgang Walther
2024-02-20 18:44:17 +01:00
committed by Wolfgang Walther
parent 6d506df6f3
commit 8a21a9c34e
9 changed files with 1803 additions and 14 deletions
+1
View File
@@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #3205, Fix wrong subquery error returning a status of 400 Bad Request - @steve-chavez
- #3224, Return status code 406 for non-accepted media type instead of code 415 - @wolfgangwalther
- #3160, Fix using select= query parameter for custom media type handlers - @wolfgangwalther
- #3237, Dump media handlers and timezones with --dump-schema - @wolfgangwalther
### Deprecated
+1
View File
@@ -67,6 +67,7 @@ let
ps.pyyaml
ps.requests
ps.requests-unixsocket
ps.syrupy
]);
testIO =
+1 -1
View File
@@ -163,7 +163,7 @@ fromHeaders allowTxDbOverride acceptedTzNames headers =
listStripPrefix prefix prefList = listToMaybe $ mapMaybe (BS.stripPrefix prefix) prefList
timezonePref = listStripPrefix "timezone=" prefs
isTimezonePrefAccepted = (S.member <$> timezonePref <*> pure acceptedTzNames) == Just True
isTimezonePrefAccepted = (S.member <$> (decodeUtf8 <$> timezonePref) <*> pure acceptedTzNames) == Just True
maxAffectedPref = listStripPrefix "max-affected=" prefs >>= readMaybe . BS.unpack
+1 -1
View File
@@ -30,7 +30,7 @@ import Protolude
type RoleSettings = (HM.HashMap ByteString (HM.HashMap ByteString ByteString))
type RoleIsolationLvl = HM.HashMap ByteString SQL.IsolationLevel
type TimezoneNames = Set ByteString -- cache timezone names for prefer timezone=
type TimezoneNames = Set Text -- cache timezone names for prefer timezone=
toIsolationLevel :: (Eq a, IsString a) => a -> SQL.IsolationLevel
toIsolationLevel a = case a of
+8 -6
View File
@@ -1,3 +1,4 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
@@ -10,6 +11,7 @@ module PostgREST.MediaType
, decodeMediaType
) where
import qualified Data.Aeson as JSON
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BS (c2w)
@@ -28,23 +30,23 @@ data MediaType
| MTUrlEncoded
| MTOctetStream
| MTAny
| MTOther ByteString
| MTOther Text
-- vendored media types
| MTVndArrayJSONStrip
| MTVndSingularJSON Bool
-- TODO MTVndPlan should only have its options as [Text]. Its ResultAggregate should have the typed attributes.
| MTVndPlan MediaType MTVndPlanFormat [MTVndPlanOption]
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, JSON.ToJSON)
instance Hashable MediaType
data MTVndPlanOption
= PlanAnalyze | PlanVerbose | PlanSettings | PlanBuffers | PlanWAL
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, JSON.ToJSON)
instance Hashable MTVndPlanOption
data MTVndPlanFormat
= PlanJSON | PlanText
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, JSON.ToJSON)
instance Hashable MTVndPlanFormat
-- | Convert MediaType to a Content-Type HTTP Header
@@ -70,7 +72,7 @@ toMime (MTVndSingularJSON False) = "application/vnd.pgrst.object+json"
toMime MTUrlEncoded = "application/x-www-form-urlencoded"
toMime MTOctetStream = "application/octet-stream"
toMime MTAny = "*/*"
toMime (MTOther ct) = ct
toMime (MTOther ct) = encodeUtf8 ct
toMime (MTVndPlan mt fmt opts) =
"application/vnd.pgrst.plan+" <> toMimePlanFormat fmt <>
("; for=\"" <> toMime mt <> "\"") <>
@@ -132,7 +134,7 @@ decodeMediaType mt =
"application/vnd.pgrst.array+json":rest -> checkArrayNullStrip rest
"application/vnd.pgrst.array":rest -> checkArrayNullStrip rest
"*/*":_ -> MTAny
other:_ -> MTOther other
other:_ -> MTOther $ decodeUtf8 other
_ -> MTAny
where
checkArrayNullStrip ["nulls=stripped"] = MTVndArrayJSONStrip
+4 -5
View File
@@ -31,7 +31,6 @@ import Control.Monad.Extra (whenJust)
import Data.Aeson ((.=))
import qualified Data.Aeson as JSON
import qualified Data.Aeson.Types as JSON
import qualified Data.HashMap.Strict as HM
import qualified Data.HashMap.Strict.InsOrd as HMI
import qualified Data.Set as S
@@ -86,13 +85,13 @@ data SchemaCache = SchemaCache
}
instance JSON.ToJSON SchemaCache where
toJSON (SchemaCache tabs rels routs reps _ _) = JSON.object [
toJSON (SchemaCache tabs rels routs reps hdlers tzs) = JSON.object [
"dbTables" .= JSON.toJSON tabs
, "dbRelationships" .= JSON.toJSON rels
, "dbRoutines" .= JSON.toJSON routs
, "dbRepresentations" .= JSON.toJSON reps
, "dbMediaHandlers" .= JSON.emptyArray
, "dbTimezones" .= JSON.emptyArray
, "dbMediaHandlers" .= JSON.toJSON hdlers
, "dbTimezones" .= JSON.toJSON tzs
]
showSummary :: SchemaCache -> Text
@@ -1222,7 +1221,7 @@ timezones = SQL.Statement sql HE.noParams decodeTimezones
where
sql = "SELECT name FROM pg_timezone_names"
decodeTimezones :: HD.Result TimezoneNames
decodeTimezones = S.fromList . map encodeUtf8 <$> HD.rowList (column HD.text)
decodeTimezones = S.fromList <$> HD.rowList (column HD.text)
param :: HE.Value a -> HE.Params a
param = HE.param . HE.nonNullable
+1 -1
View File
@@ -110,7 +110,7 @@ data MediaHandler
-- custom
| CustomFunc QualifiedIdentifier RelIdentifier
| NoAgg
deriving (Eq, Show)
deriving (Eq, Show, Generic, JSON.ToJSON)
funcReturnsSingle :: Routine -> Bool
funcReturnsSingle proc = case proc of
File diff suppressed because it is too large Load Diff
+19
View File
@@ -21,11 +21,21 @@ import jwt
import pytest
import requests
import requests_unixsocket
from syrupy.extensions.json import SingleFileSnapshotExtension
import yaml
from config import *
class YamlSnapshotExtension(SingleFileSnapshotExtension):
_file_extension = "yaml"
@pytest.fixture
def snapshot_yaml(snapshot):
return snapshot.use_extension(YamlSnapshotExtension)
def itemgetter(*items):
"operator.itemgetter with None as fallback when key does not exist"
if len(items) == 1:
@@ -225,3 +235,12 @@ def test_invalid_openapi_mode(invalidopenapimodes, defaultenv):
for line in dump.split("\n"):
if line.startswith("openapi-mode"):
print(line)
# If this test is failing, run pytest with --snapshot-update.
def test_schema_cache_snapshot(baseenv, snapshot_yaml):
"Dump of schema cache should match snapshot."
schema_cache = yaml.load(cli(["--dump-schema"], env=baseenv), Loader=yaml.Loader)
formatted = yaml.dump(schema_cache, encoding="utf8", allow_unicode=True)
assert formatted == snapshot_yaml