break: remove binary field logic, raw-media-types
BREAKING CHANGE Can be done later with custom media types
This commit is contained in:
committed by
Steve Chavez
parent
6920a88dc0
commit
14d030b96c
@@ -86,6 +86,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
+ The `/table?select=*,other!fk(*)` must be used to disambiguate
|
||||
+ The server aids in choosing the `!fk` by sending a `hint` on the error whenever an ambiguous request happens.
|
||||
|
||||
### Changed
|
||||
|
||||
- #1462, #1548, Removed [raw-media-types config](https://postgrest.org/en/v11.1/references/configuration.html#raw-media-types) - @steve-chavez
|
||||
+ Can be replaced with custom media types
|
||||
- #1462, #1548, Removed `application/octet-stream`, `text/plain`, `text/xml` [builtin support for scalar results](https://postgrest.org/en/v11.1/references/api/resource_representation.html#scalar-function-response-format) - @steve-chavez
|
||||
+ Can be replaced with custom media types
|
||||
|
||||
## [11.1.0] - 2023-06-07
|
||||
|
||||
### Added
|
||||
|
||||
@@ -218,9 +218,6 @@ exampleConfigFile =
|
||||
|## Base url for the OpenAPI output
|
||||
|openapi-server-proxy-uri = ""
|
||||
|
|
||||
|## Content types to produce raw output
|
||||
|# raw-media-types="image/png, image/jpg"
|
||||
|
|
||||
|## Configurable CORS origins
|
||||
|# server-cors-allowed-origins = ""
|
||||
|
|
||||
|
||||
@@ -61,7 +61,6 @@ import PostgREST.Config.JSPath (JSPath, JSPathExp (..),
|
||||
dumpJSPath, pRoleClaimKey)
|
||||
import PostgREST.Config.Proxy (Proxy (..),
|
||||
isMalformedProxyUri, toURI)
|
||||
import PostgREST.MediaType (MediaType (..), toMime)
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier, dumpQi,
|
||||
toQi)
|
||||
|
||||
@@ -102,7 +101,6 @@ data AppConfig = AppConfig
|
||||
, configOpenApiMode :: OpenAPIMode
|
||||
, configOpenApiSecurityActive :: Bool
|
||||
, configOpenApiServerProxyUri :: Maybe Text
|
||||
, configRawMediaTypes :: [MediaType]
|
||||
, configServerCorsAllowedOrigins :: Maybe [Text]
|
||||
, configServerHost :: Text
|
||||
, configServerPort :: Int
|
||||
@@ -169,7 +167,6 @@ toText conf =
|
||||
,("openapi-mode", q . dumpOpenApiMode . configOpenApiMode)
|
||||
,("openapi-security-active", T.toLower . show . configOpenApiSecurityActive)
|
||||
,("openapi-server-proxy-uri", q . fromMaybe mempty . configOpenApiServerProxyUri)
|
||||
,("raw-media-types", q . T.decodeUtf8 . BS.intercalate "," . fmap toMime . configRawMediaTypes)
|
||||
,("server-cors-allowed-origins", q . maybe "" (T.intercalate ",") . configServerCorsAllowedOrigins)
|
||||
,("server-host", q . configServerHost)
|
||||
,("server-port", show . configServerPort)
|
||||
@@ -274,7 +271,6 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
||||
<*> parseOpenAPIMode "openapi-mode"
|
||||
<*> (fromMaybe False <$> optBool "openapi-security-active")
|
||||
<*> parseOpenAPIServerProxyURI "openapi-server-proxy-uri"
|
||||
<*> (maybe [] (fmap (MTOther . encodeUtf8) . splitOnCommas) <$> optValue "raw-media-types")
|
||||
<*> (fmap splitOnCommas <$> optValue "server-cors-allowed-origins")
|
||||
<*> (fromMaybe "!4" <$> optString "server-host")
|
||||
<*> (fromMaybe 3000 <$> optInt "server-port")
|
||||
|
||||
+8
-50
@@ -124,18 +124,16 @@ wrappedReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest
|
||||
wrappedReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} = do
|
||||
rPlan <- readPlan identifier conf sCache apiRequest
|
||||
mediaType <- mapLeft ApiRequestError $ negotiateContent conf iAction iPathInfo iAcceptMediaType
|
||||
binField <- mapLeft ApiRequestError $ binaryField conf mediaType Nothing rPlan
|
||||
if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right ()
|
||||
return $ WrappedReadPlan rPlan SQL.Read (mediaToAggregate mediaType binField apiRequest) mediaType
|
||||
return $ WrappedReadPlan rPlan SQL.Read (mediaToAggregate mediaType apiRequest) mediaType
|
||||
|
||||
mutateReadPlan :: Mutation -> ApiRequest -> QualifiedIdentifier -> AppConfig -> SchemaCache -> Either Error MutateReadPlan
|
||||
mutateReadPlan mutation apiRequest@ApiRequest{iPreferences=Preferences{..},..} identifier conf sCache = do
|
||||
rPlan <- readPlan identifier conf sCache apiRequest
|
||||
mPlan <- mutatePlan mutation identifier apiRequest sCache rPlan
|
||||
mediaType <- mapLeft ApiRequestError $ negotiateContent conf iAction iPathInfo iAcceptMediaType
|
||||
binField <- mapLeft ApiRequestError $ binaryField conf mediaType Nothing rPlan
|
||||
if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right ()
|
||||
return $ MutateReadPlan rPlan mPlan SQL.Write (mediaToAggregate mediaType binField apiRequest) mediaType
|
||||
return $ MutateReadPlan rPlan mPlan SQL.Write (mediaToAggregate mediaType apiRequest) mediaType
|
||||
|
||||
callReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> InvokeMethod -> Either Error CallReadPlan
|
||||
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} invMethod = do
|
||||
@@ -160,9 +158,8 @@ callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferenc
|
||||
(InvPost, Routine.Volatile) -> SQL.Write
|
||||
cPlan = callPlan proc apiRequest paramKeys args rPlan
|
||||
mediaType <- mapLeft ApiRequestError $ negotiateContent conf iAction iPathInfo iAcceptMediaType
|
||||
binField <- mapLeft ApiRequestError $ binaryField conf mediaType (Just proc) rPlan
|
||||
if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right ()
|
||||
return $ CallReadPlan rPlan cPlan txMode proc (mediaToAggregate mediaType binField apiRequest) mediaType
|
||||
return $ CallReadPlan rPlan cPlan txMode proc (mediaToAggregate mediaType apiRequest) mediaType
|
||||
where
|
||||
qsParams' = QueryParams.qsParams iQueryParams
|
||||
|
||||
@@ -828,40 +825,8 @@ inferColsEmbedNeeds (Node ReadPlan{select} forest) pkCols
|
||||
addFilterToLogicForest :: CoercibleFilter -> [CoercibleLogicTree] -> [CoercibleLogicTree]
|
||||
addFilterToLogicForest flt lf = CoercibleStmnt flt : lf
|
||||
|
||||
-- | If raw(binary) output is requested, check that MediaType is one of the
|
||||
-- admitted rawMediaTypes and that`?select=...` contains only one field other
|
||||
-- than `*`
|
||||
binaryField :: AppConfig -> MediaType -> Maybe Routine -> ReadPlanTree -> Either ApiRequestError (Maybe FieldName)
|
||||
binaryField AppConfig{configRawMediaTypes} acceptMediaType proc rpTree
|
||||
| isRawMediaType =
|
||||
if (funcReturnsScalar <$> proc) == Just True ||
|
||||
(funcReturnsSetOfScalar <$> proc) == Just True
|
||||
then Right $ Just "pgrst_scalar"
|
||||
else
|
||||
let
|
||||
fieldName = fstFieldName rpTree
|
||||
in
|
||||
case fieldName of
|
||||
Just fld -> Right $ Just fld
|
||||
Nothing -> Left $ BinaryFieldError acceptMediaType
|
||||
| otherwise =
|
||||
Right Nothing
|
||||
where
|
||||
isRawMediaType = acceptMediaType `elem` configRawMediaTypes `L.union` [MTOctetStream, MTTextPlain, MTTextXML] || isRawPlan acceptMediaType
|
||||
isRawPlan mt = case mt of
|
||||
MTPlan MTOctetStream _ _ -> True
|
||||
MTPlan MTTextPlain _ _ -> True
|
||||
MTPlan MTTextXML _ _ -> True
|
||||
_ -> False
|
||||
|
||||
fstFieldName :: ReadPlanTree -> Maybe FieldName
|
||||
fstFieldName (Node ReadPlan{select=(CoercibleField{cfName="*", cfJsonPath=[]}, _, _):_} []) = Nothing
|
||||
fstFieldName (Node ReadPlan{select=[(CoercibleField{cfName=fld, cfJsonPath=[]}, _, _)]} []) = Just fld
|
||||
fstFieldName _ = Nothing
|
||||
|
||||
|
||||
mediaToAggregate :: MediaType -> Maybe FieldName -> ApiRequest -> ResultAggregate
|
||||
mediaToAggregate mt binField apiReq@ApiRequest{iAction=act, iPreferences=Preferences{preferRepresentation=rep}} =
|
||||
mediaToAggregate :: MediaType -> ApiRequest -> ResultAggregate
|
||||
mediaToAggregate mt apiReq@ApiRequest{iAction=act, iPreferences=Preferences{preferRepresentation=rep}} =
|
||||
if noAgg then NoAgg
|
||||
else case mt of
|
||||
MTApplicationJSON -> BuiltinAggJson
|
||||
@@ -873,16 +838,11 @@ mediaToAggregate mt binField apiReq@ApiRequest{iAction=act, iPreferences=Prefere
|
||||
MTOpenAPI -> BuiltinAggJson
|
||||
MTUrlEncoded -> NoAgg -- TODO: unreachable since a previous step (producedMediaTypes) whitelists the media types that can become aggregates.
|
||||
|
||||
-- binary types
|
||||
MTTextPlain -> BuiltinAggBinary binField
|
||||
MTTextXML -> BuiltinAggXml binField
|
||||
MTOctetStream -> BuiltinAggBinary binField
|
||||
MTOther _ -> BuiltinAggBinary binField
|
||||
|
||||
-- Doing `Accept: application/vnd.pgrst.plan; for="application/vnd.pgrst.plan"` doesn't make sense, so we just empty the body.
|
||||
-- TODO: fail instead to be more strict
|
||||
MTPlan (MTPlan{}) _ _ -> NoAgg
|
||||
MTPlan media _ _ -> mediaToAggregate media binField apiReq
|
||||
MTPlan media _ _ -> mediaToAggregate media apiReq
|
||||
_ -> NoAgg
|
||||
where
|
||||
noAgg = case act of
|
||||
ActionMutate _ -> rep == Just HeadersOnly || rep == Just None || isNothing rep
|
||||
@@ -904,7 +864,7 @@ negotiateContent conf action path accepts =
|
||||
producedMediaTypes :: AppConfig -> Action -> PathInfo -> [MediaType]
|
||||
producedMediaTypes conf action path =
|
||||
case action of
|
||||
ActionRead _ -> defaultMediaTypes ++ rawMediaTypes
|
||||
ActionRead _ -> defaultMediaTypes
|
||||
ActionInvoke _ -> invokeMediaTypes
|
||||
ActionInfo -> defaultMediaTypes
|
||||
ActionMutate _ -> defaultMediaTypes
|
||||
@@ -913,9 +873,7 @@ producedMediaTypes conf action path =
|
||||
inspectMediaTypes = [MTOpenAPI, MTApplicationJSON, MTArrayJSONStrip, MTAny]
|
||||
invokeMediaTypes =
|
||||
defaultMediaTypes
|
||||
++ rawMediaTypes
|
||||
++ [MTOpenAPI | pathIsRootSpec path]
|
||||
defaultMediaTypes =
|
||||
[MTApplicationJSON, MTArrayJSONStrip, MTSingularJSON True, MTSingularJSON False, MTGeoJSON, MTTextCSV] ++
|
||||
[MTPlan MTApplicationJSON PlanText mempty | configDbPlanEnabled conf] ++ [MTAny]
|
||||
rawMediaTypes = configRawMediaTypes conf `L.union` [MTOctetStream, MTTextPlain, MTTextXML]
|
||||
|
||||
@@ -66,7 +66,7 @@ import Protolude hiding (Handler)
|
||||
type DbHandler = ExceptT Error SQL.Transaction
|
||||
|
||||
readQuery :: WrappedReadPlan -> AppConfig -> ApiRequest -> DbHandler ResultSet
|
||||
readQuery WrappedReadPlan{wrReadPlan, wrMedia, wrResAgg} conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} = do
|
||||
readQuery WrappedReadPlan{..} conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} = do
|
||||
let countQuery = QueryBuilder.readPlanToCountQuery wrReadPlan
|
||||
resultSet <-
|
||||
lift . SQL.statement mempty $
|
||||
@@ -151,7 +151,7 @@ deleteQuery mrPlan@MutateReadPlan{mrMedia} apiReq@ApiRequest{..} conf = do
|
||||
pure resultSet
|
||||
|
||||
invokeQuery :: Routine -> CallReadPlan -> ApiRequest -> AppConfig -> PgVersion -> DbHandler ResultSet
|
||||
invokeQuery rout CallReadPlan{crReadPlan, crCallPlan, crResAgg, crMedia} apiReq@ApiRequest{iPreferences=Preferences{..}} conf@AppConfig{..} pgVer = do
|
||||
invokeQuery rout CallReadPlan{..} apiReq@ApiRequest{iPreferences=Preferences{..}} conf@AppConfig{..} pgVer = do
|
||||
resultSet <-
|
||||
lift . SQL.statement mempty $
|
||||
Statements.prepareCall
|
||||
|
||||
@@ -209,20 +209,9 @@ asJsonF rout strip
|
||||
Just r -> (funcReturnsSingleComposite r, funcReturnsScalar r, funcReturnsSetOfScalar r)
|
||||
Nothing -> (False, False, False)
|
||||
|
||||
|
||||
asXmlF :: Maybe FieldName -> SQL.Snippet
|
||||
asXmlF (Just fieldName) = "coalesce(xmlagg(_postgrest_t." <> pgFmtIdent fieldName <> "), '')"
|
||||
-- TODO unreachable because a previous step(binaryField) will validate that there's a field. This will be cleared once custom media types are implemented.
|
||||
asXmlF Nothing = "coalesce(xmlagg(_postgrest_t), '')"
|
||||
|
||||
asGeoJsonF :: SQL.Snippet
|
||||
asGeoJsonF = "json_build_object('type', 'FeatureCollection', 'features', coalesce(json_agg(ST_AsGeoJSON(_postgrest_t)::json), '[]'))"
|
||||
|
||||
asBinaryF :: Maybe FieldName -> SQL.Snippet
|
||||
asBinaryF (Just fieldName) = "coalesce(string_agg(_postgrest_t." <> pgFmtIdent fieldName <> ", ''), '')"
|
||||
-- TODO unreachable because a previous step(binaryField) will validate that there's a field. This will be cleared once custom media types are implemented.
|
||||
asBinaryF Nothing = "coalesce(string_agg(_postgrest_t, ''), '')"
|
||||
|
||||
locationF :: [Text] -> SQL.Snippet
|
||||
locationF pKeys = [qc|(
|
||||
WITH data AS (SELECT row_to_json(_) AS row FROM {sourceCTEName} AS _ LIMIT 1)
|
||||
@@ -504,6 +493,4 @@ aggF rout = \case
|
||||
BuiltinAggSingleJson strip -> asJsonSingleF rout strip
|
||||
BuiltinAggGeoJson -> asGeoJsonF
|
||||
BuiltinAggCsv -> asCsvF
|
||||
BuiltinAggXml bField -> asXmlF bField
|
||||
BuiltinAggBinary bField -> asBinaryF bField
|
||||
NoAgg -> "''::text"
|
||||
|
||||
@@ -23,8 +23,7 @@ import qualified Data.Aeson as JSON
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Hasql.Transaction.Sessions as SQL
|
||||
|
||||
import PostgREST.SchemaCache.Identifiers (FieldName,
|
||||
QualifiedIdentifier (..),
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
|
||||
Schema, TableName)
|
||||
|
||||
import Protolude
|
||||
@@ -95,8 +94,6 @@ data ResultAggregate
|
||||
| BuiltinAggArrayJsonStrip
|
||||
| BuiltinAggGeoJson
|
||||
| BuiltinAggCsv
|
||||
| BuiltinAggXml (Maybe FieldName)
|
||||
| BuiltinAggBinary (Maybe FieldName)
|
||||
| NoAgg
|
||||
deriving (Eq, Show)
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "error"
|
||||
openapi-mode = "follow-privileges"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-cors-allowed-origins = ""
|
||||
server-host = "!4"
|
||||
server-port = 3000
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "error"
|
||||
openapi-mode = "follow-privileges"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-cors-allowed-origins = ""
|
||||
server-host = "!4"
|
||||
server-port = 3000
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "error"
|
||||
openapi-mode = "follow-privileges"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-cors-allowed-origins = ""
|
||||
server-host = "!4"
|
||||
server-port = 3000
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "error"
|
||||
openapi-mode = "follow-privileges"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-cors-allowed-origins = ""
|
||||
server-host = "!4"
|
||||
server-port = 3000
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "info"
|
||||
openapi-mode = "disabled"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = "https://otherexample.org/api"
|
||||
raw-media-types = "application/vnd.pgrst.other-db-config"
|
||||
server-cors-allowed-origins = "http://example.com"
|
||||
server-host = "0.0.0.0"
|
||||
server-port = 80
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "info"
|
||||
openapi-mode = "ignore-privileges"
|
||||
openapi-security-active = true
|
||||
openapi-server-proxy-uri = "https://example.org/api"
|
||||
raw-media-types = "application/vnd.pgrst.db-config"
|
||||
server-cors-allowed-origins = "http://example.com"
|
||||
server-host = "0.0.0.0"
|
||||
server-port = 80
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "info"
|
||||
openapi-mode = "ignore-privileges"
|
||||
openapi-security-active = true
|
||||
openapi-server-proxy-uri = "https://postgrest.org"
|
||||
raw-media-types = "application/vnd.pgrst.config"
|
||||
server-cors-allowed-origins = "http://example.com"
|
||||
server-host = "0.0.0.0"
|
||||
server-port = 80
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "error"
|
||||
openapi-mode = "follow-privileges"
|
||||
openapi-security-active = false
|
||||
openapi-server-proxy-uri = ""
|
||||
raw-media-types = ""
|
||||
server-cors-allowed-origins = ""
|
||||
server-host = "!4"
|
||||
server-port = 3000
|
||||
|
||||
@@ -29,7 +29,6 @@ PGRST_LOG_LEVEL: info
|
||||
PGRST_OPENAPI_MODE: 'ignore-privileges'
|
||||
PGRST_OPENAPI_SECURITY_ACTIVE: true
|
||||
PGRST_OPENAPI_SERVER_PROXY_URI: 'https://postgrest.org'
|
||||
PGRST_RAW_MEDIA_TYPES: application/vnd.pgrst.config
|
||||
PGRST_SERVER_CORS_ALLOWED_ORIGINS: "http://example.com"
|
||||
PGRST_SERVER_HOST: 0.0.0.0
|
||||
PGRST_SERVER_PORT: 80
|
||||
|
||||
@@ -27,7 +27,6 @@ log-level = "info"
|
||||
openapi-mode = "ignore-privileges"
|
||||
openapi-security-active = true
|
||||
openapi-server-proxy-uri = "https://postgrest.org"
|
||||
raw-media-types = "application/vnd.pgrst.config"
|
||||
server-cors-allowed-origins = "http://example.com"
|
||||
server-host = "0.0.0.0"
|
||||
server-port = 80
|
||||
|
||||
@@ -8,6 +8,3 @@ db-channel-enabled = 13
|
||||
|
||||
# expects integer or string
|
||||
db-max-rows = true
|
||||
|
||||
# expects string
|
||||
raw-media-types = true
|
||||
|
||||
@@ -3,7 +3,6 @@ CREATE ROLE db_config_authenticator LOGIN NOINHERIT;
|
||||
-- reloadable config options
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_aud = 'https://example.org';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret = 'REALLY=REALLY=REALLY=REALLY=VERY=SAFE';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_secret_is_base64 = 'false';
|
||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"';
|
||||
@@ -51,7 +50,6 @@ ALTER ROLE db_config_authenticator SET pgrst.db_config = 'true';
|
||||
CREATE ROLE other_authenticator LOGIN NOINHERIT;
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org';
|
||||
ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api';
|
||||
ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config';
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE';
|
||||
ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true';
|
||||
ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2';
|
||||
|
||||
@@ -119,11 +119,6 @@ cli:
|
||||
use_defaultenv: true
|
||||
env:
|
||||
PGRST_DB_TX_END: rollback
|
||||
- name: raw-media-types list
|
||||
expect: 'raw-media-types = "image/png,image/jpeg"'
|
||||
use_defaultenv: true
|
||||
env:
|
||||
PGRST_RAW_MEDIA_TYPES: ' image/png , image/jpeg '
|
||||
|
||||
roleclaims:
|
||||
- key: '.postgrest.a_role'
|
||||
|
||||
@@ -12,7 +12,8 @@ import SpecHelper (acceptHdrs)
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "When raw-media-types is set to \"text/html\"" $
|
||||
it "can get raw output with Accept: text/html" $
|
||||
it "can get raw output with Accept: text/html" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome.html" (acceptHdrs "text/html") ""
|
||||
`shouldRespondWith`
|
||||
[str|
|
||||
|
||||
@@ -276,6 +276,7 @@ spec actualPgVersion = do
|
||||
totalCost `shouldBe` 68.56
|
||||
|
||||
it "outputs the plan for text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
r <- request methodGet "/rpc/return_scalar_xml"
|
||||
(acceptHdrs "application/vnd.pgrst.plan+json; for=\"text/xml\"; options=verbose") ""
|
||||
|
||||
|
||||
@@ -1043,21 +1043,24 @@ spec actualPgVersion = do
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "binary output" $ do
|
||||
it "can query if a single column is selected" $
|
||||
it "can query if a single column is selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images_base64?select=img&name=eq.A.png" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "can get raw output with Accept: text/plain" $
|
||||
it "can get raw output with Accept: text/plain" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/projects?select=name&id=eq.1" (acceptHdrs "text/plain") ""
|
||||
`shouldRespondWith` "Windows 7"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/xmltest?select=xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<myxml>foo</myxml>bar<foobar><baz/></foobar>"
|
||||
@@ -1066,6 +1069,7 @@ spec actualPgVersion = do
|
||||
}
|
||||
|
||||
it "fails if a single column is not selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images?select=img,name&name=eq.A.png" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
@@ -1085,7 +1089,8 @@ spec actualPgVersion = do
|
||||
[json| {"message":"application/octet-stream requested but more than one column was selected","code":"PGRST113","details":null,"hint":null} |]
|
||||
{ matchStatus = 406 }
|
||||
|
||||
it "concatenates results if more than one row is returned" $
|
||||
it "concatenates results if more than one row is returned" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/images_base64?select=img&name=in.(A.png,B.png)" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII="
|
||||
{ matchStatus = 200
|
||||
|
||||
@@ -1079,21 +1079,24 @@ spec actualPgVersion =
|
||||
|
||||
context "binary output" $ do
|
||||
context "Proc that returns scalar" $ do
|
||||
it "can query without selecting column" $
|
||||
it "can query without selecting column" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_base64_bin" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "can get raw output with Accept: text/plain" $
|
||||
it "can get raw output with Accept: text/plain" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome" (acceptHdrs "text/plain") ""
|
||||
`shouldRespondWith` "Welcome to PostgREST"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "text/plain; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/return_scalar_xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<my-xml-tag/>"
|
||||
@@ -1101,7 +1104,8 @@ spec actualPgVersion =
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "can get raw xml output with Accept: text/xml" $
|
||||
it "can get raw xml output with Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome.xml" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
"<html>\n <head>\n <title>PostgREST</title>\n </head>\n <body>\n <h1>Welcome to PostgREST</h1>\n </body>\n</html>"
|
||||
@@ -1109,7 +1113,8 @@ spec actualPgVersion =
|
||||
, matchHeaders = ["Content-Type" <:> "text/xml; charset=utf-8"]
|
||||
}
|
||||
|
||||
it "should fail with function returning text and Accept: text/xml" $
|
||||
it "should fail with function returning text and Accept: text/xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome" (acceptHdrs "text/xml") ""
|
||||
`shouldRespondWith`
|
||||
[json|
|
||||
@@ -1125,7 +1130,8 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
context "Proc that returns set of scalars" $
|
||||
it "can query without selecting column" $
|
||||
it "can query without selecting column" $ do
|
||||
pendingWith "TBD"
|
||||
request methodGet "/rpc/welcome_twice"
|
||||
(acceptHdrs "text/plain")
|
||||
""
|
||||
@@ -1136,14 +1142,16 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
context "Proc that returns rows" $ do
|
||||
it "can query if a single column is selected" $
|
||||
it "can query if a single column is selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_rows_with_base64_bin?select=img" (acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII="
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = ["Content-Type" <:> "application/octet-stream"]
|
||||
}
|
||||
|
||||
it "fails if a single column is not selected" $
|
||||
it "fails if a single column is not selected" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/ret_rows_with_base64_bin"
|
||||
(acceptHdrs "application/octet-stream") ""
|
||||
`shouldRespondWith`
|
||||
@@ -1318,14 +1326,16 @@ spec actualPgVersion =
|
||||
`shouldRespondWith`
|
||||
[json|{"A": 1, "B": 2, "C": 3}|]
|
||||
|
||||
it "can insert text directly" $
|
||||
it "can insert text directly" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_text_param"
|
||||
[("Content-Type", "text/plain"), ("Accept", "text/plain")]
|
||||
[str|unnamed text arg|]
|
||||
`shouldRespondWith`
|
||||
[str|unnamed text arg|]
|
||||
|
||||
it "can insert xml directly" $
|
||||
it "can insert xml directly" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_xml_param"
|
||||
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
|
||||
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]
|
||||
@@ -1333,6 +1343,7 @@ spec actualPgVersion =
|
||||
[str|<note><from>John</from><to>Jane</to><message>Remember me</message></note>|]
|
||||
|
||||
it "can insert bytea directly" $ do
|
||||
pendingWith "TBD"
|
||||
let file = unsafePerformIO $ BL.readFile "test/spec/fixtures/image.png"
|
||||
r <- request methodPost "/rpc/unnamed_bytea_param"
|
||||
[("Content-Type", "application/octet-stream"), ("Accept", "application/octet-stream")]
|
||||
@@ -1418,6 +1429,7 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
it "should be able to fallback to the single unnamed parameter function when other overloaded functions are not found" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/overloaded_unnamed_param"
|
||||
[("Content-Type", "application/json")]
|
||||
[json|{"A": 1, "B": 2, "C": 3}|]
|
||||
@@ -1476,6 +1488,7 @@ spec actualPgVersion =
|
||||
}
|
||||
|
||||
it "should fail on /rpc/unnamed_xml_param when posting invalid xml" $ do
|
||||
pendingWith "TBD"
|
||||
request methodPost "/rpc/unnamed_xml_param"
|
||||
[("Content-Type", "text/xml"), ("Accept", "text/xml")]
|
||||
[str|<|]
|
||||
|
||||
+1
-5
@@ -103,7 +103,6 @@ main = do
|
||||
asymJwkApp = app testCfgAsymJWK
|
||||
asymJwkSetApp = app testCfgAsymJWKSet
|
||||
rootSpecApp = app testCfgRootSpec
|
||||
htmlRawOutputApp = app testCfgHtmlRawOutput
|
||||
responseHeadersApp = app testCfgResponseHeaders
|
||||
disallowRollbackApp = app testCfgDisallowRollback
|
||||
forceRollbackApp = app testCfgForceRollback
|
||||
@@ -150,6 +149,7 @@ main = do
|
||||
, ("Feature.Query.ComputedRelsSpec" , Feature.Query.ComputedRelsSpec.spec)
|
||||
, ("Feature.Query.RelatedQueriesSpec" , Feature.Query.RelatedQueriesSpec.spec)
|
||||
, ("Feature.Query.SpreadQueriesSpec" , Feature.Query.SpreadQueriesSpec.spec)
|
||||
, ("Feature.Query.HtmlRawOutputSpec" , Feature.Query.HtmlRawOutputSpec.spec)
|
||||
, ("Feature.NoSuperuserSpec" , Feature.NoSuperuserSpec.spec)
|
||||
]
|
||||
|
||||
@@ -160,10 +160,6 @@ main = do
|
||||
parallel $ beforeAll_ analyze . before withApp $
|
||||
describe "Feature.Query.RangeSpec" Feature.Query.RangeSpec.spec
|
||||
|
||||
-- this test runs with a raw-output-media-types set to text/html
|
||||
parallel $ before htmlRawOutputApp $
|
||||
describe "Feature.Query.HtmlRawOutputSpec" Feature.Query.HtmlRawOutputSpec.spec
|
||||
|
||||
-- this test runs with a different server flag
|
||||
parallel $ before maxRowsApp $
|
||||
describe "Feature.Query.QueryLimitedSpec" Feature.Query.QueryLimitedSpec.spec
|
||||
|
||||
@@ -31,7 +31,6 @@ import PostgREST.Config (AppConfig (..),
|
||||
LogLevel (..),
|
||||
OpenAPIMode (..),
|
||||
parseSecret)
|
||||
import PostgREST.MediaType (MediaType (..))
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||
import Protolude hiding (get, toS)
|
||||
import Protolude.Conv (toS)
|
||||
@@ -128,7 +127,6 @@ baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
||||
, configOpenApiMode = OAFollowPriv
|
||||
, configOpenApiSecurityActive = False
|
||||
, configOpenApiServerProxyUri = Nothing
|
||||
, configRawMediaTypes = []
|
||||
, configServerCorsAllowedOrigins = Nothing
|
||||
, configServerHost = "localhost"
|
||||
, configServerPort = 3000
|
||||
@@ -221,9 +219,6 @@ testCfgExtraSearchPath = baseCfg { configDbExtraSearchPath = ["public", "extensi
|
||||
testCfgRootSpec :: AppConfig
|
||||
testCfgRootSpec = baseCfg { configDbRootSpec = Just $ QualifiedIdentifier mempty "root"}
|
||||
|
||||
testCfgHtmlRawOutput :: AppConfig
|
||||
testCfgHtmlRawOutput = baseCfg { configRawMediaTypes = [MTOther "text/html"] }
|
||||
|
||||
testCfgResponseHeaders :: AppConfig
|
||||
testCfgResponseHeaders = baseCfg { configDbPreRequest = Just $ QualifiedIdentifier mempty "custom_headers" }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user