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
|
||||
- #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
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ let
|
||||
ps.pyyaml
|
||||
ps.requests
|
||||
ps.requests-unixsocket
|
||||
ps.syrupy
|
||||
]);
|
||||
|
||||
testIO =
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user