fix: Fix ordering with mutation queries
This commit is contained in:
committed by
Steve Chavez
parent
5ff51de36f
commit
fc35d6d0f8
@@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- #3795, Clarify `Accept: vnd.pgrst.object` error message - @steve-chavez
|
- #3795, Clarify `Accept: vnd.pgrst.object` error message - @steve-chavez
|
||||||
- #3697, #3602, Handle queries on non-existing table gracefully - @taimoorzaeem
|
- #3697, #3602, Handle queries on non-existing table gracefully - @taimoorzaeem
|
||||||
- #3600, #3926, Improve JWT errors - @taimoorzaeem
|
- #3600, #3926, Improve JWT errors - @taimoorzaeem
|
||||||
|
- #3013, Fix `order=` with POST, PATCH, PUT and DELETE requests - @taimoorzaeem
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -770,10 +770,7 @@ addFilters ctx ApiRequest{..} rReq =
|
|||||||
updateNode (\flt (Node q@ReadPlan{from=fromTable, where_=lf} f) -> Node q{ReadPlan.where_=addFilterToLogicForest (resolveFilter ctx{qi=fromTable} flt) lf} f)
|
updateNode (\flt (Node q@ReadPlan{from=fromTable, where_=lf} f) -> Node q{ReadPlan.where_=addFilterToLogicForest (resolveFilter ctx{qi=fromTable} flt) lf} f)
|
||||||
|
|
||||||
addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
|
||||||
addOrders ctx ApiRequest{..} rReq =
|
addOrders ctx ApiRequest{..} rReq = foldr addOrderToNode (Right rReq) qsOrder
|
||||||
case iAction of
|
|
||||||
ActDb (ActRelationMut _ _) -> Right rReq
|
|
||||||
_ -> foldr addOrderToNode (Right rReq) qsOrder
|
|
||||||
where
|
where
|
||||||
QueryParams.QueryParams{..} = iQueryParams
|
QueryParams.QueryParams{..} = iQueryParams
|
||||||
|
|
||||||
|
|||||||
@@ -149,3 +149,15 @@ spec =
|
|||||||
{ matchStatus = 204
|
{ matchStatus = 204
|
||||||
, matchHeaders = [matchHeaderAbsent hContentType]
|
, matchHeaders = [matchHeaderAbsent hContentType]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context "with ordering" $
|
||||||
|
it "works with request method DELETE and embedded resource" $ do
|
||||||
|
request methodDelete "/artists?id=lt.3&select=id,name,albums(title)&order=id.desc"
|
||||||
|
[("Prefer", "return=representation")]
|
||||||
|
""
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json| [ {"id":2,"name":"black country, new road","albums":[{"title": "ants from up above"}]}, {"id":1,"name":"duster","albums":[{"title": "stratosphere"},{"title": "contemporary movement"}]}]
|
||||||
|
|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -426,6 +426,20 @@ spec = do
|
|||||||
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it "with ordering on top-level resource" $
|
||||||
|
request methodPatch "/no_pk?order=a.desc"
|
||||||
|
[("Prefer", "return=representation")]
|
||||||
|
[json|{ "b": "1" }|]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|
|
||||||
|
[ { "a": null, "b": "1" },
|
||||||
|
{ "a": "2", "b": "1" },
|
||||||
|
{ "a": "1", "b": "1" } ]
|
||||||
|
|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
||||||
|
}
|
||||||
|
|
||||||
it "with filters" $
|
it "with filters" $
|
||||||
request methodPatch "/web_content?id=eq.0&select=id,name,web_content(name)&web_content.name=like.f*"
|
request methodPatch "/web_content?id=eq.0&select=id,name,web_content(name)&web_content.name=like.f*"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
@@ -703,3 +717,16 @@ spec = do
|
|||||||
request methodPatch "/datarep_todos_computed?id=eq.2001&columns=label_color" [("Prefer", "return=representation")]
|
request methodPatch "/datarep_todos_computed?id=eq.2001&columns=label_color" [("Prefer", "return=representation")]
|
||||||
[json| {"due_at": "2019-01-03T11:00:00Z", "smth": "here", "label_color": "invalid", "fake_id": 13} |]
|
[json| {"due_at": "2019-01-03T11:00:00Z", "smth": "here", "label_color": "invalid", "fake_id": 13} |]
|
||||||
`shouldRespondWith` 200
|
`shouldRespondWith` 200
|
||||||
|
|
||||||
|
context "with ordering" $
|
||||||
|
it "works with request method PATCH and embedded resource" $
|
||||||
|
request methodPatch "/web_content?id=eq.0&select=id,name,web_content(name)&web_content.order=name.asc"
|
||||||
|
[("Prefer", "return=representation")]
|
||||||
|
[json|{"name": "tardis-patched"}|]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|
|
||||||
|
[ { "id": 0, "name": "tardis-patched", "web_content": [ { "name": "bar" }, { "name": "fezz" }, { "name": "foo" } ]} ]
|
||||||
|
|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -526,3 +526,27 @@ spec =
|
|||||||
}
|
}
|
||||||
get "/UnitTest?idUnitTest=eq.1" `shouldRespondWith`
|
get "/UnitTest?idUnitTest=eq.1" `shouldRespondWith`
|
||||||
[json| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|]
|
[json| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|]
|
||||||
|
|
||||||
|
context "with ordering" $ do
|
||||||
|
it "works with request method PUT and embedded resource" $
|
||||||
|
request methodPut "/web_content?id=eq.0&select=id,name,web_content(name)&web_content.order=name.asc"
|
||||||
|
[("Prefer", "return=representation")]
|
||||||
|
[json|{ "id": 0, "name": "tardis-upserted", "p_web_id": 5 }|]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|
|
||||||
|
[ { "id": 0, "name": "tardis-upserted", "web_content": [ { "name": "bar" }, { "name": "fezz" }, { "name": "foo" } ]} ]
|
||||||
|
|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson, "Preference-Applied" <:> "return=representation"]
|
||||||
|
}
|
||||||
|
|
||||||
|
it "works with batch upserts and embedded resource" $
|
||||||
|
request methodPost "/artists?select=id,name,albums(title)&order=id.desc"
|
||||||
|
[("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||||
|
[json| [{ "id": 1, "name": "duster-updated" },
|
||||||
|
{ "id": 2, "name": "bcnr-updated" }] |]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json| [{"id":2,"name":"bcnr-updated","albums":[{"title": "ants from up above"}]}, {"id":1,"name":"duster-updated","albums":[{"title": "stratosphere"}, {"title": "contemporary movement"}]}] |]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson]
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+12
@@ -921,3 +921,15 @@ INSERT INTO tsearch_to_tsvector(text_search) VALUES ('C''est un peu amusant de f
|
|||||||
INSERT INTO tsearch_to_tsvector(text_search) VALUES ('Es ist eine Art Spaß, das Unmögliche zu machen');
|
INSERT INTO tsearch_to_tsvector(text_search) VALUES ('Es ist eine Art Spaß, das Unmögliche zu machen');
|
||||||
|
|
||||||
UPDATE tsearch_to_tsvector SET jsonb_search = jsonb_build_object('text_search', text_search);
|
UPDATE tsearch_to_tsvector SET jsonb_search = jsonb_build_object('text_search', text_search);
|
||||||
|
|
||||||
|
|
||||||
|
TRUNCATE TABLE artists CASCADE;
|
||||||
|
INSERT INTO artists
|
||||||
|
VALUES (1, 'duster'), (2, 'black country, new road'), (3, 'bjork');
|
||||||
|
|
||||||
|
TRUNCATE TABLE albums CASCADE;
|
||||||
|
INSERT INTO albums
|
||||||
|
VALUES (1, 'stratosphere', 1),
|
||||||
|
(2, 'ants from up above',2),
|
||||||
|
(3, 'vespertine',3),
|
||||||
|
(4, 'contemporary movement', 1);
|
||||||
|
|||||||
Vendored
+3
@@ -61,3 +61,6 @@ REVOKE EXECUTE ON FUNCTION privileged_hello(text) FROM PUBLIC; -- All functions
|
|||||||
GRANT EXECUTE ON FUNCTION privileged_hello(text) TO postgrest_test_author;
|
GRANT EXECUTE ON FUNCTION privileged_hello(text) TO postgrest_test_author;
|
||||||
|
|
||||||
GRANT USAGE ON SCHEMA test TO postgrest_test_default_role;
|
GRANT USAGE ON SCHEMA test TO postgrest_test_default_role;
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE artists TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE albums TO postgrest_test_anonymous;
|
||||||
|
|||||||
Vendored
+17
@@ -3762,3 +3762,20 @@ $$ language sql;
|
|||||||
create function test.text_search_vector(test.tsearch_to_tsvector) returns tsvector AS $$
|
create function test.text_search_vector(test.tsearch_to_tsvector) returns tsvector AS $$
|
||||||
select to_tsvector('simple', $1.text_search)
|
select to_tsvector('simple', $1.text_search)
|
||||||
$$ language sql;
|
$$ language sql;
|
||||||
|
|
||||||
|
-- to test ordering with mutations
|
||||||
|
CREATE TABLE test.artists (
|
||||||
|
id int PRIMARY KEY,
|
||||||
|
name text
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE test.albums (
|
||||||
|
id int PRIMARY KEY,
|
||||||
|
title text,
|
||||||
|
artist_id int,
|
||||||
|
|
||||||
|
CONSTRAINT fk_artist
|
||||||
|
FOREIGN KEY (artist_id) REFERENCES artists (id)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user