Add UPSERT for POST with Prefer:resoultion=merge/ignore-duplicates
This commit is contained in:
committed by
Steve Chávez
parent
102392e4ab
commit
85b1dc0eb4
@@ -0,0 +1,73 @@
|
||||
module Feature.UpsertSpec where
|
||||
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
import Network.HTTP.Types
|
||||
|
||||
import SpecHelper
|
||||
import Network.Wai (Application)
|
||||
|
||||
import Protolude hiding (get)
|
||||
|
||||
spec :: SpecWith Application
|
||||
spec =
|
||||
describe "UPSERT" $
|
||||
context "POST with Prefer headers" $ do
|
||||
context "when Prefer: resolution=merge-duplicates is specified" $ do
|
||||
it "does upsert on pk conflict" $
|
||||
request methodPost "/tiobe_pls" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||
[json| [
|
||||
{ "name": "Javascript", "rank": 6 },
|
||||
{ "name": "Java", "rank": 5 }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "name": "Javascript", "rank": 6 },
|
||||
{ "name": "Java", "rank": 5 }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "does upsert on composite pk conflict" $
|
||||
request methodPost "/employees" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||
[json| [
|
||||
{ "first_name": "Frances M.", "last_name": "Roe", "salary": "30000" },
|
||||
{ "first_name": "Peter S.", "last_name": "Yang", "salary": 42000 }
|
||||
]|] `shouldRespondWith` [json| [
|
||||
{ "first_name": "Frances M.", "last_name": "Roe", "salary": "$30,000.00", "company": "One-Up Realty", "occupation": "Author" },
|
||||
{ "first_name": "Peter S.", "last_name": "Yang", "salary": "$42,000.00", "company": null, "occupation": null }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
context "when Prefer: resolution=ignore-duplicates is specified" $ do
|
||||
it "ignores records on pk conflict" $ do
|
||||
request methodPost "/tiobe_pls" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||
[json|[
|
||||
{ "name": "PHP", "rank": 9 },
|
||||
{ "name": "Python", "rank": 10 }
|
||||
]|] `shouldRespondWith` [json|[
|
||||
{ "name": "PHP", "rank": 9 }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
get "/tiobe_pls?rank=gte.9" `shouldRespondWith`
|
||||
[json| [{ "name": "PHP", "rank": 9 }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "ignores records on composite pk conflict" $ do
|
||||
request methodPost "/employees" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||
[json|[
|
||||
{ "first_name": "Daniel B.", "last_name": "Lyon", "salary": "72000", "company": null, "occupation": null },
|
||||
{ "first_name": "Sara M.", "last_name": "Torpey", "salary": 60000, "company": "Burstein-Applebee", "occupation": "Soil scientist" }
|
||||
]|] `shouldRespondWith` [json|[
|
||||
{ "first_name": "Sara M.", "last_name": "Torpey", "salary": "$60,000.00", "company": "Burstein-Applebee", "occupation": "Soil scientist" }
|
||||
]|]
|
||||
{ matchStatus = 201
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
get "/employees?first_name=eq.Daniel B.&last_name=eq.Lyon" `shouldRespondWith`
|
||||
[json| [{ "first_name": "Daniel B.", "last_name": "Lyon", "salary": "$36,000.00", "company": "Dubrow's Cafeteria", "occupation": "Packer" }] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
+6
-3
@@ -6,7 +6,7 @@ import SpecHelper
|
||||
import qualified Hasql.Pool as P
|
||||
|
||||
import PostgREST.App (postgrest)
|
||||
import PostgREST.Config (pgVersion96, configSettings)
|
||||
import PostgREST.Config (pgVersion95, pgVersion96, configSettings)
|
||||
import PostgREST.DbStructure (getDbStructure, getPgVersion, fillSessionWithSettings)
|
||||
import PostgREST.Types (DbStructure(..))
|
||||
import Data.Function (id)
|
||||
@@ -32,6 +32,7 @@ import qualified Feature.AndOrParamsSpec
|
||||
import qualified Feature.RpcSpec
|
||||
import qualified Feature.NonexistentSchemaSpec
|
||||
import qualified Feature.PgVersion96Spec
|
||||
import qualified Feature.UpsertSpec
|
||||
|
||||
import Protolude
|
||||
|
||||
@@ -62,7 +63,9 @@ main = do
|
||||
reset = P.use pool (fillSessionWithSettings (configSettings $ testCfg testDbConn)) >> resetDb testDbConn
|
||||
|
||||
actualPgVersion = pgVersion dbStructure
|
||||
pg96spec | actualPgVersion >= pgVersion96 = [("Feature.PgVersion96Spec" , Feature.PgVersion96Spec.spec)]
|
||||
upsertSpec | actualPgVersion >= pgVersion95 = [("Feature.UpsertSpec", Feature.UpsertSpec.spec)]
|
||||
| otherwise = []
|
||||
pg96spec | actualPgVersion >= pgVersion96 = [("Feature.PgVersion96Spec", Feature.PgVersion96Spec.spec)]
|
||||
| otherwise = []
|
||||
|
||||
specs = uncurry describe <$> [
|
||||
@@ -78,7 +81,7 @@ main = do
|
||||
, ("Feature.StructureSpec" , Feature.StructureSpec.spec)
|
||||
, ("Feature.AndOrParamsSpec" , Feature.AndOrParamsSpec.spec)
|
||||
, ("Feature.NonexistentSchemaSpec" , Feature.NonexistentSchemaSpec.spec)
|
||||
] ++ pg96spec
|
||||
] ++ pg96spec ++ upsertSpec
|
||||
|
||||
hspec $ do
|
||||
mapM_ (beforeAll_ reset . before withApp) specs
|
||||
|
||||
Vendored
+9
-3
@@ -334,6 +334,12 @@ INSERT INTO part VALUES (1), (2), (3), (4);
|
||||
|
||||
TRUNCATE TABLE being_part CASCADE;
|
||||
INSERT INTO being_part VALUES (1,1), (2,1), (3,2), (4,3);
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
TRUNCATE TABLE employees CASCADE;
|
||||
INSERT INTO employees VALUES
|
||||
('Frances M.', 'Roe', '24000', 'One-Up Realty', 'Author'),
|
||||
('Daniel B.', 'Lyon', '36000', 'Dubrow''s Cafeteria', 'Packer'),
|
||||
('Edwin S.', 'Smith', '48000', 'Pro Garden Management', 'Marine biologist');
|
||||
|
||||
TRUNCATE TABLE tiobe_pls CASCADE;
|
||||
INSERT INTO tiobe_pls VALUES ('Java', 1), ('C', 2), ('Python', 4);
|
||||
|
||||
Vendored
+2
@@ -63,6 +63,8 @@ GRANT ALL ON TABLE
|
||||
, part
|
||||
, leak
|
||||
, perf_articles
|
||||
, employees
|
||||
, tiobe_pls
|
||||
TO postgrest_test_anonymous;
|
||||
|
||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||
|
||||
Vendored
+14
@@ -1357,3 +1357,17 @@ create table test.perf_articles(
|
||||
id integer not null,
|
||||
body text not null
|
||||
);
|
||||
|
||||
create table test.employees(
|
||||
first_name text,
|
||||
last_name text,
|
||||
salary money,
|
||||
company text,
|
||||
occupation text,
|
||||
primary key(first_name, last_name)
|
||||
);
|
||||
|
||||
create table test.tiobe_pls(
|
||||
name text primary key,
|
||||
rank smallint
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user