feat: Drop support for pg 10
This commit is contained in:
committed by
Wolfgang Walther
parent
daa77d17aa
commit
126178642b
@@ -11,14 +11,12 @@ import Network.HTTP.Types
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110)
|
||||
|
||||
import PostgREST.Version (docsVersion)
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
spec :: PgVersion -> SpecWith ((), Application)
|
||||
spec actualPgVersion = describe "OpenAPI" $ do
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "OpenAPI" $ do
|
||||
it "root path returns a valid openapi spec" $ do
|
||||
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
||||
request methodHead "/"
|
||||
@@ -290,8 +288,7 @@ spec actualPgVersion = describe "OpenAPI" $ do
|
||||
|
||||
getParameterYear `shouldBe` Just "#/parameters/rowFilter.car_models.year"
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
getParameterRef `shouldBe` Just "#/parameters/rowFilter.car_models.car_brand_name"
|
||||
getParameterRef `shouldBe` Just "#/parameters/rowFilter.car_models.car_brand_name"
|
||||
|
||||
describe "Materialized view" $
|
||||
|
||||
|
||||
@@ -7,13 +7,11 @@ import Network.HTTP.Types
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Wai
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110)
|
||||
|
||||
import Protolude
|
||||
import SpecHelper
|
||||
|
||||
spec :: PgVersion -> SpecWith ((), Application)
|
||||
spec actualPgVersion = describe "Allow header" $ do
|
||||
spec :: SpecWith ((), Application)
|
||||
spec = describe "Allow header" $ do
|
||||
context "a table" $ do
|
||||
it "includes read/write methods for writeable table" $ do
|
||||
r <- request methodOptions "/items" [] ""
|
||||
@@ -29,12 +27,7 @@ spec actualPgVersion = describe "Allow header" $ do
|
||||
r <- request methodOptions "/car_models" [] ""
|
||||
liftIO $
|
||||
simpleHeaders r `shouldSatisfy`
|
||||
matchHeader "Allow" (
|
||||
if actualPgVersion >= pgVersion110 then
|
||||
"OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
|
||||
else
|
||||
"OPTIONS,GET,HEAD,POST,PATCH,DELETE"
|
||||
)
|
||||
matchHeader "Allow" "OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
|
||||
|
||||
context "a view" $ do
|
||||
context "auto updatable" $ do
|
||||
|
||||
@@ -11,9 +11,9 @@ import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
import Text.Heredoc
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
|
||||
pgVersion112, pgVersion120,
|
||||
pgVersion130, pgVersion140)
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||
pgVersion120, pgVersion130,
|
||||
pgVersion140)
|
||||
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
@@ -141,19 +141,18 @@ spec actualPgVersion = do
|
||||
, "Preference-Applied" <:> "return=headers-only"]
|
||||
}
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "should not throw and return location header for partitioned tables when selecting without PK" $
|
||||
request methodPost "/car_models"
|
||||
[("Prefer", "return=headers-only")]
|
||||
[json|{"name":"Enzo","year":2021}|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Location" <:> "/car_models?name=eq.Enzo&year=eq.2021"
|
||||
, "Content-Range" <:> "*/*"
|
||||
, "Preference-Applied" <:> "return=headers-only"]
|
||||
}
|
||||
it "should not throw and return location header for partitioned tables when selecting without PK" $
|
||||
request methodPost "/car_models"
|
||||
[("Prefer", "return=headers-only")]
|
||||
[json|{"name":"Enzo","year":2021}|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Location" <:> "/car_models?name=eq.Enzo&year=eq.2021"
|
||||
, "Content-Range" <:> "*/*"
|
||||
, "Preference-Applied" <:> "return=headers-only"]
|
||||
}
|
||||
|
||||
context "requesting no representation" $
|
||||
it "should not throw and return no location header when selecting without PK" $
|
||||
|
||||
@@ -8,8 +8,8 @@ import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
|
||||
pgVersion112, pgVersion121)
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||
pgVersion121)
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
@@ -539,107 +539,106 @@ spec actualPgVersion = do
|
||||
[json|[{"id":1,"computed_overload":true}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $ do
|
||||
describe "partitioned tables embedding" $ do
|
||||
it "can request a table as parent from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,Murcielago)&select=name,year,car_brands(name)&order=name.asc" `shouldRespondWith`
|
||||
describe "partitioned tables embedding" $ do
|
||||
it "can request a table as parent from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,Murcielago)&select=name,year,car_brands(name)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_brands":{"name":"DMC"}},
|
||||
{"name":"Murcielago","year":2001,"car_brands":{"name":"Lamborghini"}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can request partitioned tables as children from a table" $
|
||||
get "/car_brands?select=name,car_models(name,year)&order=name.asc&car_models.order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DMC","car_models":[{"name":"DeLorean","year":1981}]},
|
||||
{"name":"Ferrari","car_models":[{"name":"F310-B","year":1997}]},
|
||||
{"name":"Lamborghini","car_models":[{"name":"Murcielago","year":2001},{"name":"Veneno","year":2013}]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion121) $ do
|
||||
it "can request tables as children from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,F310-B)&select=name,year,car_racers(name)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_brands":{"name":"DMC"}},
|
||||
{"name":"Murcielago","year":2001,"car_brands":{"name":"Lamborghini"}}] |]
|
||||
[{"name":"DeLorean","year":1981,"car_racers":[]},
|
||||
{"name":"F310-B","year":1997,"car_racers":[{"name":"Michael Schumacher"}]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can request partitioned tables as children from a table" $
|
||||
get "/car_brands?select=name,car_models(name,year)&order=name.asc&car_models.order=name.asc" `shouldRespondWith`
|
||||
it "can request a partitioned table as parent from a table" $
|
||||
get "/car_racers?select=name,car_models(name,year)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DMC","car_models":[{"name":"DeLorean","year":1981}]},
|
||||
{"name":"Ferrari","car_models":[{"name":"F310-B","year":1997}]},
|
||||
{"name":"Lamborghini","car_models":[{"name":"Murcielago","year":2001},{"name":"Veneno","year":2013}]}] |]
|
||||
[{"name":"Alain Prost","car_models":null},
|
||||
{"name":"Michael Schumacher","car_models":{"name":"F310-B","year":1997}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion121) $ do
|
||||
it "can request tables as children from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,F310-B)&select=name,year,car_racers(name)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_racers":[]},
|
||||
{"name":"F310-B","year":1997,"car_racers":[{"name":"Michael Schumacher"}]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can request partitioned tables as children from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,Murcielago,Veneno)&select=name,year,car_model_sales(date,quantity)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_model_sales":[{"date":"2021-01-14","quantity":7},{"date":"2021-01-15","quantity":9}]},
|
||||
{"name":"Murcielago","year":2001,"car_model_sales":[{"date":"2021-02-11","quantity":1},{"date":"2021-02-12","quantity":3}]},
|
||||
{"name":"Veneno","year":2013,"car_model_sales":[]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can request a partitioned table as parent from a table" $
|
||||
get "/car_racers?select=name,car_models(name,year)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"Alain Prost","car_models":null},
|
||||
{"name":"Michael Schumacher","car_models":{"name":"F310-B","year":1997}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can request a partitioned table as parent from a partitioned table" $ do
|
||||
get "/car_model_sales?date=in.(2021-01-15,2021-02-11)&select=date,quantity,car_models(name,year)&order=date.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"date":"2021-01-15","quantity":9,"car_models":{"name":"DeLorean","year":1981}},
|
||||
{"date":"2021-02-11","quantity":1,"car_models":{"name":"Murcielago","year":2001}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can request partitioned tables as children from a partitioned table" $
|
||||
get "/car_models?name=in.(DeLorean,Murcielago,Veneno)&select=name,year,car_model_sales(date,quantity)&order=name.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_model_sales":[{"date":"2021-01-14","quantity":7},{"date":"2021-01-15","quantity":9}]},
|
||||
{"name":"Murcielago","year":2001,"car_model_sales":[{"date":"2021-02-11","quantity":1},{"date":"2021-02-12","quantity":3}]},
|
||||
{"name":"Veneno","year":2013,"car_model_sales":[]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can request many to many relationships between partitioned tables ignoring the intermediate table partitions" $
|
||||
get "/car_models?select=name,year,car_dealers(name,city)&order=name.asc&limit=4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_dealers":[{"name":"Springfield Cars S.A.","city":"Springfield"}]},
|
||||
{"name":"F310-B","year":1997,"car_dealers":[]},
|
||||
{"name":"Murcielago","year":2001,"car_dealers":[{"name":"The Best Deals S.A.","city":"Franklin"}]},
|
||||
{"name":"Veneno","year":2013,"car_dealers":[]}] |]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "can request a partitioned table as parent from a partitioned table" $ do
|
||||
get "/car_model_sales?date=in.(2021-01-15,2021-02-11)&select=date,quantity,car_models(name,year)&order=date.asc" `shouldRespondWith`
|
||||
[json|
|
||||
[{"date":"2021-01-15","quantity":9,"car_models":{"name":"DeLorean","year":1981}},
|
||||
{"date":"2021-02-11","quantity":1,"car_models":{"name":"Murcielago","year":2001}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "cannot request partitions as children from a partitioned table" $
|
||||
get "/car_models?id=in.(1,2,4)&select=id,name,car_model_sales_202101(id)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_models' and 'car_model_sales_202101' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_models' and 'car_model_sales_202101' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "can request many to many relationships between partitioned tables ignoring the intermediate table partitions" $
|
||||
get "/car_models?select=name,year,car_dealers(name,city)&order=name.asc&limit=4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"DeLorean","year":1981,"car_dealers":[{"name":"Springfield Cars S.A.","city":"Springfield"}]},
|
||||
{"name":"F310-B","year":1997,"car_dealers":[]},
|
||||
{"name":"Murcielago","year":2001,"car_dealers":[{"name":"The Best Deals S.A.","city":"Franklin"}]},
|
||||
{"name":"Veneno","year":2013,"car_dealers":[]}] |]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
it "cannot request a partitioned table as parent from a partition" $
|
||||
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_model_sales_202101' and 'car_models' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_model_sales_202101' and 'car_models' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "cannot request partitions as children from a partitioned table" $
|
||||
get "/car_models?id=in.(1,2,4)&select=id,name,car_model_sales_202101(id)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_models' and 'car_model_sales_202101' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_models' and 'car_model_sales_202101' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
it "cannot request a partition as parent from a partitioned table" $
|
||||
get "/car_model_sales?id=in.(1,3,4)&select=id,name,car_models_default(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_models' instead of 'car_models_default'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_model_sales' and 'car_models_default' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_model_sales' and 'car_models_default' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "cannot request a partitioned table as parent from a partition" $
|
||||
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_model_sales_202101' and 'car_models' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_model_sales_202101' and 'car_models' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "cannot request a partition as parent from a partitioned table" $
|
||||
get "/car_model_sales?id=in.(1,3,4)&select=id,name,car_models_default(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_models' instead of 'car_models_default'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_model_sales' and 'car_models_default' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_model_sales' and 'car_models_default' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "cannot request partitioned tables as children from a partition" $
|
||||
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_models_default'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_models_default' and 'car_model_sales' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_models_default' and 'car_model_sales' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
it "cannot request partitioned tables as children from a partition" $
|
||||
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
|
||||
[json|
|
||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_models_default'.",
|
||||
"details":"Searched for a foreign key relationship between 'car_models_default' and 'car_model_sales' in the schema 'test', but no matches were found.",
|
||||
"code":"PGRST200",
|
||||
"message":"Could not find a relationship between 'car_models_default' and 'car_model_sales' in the schema cache"} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
describe "view embedding" $ do
|
||||
it "can detect fk relations through views to tables in the public schema" $
|
||||
@@ -1362,22 +1361,19 @@ spec actualPgVersion = do
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
-- Before PG 11, this will fail because we need arrays of domain type values. The docs should explain data reps are
|
||||
-- not supported in this case.
|
||||
when (actualPgVersion >= pgVersion110) $ do
|
||||
it "uses text parser for filter with 'IN' predicates" $
|
||||
get "/datarep_todos?select=id,due_at&label_color=in.(000100,01E240)" `shouldRespondWith`
|
||||
[json| [
|
||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"},
|
||||
{"id":3, "due_at": "2018-01-01T14:12:34.123456Z"}
|
||||
] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "uses text parser for filter with 'NOT IN' predicates" $
|
||||
get "/datarep_todos?select=id,due_at&label_color=not.in.(000000,01E240)" `shouldRespondWith`
|
||||
[json| [
|
||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"}
|
||||
] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "uses text parser for filter with 'IN' predicates" $
|
||||
get "/datarep_todos?select=id,due_at&label_color=in.(000100,01E240)" `shouldRespondWith`
|
||||
[json| [
|
||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"},
|
||||
{"id":3, "due_at": "2018-01-01T14:12:34.123456Z"}
|
||||
] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "uses text parser for filter with 'NOT IN' predicates" $
|
||||
get "/datarep_todos?select=id,due_at&label_color=not.in.(000000,01E240)" `shouldRespondWith`
|
||||
[json| [
|
||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"}
|
||||
] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "uses text parser on value for filter across relations" $
|
||||
get "/datarep_next_two_todos?select=id,name,datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&datarep_todos.label_color=neq.000100" `shouldRespondWith`
|
||||
[json| [{"id":1,"name":"school related","datarep_todos":null},{"id":2,"name":"do these first","datarep_todos":{"label_color":"#000000","due_at":"2018-01-02T00:00:00Z"}}] |]
|
||||
@@ -1385,15 +1381,10 @@ spec actualPgVersion = do
|
||||
-- This is not supported by data reps (would be hard to make it work with high performance). So the test just
|
||||
-- verifies we don't panic or add inappropriate SQL to the filters.
|
||||
it "fails safely on user trying to use ilike operator on data reps column" $
|
||||
get "/datarep_todos?select=id,name&label_color=ilike.#*100" `shouldRespondWith` (
|
||||
if actualPgVersion >= pgVersion110 then
|
||||
get "/datarep_todos?select=id,name&label_color=ilike.#*100" `shouldRespondWith`
|
||||
[json|
|
||||
{"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|
||||
|]
|
||||
else
|
||||
[json|
|
||||
{"code":"42883","details":null,"hint":"No operator matches the given name and argument type(s). You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|
||||
|])
|
||||
{ matchStatus = 404
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
import Text.Heredoc
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion109,
|
||||
pgVersion110, pgVersion112,
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||
pgVersion114)
|
||||
|
||||
import Protolude hiding (get)
|
||||
@@ -395,15 +394,14 @@ spec actualPgVersion =
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "can embed if rpc returns domain of table type" $ do
|
||||
post "/rpc/getproject_domain?select=id,name,client:clients(id),tasks(id)"
|
||||
[json| { "id": 1} |]
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
get "/rpc/getproject_domain?id=1&select=id,name,client:clients(id),tasks(id)"
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
it "can embed if rpc returns domain of table type" $ do
|
||||
post "/rpc/getproject_domain?select=id,name,client:clients(id),tasks(id)"
|
||||
[json| { "id": 1} |]
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
get "/rpc/getproject_domain?id=1&select=id,name,client:clients(id),tasks(id)"
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
|
||||
context "a proc that returns an empty rowset" $
|
||||
it "returns empty json array" $ do
|
||||
@@ -466,12 +464,11 @@ spec actualPgVersion =
|
||||
it "cannot return composite type in hidden schema" $
|
||||
post "/rpc/ret_point_3d" [json|{}|] `shouldRespondWith` 401
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "returns domain of composite type" $
|
||||
post "/rpc/ret_composite_domain"
|
||||
[json|{}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"x": 10, "y": 5}|]
|
||||
it "returns domain of composite type" $
|
||||
post "/rpc/ret_composite_domain"
|
||||
[json|{}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"x": 10, "y": 5}|]
|
||||
|
||||
it "returns single row from table" $
|
||||
post "/rpc/single_article?select=id"
|
||||
@@ -494,26 +491,25 @@ spec actualPgVersion =
|
||||
`shouldRespondWith`
|
||||
[json|null|]
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $ do
|
||||
it "returns a record type" $ do
|
||||
post "/rpc/returns_record"
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||
post "/rpc/returns_record_params"
|
||||
[json|{"id":1, "name": "Windows%"}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||
it "returns a record type" $ do
|
||||
post "/rpc/returns_record"
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||
post "/rpc/returns_record_params"
|
||||
[json|{"id":1, "name": "Windows%"}|]
|
||||
`shouldRespondWith`
|
||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||
|
||||
it "returns a setof record type" $ do
|
||||
post "/rpc/returns_setof_record"
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||
post "/rpc/returns_setof_record_params"
|
||||
[json|{"id":1,"name":"Windows%"}|]
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||
it "returns a setof record type" $ do
|
||||
post "/rpc/returns_setof_record"
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||
post "/rpc/returns_setof_record_params"
|
||||
[json|{"id":1,"name":"Windows%"}|]
|
||||
`shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||
|
||||
context "different types when overloaded" $ do
|
||||
it "returns composite type" $
|
||||
@@ -603,8 +599,7 @@ spec actualPgVersion =
|
||||
[json|"object"|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
when ((actualPgVersion >= pgVersion109 && actualPgVersion < pgVersion110)
|
||||
|| actualPgVersion >= pgVersion114) $
|
||||
when (actualPgVersion >= pgVersion114) $
|
||||
it "parses quoted JSON arguments as JSON string (from Postgres 10.9, 11.4)" $
|
||||
post "/rpc/json_argument"
|
||||
[json| { "arg": "{ \"key\": 3 }" } |]
|
||||
|
||||
@@ -7,13 +7,11 @@ import Test.Hspec
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110)
|
||||
|
||||
import Protolude hiding (get, put)
|
||||
import SpecHelper
|
||||
|
||||
spec :: PgVersion -> SpecWith ((), Application)
|
||||
spec actualPgVersion =
|
||||
spec :: SpecWith ((), Application)
|
||||
spec =
|
||||
describe "UPSERT" $ do
|
||||
context "with POST" $ do
|
||||
context "when Prefer: resolution=merge-duplicates is specified" $ do
|
||||
@@ -60,19 +58,18 @@ spec actualPgVersion =
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "INSERTs and UPDATEs rows on composite pk conflict for partitioned tables" $
|
||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||
[json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
it "INSERTs and UPDATEs rows on composite pk conflict for partitioned tables" $
|
||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||
[json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "succeeds when the payload has no elements" $
|
||||
request methodPost "/articles" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||
@@ -131,18 +128,17 @@ spec actualPgVersion =
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "INSERTs and ignores rows on composite pk conflict for partitioned tables" $
|
||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||
[json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": "Ferrari" },
|
||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
it "INSERTs and ignores rows on composite pk conflict for partitioned tables" $
|
||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||
[json| [
|
||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": "Ferrari" },
|
||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "INSERTs and ignores rows on single unique key conflict" $
|
||||
request methodPost "/single_unique?on_conflict=unique_key"
|
||||
@@ -312,19 +308,18 @@ spec actualPgVersion =
|
||||
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|]
|
||||
{ matchStatus = 201 }
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "succeeds on a partitioned table with composite pk" $ do
|
||||
-- assert that the next request will indeed be an insert
|
||||
get "/car_models?name=eq.Supra&year=eq.2021"
|
||||
`shouldRespondWith`
|
||||
[json|[]|]
|
||||
it "succeeds on a partitioned table with composite pk" $ do
|
||||
-- assert that the next request will indeed be an insert
|
||||
get "/car_models?name=eq.Supra&year=eq.2021"
|
||||
`shouldRespondWith`
|
||||
[json|[]|]
|
||||
|
||||
request methodPut "/car_models?name=eq.Supra&year=eq.2021"
|
||||
[("Prefer", "return=representation")]
|
||||
[json| [ { "name": "Supra", "year": 2021 } ]|]
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "Supra", "year": 2021, "car_brand_name": null } ]|]
|
||||
{ matchStatus = 201 }
|
||||
request methodPut "/car_models?name=eq.Supra&year=eq.2021"
|
||||
[("Prefer", "return=representation")]
|
||||
[json| [ { "name": "Supra", "year": 2021 } ]|]
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "Supra", "year": 2021, "car_brand_name": null } ]|]
|
||||
{ matchStatus = 201 }
|
||||
|
||||
it "succeeds if the table has only PK cols and no other cols" $ do
|
||||
-- assert that the next request will indeed be an insert
|
||||
@@ -378,18 +373,17 @@ spec actualPgVersion =
|
||||
`shouldRespondWith`
|
||||
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "$60,000.00", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
|
||||
|
||||
when (actualPgVersion >= pgVersion110) $
|
||||
it "succeeds on a partitioned table with composite pk" $ do
|
||||
-- assert that the next request will indeed be an update
|
||||
get "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": "DMC" } ]|]
|
||||
it "succeeds on a partitioned table with composite pk" $ do
|
||||
-- assert that the next request will indeed be an update
|
||||
get "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": "DMC" } ]|]
|
||||
|
||||
request methodPut "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||
[("Prefer", "return=representation")]
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||
request methodPut "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||
[("Prefer", "return=representation")]
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||
`shouldRespondWith`
|
||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||
|
||||
it "succeeds if the table has only PK cols and no other cols" $ do
|
||||
-- assert that the next request will indeed be an update
|
||||
|
||||
Reference in New Issue
Block a user