diff --git a/CHANGELOG.md b/CHANGELOG.md index 88c4e2f6d..70a2392f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #3795, Clarify `Accept: vnd.pgrst.object` error message - @steve-chavez - #3697, #3602, Handle queries on non-existing table gracefully - @taimoorzaeem - #3600, #3926, Improve JWT errors - @taimoorzaeem + - #3013, Fix `order=` with POST, PATCH, PUT and DELETE requests - @taimoorzaeem ### Changed diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index 17ba08ab2..df40c248a 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -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) addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree -addOrders ctx ApiRequest{..} rReq = - case iAction of - ActDb (ActRelationMut _ _) -> Right rReq - _ -> foldr addOrderToNode (Right rReq) qsOrder +addOrders ctx ApiRequest{..} rReq = foldr addOrderToNode (Right rReq) qsOrder where QueryParams.QueryParams{..} = iQueryParams diff --git a/test/spec/Feature/Query/DeleteSpec.hs b/test/spec/Feature/Query/DeleteSpec.hs index 788482c8b..486446643 100644 --- a/test/spec/Feature/Query/DeleteSpec.hs +++ b/test/spec/Feature/Query/DeleteSpec.hs @@ -149,3 +149,15 @@ spec = { matchStatus = 204 , 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"] + } diff --git a/test/spec/Feature/Query/UpdateSpec.hs b/test/spec/Feature/Query/UpdateSpec.hs index 7b285a629..43c8ff411 100644 --- a/test/spec/Feature/Query/UpdateSpec.hs +++ b/test/spec/Feature/Query/UpdateSpec.hs @@ -426,6 +426,20 @@ spec = do , 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" $ request methodPatch "/web_content?id=eq.0&select=id,name,web_content(name)&web_content.name=like.f*" [("Prefer", "return=representation")] @@ -703,3 +717,16 @@ spec = do 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} |] `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"] + } diff --git a/test/spec/Feature/Query/UpsertSpec.hs b/test/spec/Feature/Query/UpsertSpec.hs index d875cd4c9..2f582e538 100644 --- a/test/spec/Feature/Query/UpsertSpec.hs +++ b/test/spec/Feature/Query/UpsertSpec.hs @@ -526,3 +526,27 @@ spec = } get "/UnitTest?idUnitTest=eq.1" `shouldRespondWith` [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] + } diff --git a/test/spec/fixtures/data.sql b/test/spec/fixtures/data.sql index deaf8a2ca..6a58e6ce6 100644 --- a/test/spec/fixtures/data.sql +++ b/test/spec/fixtures/data.sql @@ -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'); 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); diff --git a/test/spec/fixtures/privileges.sql b/test/spec/fixtures/privileges.sql index 246d0c37c..49132f79b 100644 --- a/test/spec/fixtures/privileges.sql +++ b/test/spec/fixtures/privileges.sql @@ -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 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; diff --git a/test/spec/fixtures/schema.sql b/test/spec/fixtures/schema.sql index 39bbcd58d..164e6fd7c 100644 --- a/test/spec/fixtures/schema.sql +++ b/test/spec/fixtures/schema.sql @@ -3762,3 +3762,20 @@ $$ language sql; create function test.text_search_vector(test.tsearch_to_tsvector) returns tsvector AS $$ select to_tsvector('simple', $1.text_search) $$ 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 +);