Skip to content

Commit 19a925f

Browse files
authored
Merge pull request #6457 from neo4j/6448-dev
6448 dev
2 parents 3bed953 + aeda11b commit 19a925f

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

.changeset/chubby-nails-count.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@neo4j/graphql": patch
3+
---
4+
5+
Fix Cypher error on nested update operations with authorization

packages/graphql/src/translate/create-update-and-params.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ export default function createUpdateAndParams({
240240

241241
if (subqueries) {
242242
innerUpdate.push(subqueries);
243+
if (whereStrs.length) {
244+
innerUpdate.push("WITH *");
245+
}
243246
}
244247
}
245248

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
import type { UniqueType } from "../../utils/graphql-types";
21+
import { TestHelper } from "../../utils/tests-helper";
22+
23+
describe("https://github.com/neo4j/graphql/issues/6448", () => {
24+
const testHelper = new TestHelper();
25+
let Movie: UniqueType;
26+
let Studio: UniqueType;
27+
let Actor: UniqueType;
28+
29+
let typeDefs: string;
30+
const secret = "sssh";
31+
32+
beforeAll(async () => {
33+
Movie = testHelper.createUniqueType("Movie");
34+
Studio = testHelper.createUniqueType("Studio");
35+
Actor = testHelper.createUniqueType("Actor");
36+
37+
typeDefs = /* GraphQL */ `
38+
type ${Movie}
39+
@authorization(validate: [{ operations: [UPDATE], where: { node: { MyRoles_INCLUDES: "admin" } } }])
40+
@node {
41+
Id: ID!
42+
Name: String!
43+
MyRoles: [String!]!
44+
@cypher(
45+
statement: """
46+
MATCH (this)
47+
RETURN ['admin'] as roles
48+
"""
49+
columnName: "roles"
50+
)
51+
Actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN)
52+
Studio: [${Studio}!]! @relationship(type: "PRODUCED_BY", direction: OUT)
53+
}
54+
55+
type ${Studio}
56+
@authorization(validate: [{ operations: [UPDATE], where: { node: { MyRoles_INCLUDES: "admin" } } }])
57+
@node {
58+
Id: ID!
59+
Name: String!
60+
Movies: [${Movie}!]! @relationship(type: "PRODUCED_BY", direction: IN)
61+
MyRoles: [String!]!
62+
@cypher(
63+
statement: """
64+
MATCH (this)
65+
RETURN ['admin'] as roles
66+
"""
67+
columnName: "roles"
68+
)
69+
}
70+
71+
type ${Actor}
72+
@authorization(validate: [{ operations: [UPDATE], where: { node: { MyRoles_INCLUDES: "admin" } } }])
73+
@node {
74+
Id: ID!
75+
Name: String!
76+
Movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT)
77+
MyRoles: [String!]!
78+
@cypher(
79+
statement: """
80+
MATCH (this)
81+
RETURN ['admin'] as roles
82+
"""
83+
columnName: "roles"
84+
)
85+
}
86+
`;
87+
88+
await testHelper.executeCypher(`
89+
CREATE (:${Studio} { Name: "A Studio" })<-[:PRODUCED_BY]-(:${Movie} { Name: "A Movie" })
90+
CREATE (:${Actor} {Name: "An Actor"})
91+
`);
92+
await testHelper.initNeo4jGraphQL({
93+
typeDefs,
94+
features: {
95+
authorization: {
96+
key: secret,
97+
},
98+
},
99+
});
100+
});
101+
102+
afterAll(async () => {
103+
await testHelper.close();
104+
});
105+
106+
test("mutation is valid cypher", async () => {
107+
const query = /* GraphQL */ `
108+
mutation {
109+
${Studio.operations.update}(
110+
where: { Name: {eq: "A Studio"} }
111+
update: {
112+
Movies: [
113+
{
114+
update: {
115+
node: { Actors: [{ connect: [{ where: { node: { Name: {eq: "An Actor"} } } }] }] }
116+
where: { node: { Name: {eq: "A Movie"} } }
117+
}
118+
}
119+
]
120+
}
121+
) {
122+
info {
123+
relationshipsCreated
124+
}
125+
}
126+
}
127+
`;
128+
129+
const token = testHelper.createBearerToken(secret);
130+
131+
const queryResult = await testHelper.executeGraphQLWithToken(query, token);
132+
133+
expect(queryResult.errors).toBeUndefined();
134+
expect(queryResult.data).toEqual({
135+
[Studio.operations.update]: {
136+
info: {
137+
relationshipsCreated: 1,
138+
},
139+
},
140+
});
141+
});
142+
});

0 commit comments

Comments
 (0)