feat: drop support for pg 9.5
This commit is contained in:
committed by
Wolfgang Walther
parent
7ed8b10776
commit
a65ac9b0f6
@@ -63,9 +63,6 @@ jobs:
|
||||
- name: Run the spec tests against PostgreSQL 9.6
|
||||
if: always()
|
||||
run: postgrest-with-postgresql-9.6 postgrest-test-spec
|
||||
- name: Run the spec tests against PostgreSQL 9.5
|
||||
if: always()
|
||||
run: postgrest-with-postgresql-9.5 postgrest-test-spec
|
||||
|
||||
- name: Run query cost tests against all PostgreSQL versions
|
||||
if: always()
|
||||
|
||||
@@ -31,6 +31,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
+ Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`
|
||||
+ PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
|
||||
- #1783, Partitions (created using `PARTITION OF`) are no longer included in the schema cache. - @laurenceisla
|
||||
- #2038, Dropped support for PostgreSQL 9.5 - @wolfgangwalther
|
||||
|
||||
## [8.0.0] - 2021-07-25
|
||||
|
||||
|
||||
@@ -112,15 +112,6 @@ the connection cannot do anything the user themselves couldn't. Other
|
||||
forms of authentication can be built on top of the JWT primitive. See
|
||||
the docs for more information.
|
||||
|
||||
Since PostgreSQL 9.5 supports true [row-level
|
||||
security](http://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.html).
|
||||
In previous versions it can be simulated with triggers and
|
||||
security-barrier views. Because the possible queries to the database
|
||||
are limited to certain templates using
|
||||
[leakproof](http://blog.2ndquadrant.com/how-do-postgresql-security_barrier-views-work/)
|
||||
functions, the trigger workaround does not compromise row-level
|
||||
security.
|
||||
|
||||
## Versioning
|
||||
|
||||
A robust long-lived API needs the freedom to exist in multiple
|
||||
|
||||
@@ -52,7 +52,6 @@ let
|
||||
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11; }
|
||||
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10; }
|
||||
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6; }
|
||||
{ name = "postgresql-9.5"; postgresql = pkgs.postgresql_9_5; }
|
||||
];
|
||||
|
||||
patches =
|
||||
|
||||
+4
-4
@@ -80,8 +80,8 @@ postgrest-coverage postgrest-with-postgresql-10
|
||||
postgrest-lint postgrest-with-postgresql-11
|
||||
postgrest-run postgrest-with-postgresql-12
|
||||
postgrest-style postgrest-with-postgresql-13
|
||||
postgrest-style-check postgrest-with-postgresql-9.5
|
||||
postgrest-test-io postgrest-with-postgresql-9.6
|
||||
postgrest-style-check postgrest-with-postgresql-9.6
|
||||
postgrest-test-io
|
||||
...
|
||||
|
||||
[nix-shell]$
|
||||
@@ -104,8 +104,8 @@ postgrest-coverage postgrest-with-postgresql-10
|
||||
postgrest-lint postgrest-with-postgresql-11
|
||||
postgrest-run postgrest-with-postgresql-12
|
||||
postgrest-style postgrest-with-postgresql-13
|
||||
postgrest-style-check postgrest-with-postgresql-9.5
|
||||
postgrest-test-io postgrest-with-postgresql-9.6
|
||||
postgrest-style-check postgrest-with-postgresql-9.6
|
||||
postgrest-test-io
|
||||
postgrest-test-memory
|
||||
...
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@ self: super:
|
||||
# PostgreSQL 9.5 was removed from Nixpkgs with
|
||||
# https://github.com/NixOS/nixpkgs/commit/72ab382fb6b729b0d654f2c03f5eb25b39f11fbb
|
||||
# We pin its parent commit to get the last version that was available.
|
||||
postgresql_9_5 =
|
||||
let
|
||||
rev = "55ac7d4580c9ab67848c98cb9519317a1cc399c8";
|
||||
tarballHash = "02ffj9f8s1hwhmxj85nx04sv64qb6jm7w0122a1dz9n32fymgklj";
|
||||
|
||||
pinnedPkgs =
|
||||
builtins.fetchTarball {
|
||||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
sha256 = tarballHash;
|
||||
};
|
||||
in
|
||||
(import pinnedPkgs { }).pkgs.postgresql_9_5;
|
||||
# postgresql_9_5 =
|
||||
# let
|
||||
# rev = "55ac7d4580c9ab67848c98cb9519317a1cc399c8";
|
||||
# tarballHash = "02ffj9f8s1hwhmxj85nx04sv64qb6jm7w0122a1dz9n32fymgklj";
|
||||
#
|
||||
# pinnedPkgs =
|
||||
# builtins.fetchTarball {
|
||||
# url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
# sha256 = tarballHash;
|
||||
# };
|
||||
# in
|
||||
# (import pinnedPkgs { }).pkgs.postgresql_9_5;
|
||||
}
|
||||
|
||||
@@ -258,7 +258,6 @@ handleRead headersOnly identifier context@RequestContext{..} = do
|
||||
(shouldCount iPreferCount)
|
||||
(iAcceptContentType == CTTextCSV)
|
||||
bField
|
||||
ctxPgVersion
|
||||
configDbPreparedStatements
|
||||
|
||||
total <- readTotal ctxConfig ctxApiRequest tableTotal countQuery
|
||||
@@ -447,7 +446,6 @@ handleInvoke invMethod proc context@RequestContext{..} = do
|
||||
(iAcceptContentType == CTTextCSV)
|
||||
(iPreferParameters == Just MultipleObjects)
|
||||
bField
|
||||
ctxPgVersion
|
||||
(configDbPreparedStatements ctxConfig)
|
||||
|
||||
response <- liftEither $ gucResponse <$> gucStatus <*> gucHeaders
|
||||
@@ -532,7 +530,6 @@ writeQuery identifier@QualifiedIdentifier{..} isInsert pkCols context@RequestCon
|
||||
(iAcceptContentType ctxApiRequest == CTTextCSV)
|
||||
(iPreferRepresentation ctxApiRequest)
|
||||
pkCols
|
||||
ctxPgVersion
|
||||
(configDbPreparedStatements ctxConfig)
|
||||
|
||||
liftEither $ WriteQueryResult queryTotal fields body <$> gucStatus <*> gucHeaders
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
module PostgREST.Config.PgVersion
|
||||
( PgVersion(..)
|
||||
, minimumPgVersion
|
||||
, pgVersion95
|
||||
, pgVersion96
|
||||
, pgVersion100
|
||||
, pgVersion109
|
||||
@@ -31,10 +30,7 @@ instance Ord PgVersion where
|
||||
|
||||
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
|
||||
minimumPgVersion :: PgVersion
|
||||
minimumPgVersion = pgVersion95
|
||||
|
||||
pgVersion95 :: PgVersion
|
||||
pgVersion95 = PgVersion 90500 "9.5"
|
||||
minimumPgVersion = pgVersion96
|
||||
|
||||
pgVersion96 :: PgVersion
|
||||
pgVersion96 = PgVersion 90600 "9.6"
|
||||
|
||||
@@ -46,7 +46,6 @@ import qualified Hasql.Encoders as HE
|
||||
import Data.Foldable (foldr1)
|
||||
import Text.InterpolatedString.Perl6 (qc)
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion96)
|
||||
import PostgREST.DbStructure.Identifiers (FieldName,
|
||||
QualifiedIdentifier (..))
|
||||
import PostgREST.RangeQuery (NonnegRange, allRange,
|
||||
@@ -315,17 +314,11 @@ limitOffsetF range =
|
||||
limit = maybe "ALL" (\l -> unknownEncoder (BS.pack $ show l)) $ rangeLimit range
|
||||
offset = unknownEncoder (BS.pack . show $ rangeOffset range)
|
||||
|
||||
responseHeadersF :: PgVersion -> SqlFragment
|
||||
responseHeadersF pgVer =
|
||||
if pgVer >= pgVersion96
|
||||
then currentSettingF "response.headers"
|
||||
else "null"
|
||||
responseHeadersF :: SqlFragment
|
||||
responseHeadersF = currentSettingF "response.headers"
|
||||
|
||||
responseStatusF :: PgVersion -> SqlFragment
|
||||
responseStatusF pgVer =
|
||||
if pgVer >= pgVersion96
|
||||
then currentSettingF "response.status"
|
||||
else "null"
|
||||
responseStatusF :: SqlFragment
|
||||
responseStatusF = currentSettingF "response.status"
|
||||
|
||||
currentSettingF :: SqlFragment -> SqlFragment
|
||||
currentSettingF setting =
|
||||
|
||||
@@ -30,9 +30,8 @@ import Data.Maybe (fromJust)
|
||||
import Data.Text.Read (decimal)
|
||||
import Network.HTTP.Types.Status (Status)
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion)
|
||||
import PostgREST.Error (Error (..))
|
||||
import PostgREST.GucHeader (GucHeader)
|
||||
import PostgREST.Error (Error (..))
|
||||
import PostgREST.GucHeader (GucHeader)
|
||||
|
||||
import PostgREST.DbStructure.Identifiers (FieldName)
|
||||
import PostgREST.Query.SqlFragment
|
||||
@@ -47,9 +46,9 @@ import Protolude
|
||||
type ResultsWithCount = (Maybe Int64, Int64, [BS.ByteString], BS.ByteString, Either Error [GucHeader], Either Error (Maybe Status))
|
||||
|
||||
createWriteStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool ->
|
||||
PreferRepresentation -> [Text] -> PgVersion -> Bool ->
|
||||
PreferRepresentation -> [Text] -> Bool ->
|
||||
SQL.Statement () ResultsWithCount
|
||||
createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys pgVer =
|
||||
createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys =
|
||||
SQL.dynamicallyParameterized snippet decodeStandard
|
||||
where
|
||||
snippet =
|
||||
@@ -60,8 +59,8 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
|
||||
"pg_catalog.count(_postgrest_t) AS page_total, " <>
|
||||
locF <> " AS header, " <>
|
||||
bodyF <> " AS body, " <>
|
||||
responseHeadersF pgVer <> " AS response_headers, " <>
|
||||
responseStatusF pgVer <> " AS response_status "
|
||||
responseHeadersF <> " AS response_headers, " <>
|
||||
responseStatusF <> " AS response_status "
|
||||
) <>
|
||||
"FROM (" <> selectF <> ") _postgrest_t"
|
||||
|
||||
@@ -89,9 +88,9 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
|
||||
decodeStandard =
|
||||
fromMaybe (Nothing, 0, [], mempty, Right [], Right Nothing) <$> HD.rowMaybe standardRow
|
||||
|
||||
createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Maybe FieldName -> PgVersion -> Bool ->
|
||||
createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
|
||||
SQL.Statement () ResultsWithCount
|
||||
createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField pgVer =
|
||||
createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField =
|
||||
SQL.dynamicallyParameterized snippet decodeStandard
|
||||
where
|
||||
snippet =
|
||||
@@ -103,8 +102,8 @@ createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField
|
||||
"pg_catalog.count(_postgrest_t) AS page_total, " <>
|
||||
noLocationF <> " AS header, " <>
|
||||
bodyF <> " AS body, " <>
|
||||
responseHeadersF pgVer <> " AS response_headers, " <>
|
||||
responseStatusF pgVer <> " AS response_status " <>
|
||||
responseHeadersF <> " AS response_headers, " <>
|
||||
responseStatusF <> " AS response_status " <>
|
||||
"FROM ( SELECT * FROM " <> sourceCTEName <> " ) _postgrest_t")
|
||||
|
||||
(countCTEF, countResultF) = countF countQuery countTotal
|
||||
@@ -132,9 +131,9 @@ standardRow = (,,,,,) <$> nullableColumn HD.int8 <*> column HD.int8
|
||||
type ProcResults = (Maybe Int64, Int64, ByteString, Either Error [GucHeader], Either Error (Maybe Status))
|
||||
|
||||
callProcStatement :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool ->
|
||||
Bool -> Bool -> Bool -> Maybe FieldName -> PgVersion -> Bool ->
|
||||
Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
|
||||
SQL.Statement () ProcResults
|
||||
callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv multObjects binaryField pgVer =
|
||||
callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv multObjects binaryField =
|
||||
SQL.dynamicallyParameterized snippet decodeProc
|
||||
where
|
||||
snippet =
|
||||
@@ -145,8 +144,8 @@ callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQue
|
||||
countResultF <> " AS total_result_set, " <>
|
||||
"pg_catalog.count(_postgrest_t) AS page_total, " <>
|
||||
bodyF <> " AS body, " <>
|
||||
responseHeadersF pgVer <> " AS response_headers, " <>
|
||||
responseStatusF pgVer <> " AS response_status ") <>
|
||||
responseHeadersF <> " AS response_headers, " <>
|
||||
responseStatusF <> " AS response_status ") <>
|
||||
"FROM (" <> selectQuery <> ") _postgrest_t"
|
||||
|
||||
(countCTEF, countResultF) = countF countQuery countTotal
|
||||
|
||||
@@ -15,10 +15,8 @@ import Test.Hspec.Wai.JSON
|
||||
import Protolude
|
||||
import SpecHelper
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion96)
|
||||
|
||||
spec :: PgVersion -> SpecWith ((), Application)
|
||||
spec actualPgVersion =
|
||||
spec :: SpecWith ((), Application)
|
||||
spec =
|
||||
describe "multiple schemas in single instance" $ do
|
||||
context "Reading tables on different schemas" $ do
|
||||
it "succeeds in reading table from default schema v1 if no schema is selected via header" $
|
||||
@@ -191,16 +189,15 @@ spec actualPgVersion =
|
||||
[json|[{"id": 1, "name": "child v2-3", "parent_id": 3}]|]
|
||||
{ matchHeaders = ["Content-Profile" <:> "v2"] }
|
||||
|
||||
when (actualPgVersion >= pgVersion96) $
|
||||
it "succeeds on PUT on the v2 schema" $
|
||||
request methodPut "/children?id=eq.111" [("Content-Profile", "v2"), ("Prefer", "return=representation")]
|
||||
[json| [ { "id": 111, "name": "child v2-111", "parent_id": null } ]|]
|
||||
`shouldRespondWith`
|
||||
[json|[{ "id": 111, "name": "child v2-111", "parent_id": null }]|]
|
||||
{
|
||||
matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
|
||||
}
|
||||
it "succeeds on PUT on the v2 schema" $
|
||||
request methodPut "/children?id=eq.111" [("Content-Profile", "v2"), ("Prefer", "return=representation")]
|
||||
[json| [ { "id": 111, "name": "child v2-111", "parent_id": null } ]|]
|
||||
`shouldRespondWith`
|
||||
[json|[{ "id": 111, "name": "child v2-111", "parent_id": null }]|]
|
||||
{
|
||||
matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
|
||||
}
|
||||
|
||||
context "OpenAPI output" $ do
|
||||
it "succeeds in reading table definition from default schema v1 if no schema is selected via header" $ do
|
||||
|
||||
+25
-27
@@ -9,8 +9,7 @@ import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
|
||||
pgVersion112, pgVersion121,
|
||||
pgVersion96)
|
||||
pgVersion112, pgVersion121)
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
@@ -200,34 +199,33 @@ spec actualPgVersion = do
|
||||
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion96) $
|
||||
context "Use of the phraseto_tsquery function" $ do
|
||||
it "finds matches" $
|
||||
get "/tsearch?text_search_vector=phfts.The%20Fat%20Cats" `shouldRespondWith`
|
||||
[json| [{"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
context "Use of the phraseto_tsquery function" $ do
|
||||
it "finds matches" $
|
||||
get "/tsearch?text_search_vector=phfts.The%20Fat%20Cats" `shouldRespondWith`
|
||||
[json| [{"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "finds matches with different dictionaries" $
|
||||
get "/tsearch?text_search_vector=phfts(german).Art%20Spass" `shouldRespondWith`
|
||||
[json| [{"text_search_vector": "'art':4 'spass':5 'unmog':7" }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "finds matches with different dictionaries" $
|
||||
get "/tsearch?text_search_vector=phfts(german).Art%20Spass" `shouldRespondWith`
|
||||
[json| [{"text_search_vector": "'art':4 'spass':5 'unmog':7" }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can be negated with not operator" $
|
||||
get "/tsearch?text_search_vector=not.phfts(english).The%20Fat%20Cats" `shouldRespondWith`
|
||||
[json| [
|
||||
{"text_search_vector": "'fun':5 'imposs':9 'kind':3"},
|
||||
{"text_search_vector": "'also':2 'fun':3 'possibl':8"},
|
||||
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"},
|
||||
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can be negated with not operator" $
|
||||
get "/tsearch?text_search_vector=not.phfts(english).The%20Fat%20Cats" `shouldRespondWith`
|
||||
[json| [
|
||||
{"text_search_vector": "'fun':5 'imposs':9 'kind':3"},
|
||||
{"text_search_vector": "'also':2 'fun':3 'possibl':8"},
|
||||
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"},
|
||||
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can be used with or query param" $
|
||||
get "/tsearch?or=(text_search_vector.phfts(german).Art%20Spass, text_search_vector.phfts(french).amusant, text_search_vector.fts(english).impossible)" `shouldRespondWith`
|
||||
[json|[
|
||||
{"text_search_vector": "'fun':5 'imposs':9 'kind':3" },
|
||||
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" },
|
||||
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
it "can be used with or query param" $
|
||||
get "/tsearch?or=(text_search_vector.phfts(german).Art%20Spass, text_search_vector.phfts(french).amusant, text_search_vector.fts(english).impossible)" `shouldRespondWith`
|
||||
[json|[
|
||||
{"text_search_vector": "'fun':5 'imposs':9 'kind':3" },
|
||||
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" },
|
||||
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "matches with computed column" $
|
||||
get "/items?always_true=eq.true&order=id.asc" `shouldRespondWith`
|
||||
|
||||
+57
-59
@@ -15,7 +15,7 @@ import Text.Heredoc
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
|
||||
pgVersion109, pgVersion110,
|
||||
pgVersion112, pgVersion114,
|
||||
pgVersion140, pgVersion96)
|
||||
pgVersion140)
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
@@ -1005,72 +1005,70 @@ spec actualPgVersion =
|
||||
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion96) $
|
||||
it "should work with the phraseto_tsquery function" $
|
||||
get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith`
|
||||
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "should work with the phraseto_tsquery function" $
|
||||
get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith`
|
||||
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "should work with an argument of custom type in public schema" $
|
||||
get "/rpc/test_arg?my_arg=something" `shouldRespondWith`
|
||||
[json|"foobar"|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion96) $ do
|
||||
context "GUC headers on function calls" $ do
|
||||
it "succeeds setting the headers" $ do
|
||||
get "/rpc/get_projects_and_guc_headers?id=eq.2&select=id"
|
||||
`shouldRespondWith` [json|[{"id": 2}]|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
get "/rpc/get_int_and_guc_headers?num=1"
|
||||
`shouldRespondWith` [json|1|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
post "/rpc/get_int_and_guc_headers" [json|{"num": 1}|]
|
||||
`shouldRespondWith` [json|1|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
context "GUC headers on function calls" $ do
|
||||
it "succeeds setting the headers" $ do
|
||||
get "/rpc/get_projects_and_guc_headers?id=eq.2&select=id"
|
||||
`shouldRespondWith` [json|[{"id": 2}]|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
get "/rpc/get_int_and_guc_headers?num=1"
|
||||
`shouldRespondWith` [json|1|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
post "/rpc/get_int_and_guc_headers" [json|{"num": 1}|]
|
||||
`shouldRespondWith` [json|1|]
|
||||
{matchHeaders = [
|
||||
matchContentTypeJson,
|
||||
"X-Test" <:> "key1=val1; someValue; key2=val2",
|
||||
"X-Test-2" <:> "key1=val1"]}
|
||||
|
||||
it "fails when setting headers with wrong json structure" $ do
|
||||
get "/rpc/bad_guc_headers_1"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
get "/rpc/bad_guc_headers_2"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
get "/rpc/bad_guc_headers_3"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
post "/rpc/bad_guc_headers_1" [json|{}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
it "fails when setting headers with wrong json structure" $ do
|
||||
get "/rpc/bad_guc_headers_1"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
get "/rpc/bad_guc_headers_2"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
get "/rpc/bad_guc_headers_3"
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
post "/rpc/bad_guc_headers_1" [json|{}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
|
||||
{ matchStatus = 500
|
||||
, matchHeaders = [ matchContentTypeJson ]
|
||||
}
|
||||
|
||||
it "can set the same http header twice" $
|
||||
get "/rpc/set_cookie_twice"
|
||||
`shouldRespondWith`
|
||||
"null"
|
||||
{ matchHeaders = [ matchContentTypeJson
|
||||
, "Set-Cookie" <:> "sessionid=38afes7a8; HttpOnly; Path=/"
|
||||
, "Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly" ]}
|
||||
it "can set the same http header twice" $
|
||||
get "/rpc/set_cookie_twice"
|
||||
`shouldRespondWith`
|
||||
"null"
|
||||
{ matchHeaders = [ matchContentTypeJson
|
||||
, "Set-Cookie" <:> "sessionid=38afes7a8; HttpOnly; Path=/"
|
||||
, "Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly" ]}
|
||||
|
||||
it "can override the Location header on a trigger" $
|
||||
post "/stuff"
|
||||
|
||||
+12
-14
@@ -9,13 +9,12 @@ import Data.List.NonEmpty (toList)
|
||||
|
||||
import Test.Hspec
|
||||
|
||||
import PostgREST.App (postgrest)
|
||||
import PostgREST.Config (AppConfig (..), LogLevel (..))
|
||||
import PostgREST.Config.Database (queryPgVersion)
|
||||
import PostgREST.Config.PgVersion (pgVersion96)
|
||||
import PostgREST.DbStructure (queryDbStructure)
|
||||
import Protolude hiding (toList, toS)
|
||||
import Protolude.Conv (toS)
|
||||
import PostgREST.App (postgrest)
|
||||
import PostgREST.Config (AppConfig (..), LogLevel (..))
|
||||
import PostgREST.Config.Database (queryPgVersion)
|
||||
import PostgREST.DbStructure (queryDbStructure)
|
||||
import Protolude hiding (toList, toS)
|
||||
import Protolude.Conv (toS)
|
||||
import SpecHelper
|
||||
|
||||
import qualified PostgREST.AppState as AppState
|
||||
@@ -203,16 +202,15 @@ main = do
|
||||
parallel $ before extraSearchPathApp $
|
||||
describe "Feature.ExtraSearchPathSpec" Feature.ExtraSearchPathSpec.spec
|
||||
|
||||
when (actualPgVersion >= pgVersion96) $ do
|
||||
-- this test runs with a root spec function override
|
||||
parallel $ before rootSpecApp $
|
||||
describe "Feature.RootSpec" Feature.RootSpec.spec
|
||||
parallel $ before responseHeadersApp $
|
||||
describe "Feature.RpcPreRequestGucsSpec" Feature.RpcPreRequestGucsSpec.spec
|
||||
-- this test runs with a root spec function override
|
||||
parallel $ before rootSpecApp $
|
||||
describe "Feature.RootSpec" Feature.RootSpec.spec
|
||||
parallel $ before responseHeadersApp $
|
||||
describe "Feature.RpcPreRequestGucsSpec" Feature.RpcPreRequestGucsSpec.spec
|
||||
|
||||
-- this test runs with multiple schemas
|
||||
parallel $ before multipleSchemaApp $
|
||||
describe "Feature.MultipleSchemaSpec" $ Feature.MultipleSchemaSpec.spec actualPgVersion
|
||||
describe "Feature.MultipleSchemaSpec" Feature.MultipleSchemaSpec.spec
|
||||
|
||||
-- this test runs with db-uses-legacy-gucs = false
|
||||
parallel $ before testCfgLegacyGucsApp $
|
||||
|
||||
Reference in New Issue
Block a user