Allow embeds alias to be used in filters, fix #821
This commit is contained in:
committed by
Steve Chávez
parent
062a5581f5
commit
32c7e32bdf
@@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- The configuration (e.g. `postgrest.conf`) now accepts arbitrary settings that will be passed through as session-local database settings. This can be used to pass in secret keys directly as strings, or via OS environment variables. For instance: `app.settings.jwt_secret = "$(MYAPP_JWT_SECRET)"` will take `MYAPP_JWT_SECRET` from the environment and make it available to postgresql functions as `current_setting('app.settings.jwt_secret')`. Only `app.settings.*` values in the configuration file are treated in this way. - @canadaduane
|
||||
- #256, Add support for bulk UPSERT with POST and single UPSERT with PUT - @steve-chavez
|
||||
- #1078, Add ability to specify source column in embed - @steve-chavez
|
||||
- #821, Allow embeds alias to be used in filters - @steve-chavez
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -291,25 +291,13 @@ addLogicTree :: (EmbedPath, LogicTree) -> ReadRequest -> ReadRequest
|
||||
addLogicTree = addProperty addLogicTreeToNode
|
||||
|
||||
addProperty :: (a -> ReadRequest -> ReadRequest) -> (EmbedPath, a) -> ReadRequest -> ReadRequest
|
||||
addProperty f ([], a) n = f a n
|
||||
addProperty f (path, a) (Node rn forest) =
|
||||
case targetNode of
|
||||
addProperty f ([], a) rr = f a rr
|
||||
addProperty f (targetNodeName:remainingPath, a) (Node rn forest) =
|
||||
case pathNode of
|
||||
Nothing -> Node rn forest -- the property is silenty dropped in the Request does not contain the required path
|
||||
Just tn -> Node rn (addProperty f (remainingPath, a) tn:restForest)
|
||||
Just tn -> Node rn (addProperty f (remainingPath, a) tn:delete tn forest)
|
||||
where
|
||||
targetNodeName:remainingPath = path
|
||||
(targetNode,restForest) = splitForest targetNodeName forest
|
||||
splitForest :: NodeName -> Forest ReadNode -> (Maybe ReadRequest, Forest ReadNode)
|
||||
splitForest name forst =
|
||||
case maybeNode of
|
||||
Nothing -> (Nothing,forest)
|
||||
Just node -> (Just node, delete node forest)
|
||||
where
|
||||
maybeNode :: Maybe ReadRequest
|
||||
maybeNode = find fnd forst
|
||||
where
|
||||
fnd :: ReadRequest -> Bool
|
||||
fnd (Node (_,(n,_,_,_,_)) _) = n == name
|
||||
pathNode = find (\(Node (_,(nodeName,_,alias,_,_)) _) -> nodeName == targetNodeName || alias == Just targetNodeName) forest
|
||||
|
||||
mutateRequest :: ApiRequest -> TableName -> [Text] -> [FieldName] -> Either Response MutateRequest
|
||||
mutateRequest apiRequest tName pkCols fldNames = mapLeft apiRequestError $
|
||||
|
||||
@@ -454,6 +454,63 @@ spec = do
|
||||
[json|[{"id":1,"users":[{"id":1},{"id":3}]},{"id":2,"users":[{"id":1}]},{"id":3,"users":[{"id":1}]},{"id":4,"users":[{"id":1}]},{"id":5,"users":[{"id":2},{"id":3}]},{"id":6,"users":[{"id":2}]},{"id":7,"users":[{"id":2}]},{"id":8,"users":[]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "aliased embeds" $ do
|
||||
it "works with child relation" $
|
||||
get "/space?select=id,zones:zone(id,name),stores:zone(id,name)&zones.zone_type_id=eq.2&stores.zone_type_id=eq.3" `shouldRespondWith`
|
||||
[json|[
|
||||
{ "id":1,
|
||||
"zones": [ {"id":1,"name":"zone 1"}, {"id":2,"name":"zone 2"}],
|
||||
"stores": [ {"id":3,"name":"store 3"}, {"id":4,"name":"store 4"}]}
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with many to many relation" $
|
||||
get "/users?select=id,designTasks:tasks(id,name),codeTasks:tasks(id,name)&designTasks.name=like.*Design*&codeTasks.name=like.*Code*" `shouldRespondWith`
|
||||
[json|[
|
||||
{ "id":1,
|
||||
"designTasks":[ { "id":1, "name":"Design w7" }, { "id":3, "name":"Design w10" } ],
|
||||
"codeTasks":[ { "id":2, "name":"Code w7" }, { "id":4, "name":"Code w10" } ] },
|
||||
{ "id":2,
|
||||
"designTasks":[ { "id":5, "name":"Design IOS" }, { "id":7, "name":"Design OSX" } ],
|
||||
"codeTasks":[ { "id":6, "name":"Code IOS" } ] },
|
||||
{ "id":3,
|
||||
"designTasks":[ { "id":1, "name":"Design w7" }, { "id":5, "name":"Design IOS" } ],
|
||||
"codeTasks":[ ] }
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with an aliased child plus non aliased child" $
|
||||
get "/projects?select=id,name,designTasks:tasks{name,users{id,name}}&designTasks.name=like.*Design*&designTasks.users.id=in.(1,2)" `shouldRespondWith`
|
||||
[json|[
|
||||
{
|
||||
"id":1, "name":"Windows 7",
|
||||
"designTasks":[ { "name":"Design w7", "users":[ { "id":1, "name":"Angela Martin" } ] } ] },
|
||||
{
|
||||
"id":2, "name":"Windows 10",
|
||||
"designTasks":[ { "name":"Design w10", "users":[ { "id":1, "name":"Angela Martin" } ] } ] },
|
||||
{
|
||||
"id":3, "name":"IOS",
|
||||
"designTasks":[ { "name":"Design IOS", "users":[ { "id":2, "name":"Michael Scott" } ] } ] },
|
||||
{
|
||||
"id":4, "name":"OSX",
|
||||
"designTasks":[ { "name":"Design OSX", "users":[ { "id":2, "name":"Michael Scott" } ] } ] },
|
||||
{
|
||||
"id":5, "name":"Orphan",
|
||||
"designTasks":[ ] }
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with two aliased childs embeds plus and/or" $
|
||||
get "/entities?select=id,childs:child_entities{id,gChilds:grandchild_entities{id}}&childs.and=(id.in.(1,2,3))&childs.gChilds.or=(id.eq.1,id.eq.2)" `shouldRespondWith`
|
||||
[json|[
|
||||
{ "id":1,
|
||||
"childs":[
|
||||
{"id":1,"gChilds":[{"id":1}, {"id":2}]},
|
||||
{"id":2,"gChilds":[]}]},
|
||||
{ "id":2,
|
||||
"childs":[
|
||||
{"id":3,"gChilds":[]}]},
|
||||
{ "id":3,"childs":[]},
|
||||
{ "id":4,"childs":[]}
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "tables with self reference foreign keys" $ do
|
||||
context "one self reference foreign key" $ do
|
||||
it "embeds parents recursively" $
|
||||
|
||||
Vendored
+9
@@ -399,3 +399,12 @@ INSERT INTO message VALUES (2, 'Hi John', 2, 1);
|
||||
INSERT INTO message VALUES (3, 'How are you doing?', 1, 2);
|
||||
INSERT INTO message VALUES (4, 'Hey Julie', 3, 4);
|
||||
INSERT INTO message VALUES (5, 'What''s up Jake', 4, 3);
|
||||
|
||||
TRUNCATE TABLE space CASCADE;
|
||||
INSERT INTO space VALUES (1, 'space 1');
|
||||
|
||||
TRUNCATE TABLE zone CASCADE;
|
||||
INSERT INTO zone VALUES (1, 'zone 1', 2, 1);
|
||||
INSERT INTO zone VALUES (2, 'zone 2', 2, 1);
|
||||
INSERT INTO zone VALUES (3, 'store 3', 3, 1);
|
||||
INSERT INTO zone VALUES (4, 'store 4', 3, 1);
|
||||
|
||||
Vendored
+2
@@ -76,6 +76,8 @@ GRANT ALL ON TABLE
|
||||
, person
|
||||
, message
|
||||
, person_detail
|
||||
, space
|
||||
, zone
|
||||
TO postgrest_test_anonymous;
|
||||
|
||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||
|
||||
Vendored
+10
@@ -1425,3 +1425,13 @@ create view person_detail as
|
||||
from person p
|
||||
join lateral (select message.sender, count(message.id) as count from message group by message.sender) s on s.sender = p.id
|
||||
join lateral (select message.recipient, count(message.id) as count from message group by message.recipient) r on r.recipient = p.id;
|
||||
|
||||
create table space(
|
||||
id integer primary key,
|
||||
name text);
|
||||
|
||||
create table zone(
|
||||
id integer primary key,
|
||||
name text,
|
||||
zone_type_id integer,
|
||||
space_id integer references space(id));
|
||||
|
||||
Reference in New Issue
Block a user