Add UPSERT for POST with Prefer:resoultion=merge/ignore-duplicates

This commit is contained in:
steve-chavez
2018-02-21 07:33:54 -05:00
committed by Steve Chávez
parent 102392e4ab
commit 85b1dc0eb4
12 changed files with 137 additions and 20 deletions
+73
View File
@@ -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
View File
@@ -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
+9 -3
View File
@@ -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);
+2
View File
@@ -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;
+14
View File
@@ -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
);