fix: HTTP status responses for upserts

* PUT returns 201 instead of 200 when rows are inserted
* POST with "Prefer: resolution=merge-duplicates" returns 200 instead of 201 when no rows are inserted
This commit is contained in:
Taimoor Zaeem
2023-10-26 20:49:20 -05:00
committed by GitHub
parent 82b38341bf
commit 5c9c7f4ff4
12 changed files with 97 additions and 42 deletions
@@ -225,13 +225,11 @@ spec =
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 } ]|]
[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"]
}
[json|[{"id": 111, "name": "child v2-111", "parent_id": null}]|]
{ matchStatus = 201
, 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
+5 -5
View File
@@ -192,7 +192,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 1.29
totalCost `shouldBe` 3.55
it "outputs the total cost for 2 upserts" $ do
r <- request methodPost "/tiobe_pls"
@@ -206,7 +206,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
totalCost `shouldBe` 5.53
it "outputs the total cost for an upsert with 10 rows" $ do
r <- request methodPost "/tiobe_pls"
@@ -220,7 +220,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
totalCost `shouldBe` 5.53
it "outputs the total cost for an upsert with 100 rows" $ do
r <- request methodPost "/tiobe_pls"
@@ -234,7 +234,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
totalCost `shouldBe` 5.53
it "outputs the total cost for an upsert with 1000 rows" $ do
r <- request methodPost "/tiobe_pls"
@@ -248,7 +248,7 @@ spec actualPgVersion = do
liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` 3.27
totalCost `shouldBe` 5.53
it "outputs the plan for application/vnd.pgrst.object" $ do
r <- request methodDelete "/projects?id=eq.6"
+3 -3
View File
@@ -45,11 +45,11 @@ spec =
}
it "works with put request" $
request methodPut "/tiobe_pls?name=eq.Go"
request methodPut "/tiobe_pls?name=eq.Python"
[("Prefer", "return=representation")]
[json| [ { "name": "Go", "rank": 19 } ]|]
[json| [ { "name": "Python", "rank": 19 } ]|]
`shouldRespondWith`
[json| [ { "name": "Go", "rank": 19 } ]|]
[json| [ { "name": "Python", "rank": 19 } ]|]
{ matchStatus = 200
, matchHeaders = map matchServerTimingHasTiming ["jwt", "plan", "query", "render"]
}
+25 -2
View File
@@ -32,6 +32,21 @@ spec actualPgVersion =
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
}
it "UPDATEs rows on pk conflict" $
request methodPost "/tiobe_pls" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
[json| [
{ "name": "Python", "rank": 6 },
{ "name": "Java", "rank": 2 },
{ "name": "C", "rank": 1 }
]|] `shouldRespondWith` [json| [
{ "name": "Python", "rank": 6 },
{ "name": "Java", "rank": 2 },
{ "name": "C", "rank": 1 }
]|]
{ matchStatus = 200
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
}
it "INSERTs and UPDATEs row on composite pk conflict" $
request methodPost "/employees" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
[json| [
@@ -62,7 +77,7 @@ spec actualPgVersion =
it "succeeds when the payload has no elements" $
request methodPost "/articles" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
[json|[]|] `shouldRespondWith`
[json|[]|] { matchStatus = 201
[json|[]|] { matchStatus = 200 -- nothing was inserted, so it should be 200
, matchHeaders = [matchContentTypeJson] }
it "INSERTs and UPDATEs rows on single unique key conflict" $
@@ -282,6 +297,7 @@ spec actualPgVersion =
[json| [ { "name": "Go", "rank": 19 } ]|]
`shouldRespondWith`
[json| [ { "name": "Go", "rank": 19 } ]|]
{ matchStatus = 201 }
it "succeeds on table with composite pk" $ do
-- assert that the next request will indeed be an insert
@@ -294,6 +310,7 @@ spec actualPgVersion =
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|]
`shouldRespondWith`
[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
@@ -307,6 +324,7 @@ spec actualPgVersion =
[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
@@ -319,6 +337,7 @@ spec actualPgVersion =
[json|[ { "id": 10 } ]|]
`shouldRespondWith`
[json|[ { "id": 10 } ]|]
{ matchStatus = 201 }
context "Updating row" $ do
it "succeeds on table with single pk col" $ do
@@ -401,7 +420,11 @@ spec actualPgVersion =
request methodPut "/tiobe_pls?name=eq.Ruby"
[("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.object+json")]
[json| [ { "name": "Ruby", "rank": 11 } ]|]
`shouldRespondWith` [json|{ "name": "Ruby", "rank": 11 }|] { matchHeaders = [matchContentTypeSingular] }
`shouldRespondWith`
[json|{ "name": "Ruby", "rank": 11 }|]
{ matchStatus = 201
, matchHeaders = [matchContentTypeSingular] }
context "with a camel case pk column" $ do
it "works with POST and merge-duplicates" $ do
+7 -5
View File
@@ -118,11 +118,12 @@ shouldPersistMutations reqHeaders respHeaders = do
it "does persist put" $ do
request methodPut "/items?id=eq.0"
reqHeaders
[json|{"id":0}|]
reqHeaders
[json|{"id":0}|]
`shouldRespondWith`
[json|[{"id":0}]|]
{ matchHeaders = respHeaders }
[json|[{"id":0}]|]
{ matchStatus = 201
, matchHeaders = respHeaders }
get "/items?id=eq.0"
`shouldRespondWith`
[json|[{"id":0}]|]
@@ -175,7 +176,8 @@ shouldNotPersistMutations reqHeaders respHeaders = do
[json|{"id":0}|]
`shouldRespondWith`
[json|[{"id":0}]|]
{ matchHeaders = respHeaders }
{ matchStatus = 201
, matchHeaders = respHeaders }
get "/items?id=eq.0"
`shouldRespondWith`
[json|[]|]