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:
committed by
Wolfgang Walther
parent
6d506df6f3
commit
8a21a9c34e
@@ -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
|
- #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
|
- #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
|
- #3160, Fix using select= query parameter for custom media type handlers - @wolfgangwalther
|
||||||
|
- #3237, Dump media handlers and timezones with --dump-schema - @wolfgangwalther
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ let
|
|||||||
ps.pyyaml
|
ps.pyyaml
|
||||||
ps.requests
|
ps.requests
|
||||||
ps.requests-unixsocket
|
ps.requests-unixsocket
|
||||||
|
ps.syrupy
|
||||||
]);
|
]);
|
||||||
|
|
||||||
testIO =
|
testIO =
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ fromHeaders allowTxDbOverride acceptedTzNames headers =
|
|||||||
listStripPrefix prefix prefList = listToMaybe $ mapMaybe (BS.stripPrefix prefix) prefList
|
listStripPrefix prefix prefList = listToMaybe $ mapMaybe (BS.stripPrefix prefix) prefList
|
||||||
|
|
||||||
timezonePref = listStripPrefix "timezone=" prefs
|
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
|
maxAffectedPref = listStripPrefix "max-affected=" prefs >>= readMaybe . BS.unpack
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import Protolude
|
|||||||
|
|
||||||
type RoleSettings = (HM.HashMap ByteString (HM.HashMap ByteString ByteString))
|
type RoleSettings = (HM.HashMap ByteString (HM.HashMap ByteString ByteString))
|
||||||
type RoleIsolationLvl = HM.HashMap ByteString SQL.IsolationLevel
|
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 :: (Eq a, IsString a) => a -> SQL.IsolationLevel
|
||||||
toIsolationLevel a = case a of
|
toIsolationLevel a = case a of
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE DeriveAnyClass #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ module PostgREST.MediaType
|
|||||||
, decodeMediaType
|
, decodeMediaType
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import qualified Data.Aeson as JSON
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Internal as BS (c2w)
|
import qualified Data.ByteString.Internal as BS (c2w)
|
||||||
|
|
||||||
@@ -28,23 +30,23 @@ data MediaType
|
|||||||
| MTUrlEncoded
|
| MTUrlEncoded
|
||||||
| MTOctetStream
|
| MTOctetStream
|
||||||
| MTAny
|
| MTAny
|
||||||
| MTOther ByteString
|
| MTOther Text
|
||||||
-- vendored media types
|
-- vendored media types
|
||||||
| MTVndArrayJSONStrip
|
| MTVndArrayJSONStrip
|
||||||
| MTVndSingularJSON Bool
|
| MTVndSingularJSON Bool
|
||||||
-- TODO MTVndPlan should only have its options as [Text]. Its ResultAggregate should have the typed attributes.
|
-- TODO MTVndPlan should only have its options as [Text]. Its ResultAggregate should have the typed attributes.
|
||||||
| MTVndPlan MediaType MTVndPlanFormat [MTVndPlanOption]
|
| MTVndPlan MediaType MTVndPlanFormat [MTVndPlanOption]
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic, JSON.ToJSON)
|
||||||
instance Hashable MediaType
|
instance Hashable MediaType
|
||||||
|
|
||||||
data MTVndPlanOption
|
data MTVndPlanOption
|
||||||
= PlanAnalyze | PlanVerbose | PlanSettings | PlanBuffers | PlanWAL
|
= PlanAnalyze | PlanVerbose | PlanSettings | PlanBuffers | PlanWAL
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic, JSON.ToJSON)
|
||||||
instance Hashable MTVndPlanOption
|
instance Hashable MTVndPlanOption
|
||||||
|
|
||||||
data MTVndPlanFormat
|
data MTVndPlanFormat
|
||||||
= PlanJSON | PlanText
|
= PlanJSON | PlanText
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic, JSON.ToJSON)
|
||||||
instance Hashable MTVndPlanFormat
|
instance Hashable MTVndPlanFormat
|
||||||
|
|
||||||
-- | Convert MediaType to a Content-Type HTTP Header
|
-- | 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 MTUrlEncoded = "application/x-www-form-urlencoded"
|
||||||
toMime MTOctetStream = "application/octet-stream"
|
toMime MTOctetStream = "application/octet-stream"
|
||||||
toMime MTAny = "*/*"
|
toMime MTAny = "*/*"
|
||||||
toMime (MTOther ct) = ct
|
toMime (MTOther ct) = encodeUtf8 ct
|
||||||
toMime (MTVndPlan mt fmt opts) =
|
toMime (MTVndPlan mt fmt opts) =
|
||||||
"application/vnd.pgrst.plan+" <> toMimePlanFormat fmt <>
|
"application/vnd.pgrst.plan+" <> toMimePlanFormat fmt <>
|
||||||
("; for=\"" <> toMime mt <> "\"") <>
|
("; for=\"" <> toMime mt <> "\"") <>
|
||||||
@@ -132,7 +134,7 @@ decodeMediaType mt =
|
|||||||
"application/vnd.pgrst.array+json":rest -> checkArrayNullStrip rest
|
"application/vnd.pgrst.array+json":rest -> checkArrayNullStrip rest
|
||||||
"application/vnd.pgrst.array":rest -> checkArrayNullStrip rest
|
"application/vnd.pgrst.array":rest -> checkArrayNullStrip rest
|
||||||
"*/*":_ -> MTAny
|
"*/*":_ -> MTAny
|
||||||
other:_ -> MTOther other
|
other:_ -> MTOther $ decodeUtf8 other
|
||||||
_ -> MTAny
|
_ -> MTAny
|
||||||
where
|
where
|
||||||
checkArrayNullStrip ["nulls=stripped"] = MTVndArrayJSONStrip
|
checkArrayNullStrip ["nulls=stripped"] = MTVndArrayJSONStrip
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import Control.Monad.Extra (whenJust)
|
|||||||
|
|
||||||
import Data.Aeson ((.=))
|
import Data.Aeson ((.=))
|
||||||
import qualified Data.Aeson as JSON
|
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 as HM
|
||||||
import qualified Data.HashMap.Strict.InsOrd as HMI
|
import qualified Data.HashMap.Strict.InsOrd as HMI
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
@@ -86,13 +85,13 @@ data SchemaCache = SchemaCache
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance JSON.ToJSON SchemaCache where
|
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
|
"dbTables" .= JSON.toJSON tabs
|
||||||
, "dbRelationships" .= JSON.toJSON rels
|
, "dbRelationships" .= JSON.toJSON rels
|
||||||
, "dbRoutines" .= JSON.toJSON routs
|
, "dbRoutines" .= JSON.toJSON routs
|
||||||
, "dbRepresentations" .= JSON.toJSON reps
|
, "dbRepresentations" .= JSON.toJSON reps
|
||||||
, "dbMediaHandlers" .= JSON.emptyArray
|
, "dbMediaHandlers" .= JSON.toJSON hdlers
|
||||||
, "dbTimezones" .= JSON.emptyArray
|
, "dbTimezones" .= JSON.toJSON tzs
|
||||||
]
|
]
|
||||||
|
|
||||||
showSummary :: SchemaCache -> Text
|
showSummary :: SchemaCache -> Text
|
||||||
@@ -1222,7 +1221,7 @@ timezones = SQL.Statement sql HE.noParams decodeTimezones
|
|||||||
where
|
where
|
||||||
sql = "SELECT name FROM pg_timezone_names"
|
sql = "SELECT name FROM pg_timezone_names"
|
||||||
decodeTimezones :: HD.Result TimezoneNames
|
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.Value a -> HE.Params a
|
||||||
param = HE.param . HE.nonNullable
|
param = HE.param . HE.nonNullable
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ data MediaHandler
|
|||||||
-- custom
|
-- custom
|
||||||
| CustomFunc QualifiedIdentifier RelIdentifier
|
| CustomFunc QualifiedIdentifier RelIdentifier
|
||||||
| NoAgg
|
| NoAgg
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show, Generic, JSON.ToJSON)
|
||||||
|
|
||||||
funcReturnsSingle :: Routine -> Bool
|
funcReturnsSingle :: Routine -> Bool
|
||||||
funcReturnsSingle proc = case proc of
|
funcReturnsSingle proc = case proc of
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,11 +21,21 @@ import jwt
|
|||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
import requests_unixsocket
|
import requests_unixsocket
|
||||||
|
from syrupy.extensions.json import SingleFileSnapshotExtension
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from config import *
|
from config import *
|
||||||
|
|
||||||
|
|
||||||
|
class YamlSnapshotExtension(SingleFileSnapshotExtension):
|
||||||
|
_file_extension = "yaml"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def snapshot_yaml(snapshot):
|
||||||
|
return snapshot.use_extension(YamlSnapshotExtension)
|
||||||
|
|
||||||
|
|
||||||
def itemgetter(*items):
|
def itemgetter(*items):
|
||||||
"operator.itemgetter with None as fallback when key does not exist"
|
"operator.itemgetter with None as fallback when key does not exist"
|
||||||
if len(items) == 1:
|
if len(items) == 1:
|
||||||
@@ -225,3 +235,12 @@ def test_invalid_openapi_mode(invalidopenapimodes, defaultenv):
|
|||||||
for line in dump.split("\n"):
|
for line in dump.split("\n"):
|
||||||
if line.startswith("openapi-mode"):
|
if line.startswith("openapi-mode"):
|
||||||
print(line)
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user