From 39d9ecff716c64c8d5c7e024c4d5a2192ea96e72 Mon Sep 17 00:00:00 2001 From: Michael Webb <28074382+mjfwebb@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:34:56 +0100 Subject: [PATCH 01/72] chore: update labeler to commit hash pin of version 1.7.2 (#4319) --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 45d917564c6..95c3bfd9816 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -13,6 +13,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: srvaroa/labeler@c6b5a7f36f14b184378092f75437bfd2b9facb97 # v1.4 + - uses: srvaroa/labeler@acf01918af3d0369c12611117826c1cf718b82ce # v1.7.2 env: GITHUB_TOKEN: ${{ secrets.NEO4J_TEAM_GRAPHQL_PERSONAL_ACCESS_TOKEN }} From 7eb78cf2ba6852b8ec1244d219de82027a5672d8 Mon Sep 17 00:00:00 2001 From: Darrell Warde Date: Tue, 21 Nov 2023 14:39:26 +0000 Subject: [PATCH 02/72] Apply Renovate best practices by default --- renovate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 1ad8291fb58..996e471d767 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,5 @@ { - "extends": ["config:base"], + "extends": ["config:best-practices"], "baseBranches": ["dev"], "rebaseWhen": "auto", "automerge": true, From 1bf077318d0ddbf730edf53d635f507e36fc7374 Mon Sep 17 00:00:00 2001 From: MacondoExpress Date: Tue, 21 Nov 2023 14:46:30 +0000 Subject: [PATCH 03/72] Fix 4292 (#4309) * fix authorization not addedd inside Cypher.Call * fix predicate/subquery order on connection operation * add test for 4292 * Create warm-students-float.md * Update packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts Co-authored-by: Michael Webb <28074382+mjfwebb@users.noreply.github.com> * remove graphql-tag import on test file --------- Co-authored-by: Michael Webb <28074382+mjfwebb@users.noreply.github.com> --- .changeset/warm-students-float.md | 5 + .../ast/operations/ConnectionReadOperation.ts | 58 ++-- .../tests/integration/issues/4268.int.test.ts | 7 +- .../tests/integration/issues/4292.int.test.ts | 277 ++++++++++++++++++ .../graphql/tests/tck/issues/4292.test.ts | 276 +++++++++++++++++ 5 files changed, 597 insertions(+), 26 deletions(-) create mode 100644 .changeset/warm-students-float.md create mode 100644 packages/graphql/tests/integration/issues/4292.int.test.ts create mode 100644 packages/graphql/tests/tck/issues/4292.test.ts diff --git a/.changeset/warm-students-float.md b/.changeset/warm-students-float.md new file mode 100644 index 00000000000..c77a95c2762 --- /dev/null +++ b/.changeset/warm-students-float.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Fix an authorization bug present for validation rules with a predicate against a nested field and the Connection API. https://github.com/neo4j/graphql/issues/4292. diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts index 686b582b429..33d33c3ab09 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts @@ -137,7 +137,7 @@ export class ConnectionReadOperation extends Operation { private transpileNested(context: QueryASTContext): OperationTranspileResult { if (!context.target || !this.relationship) throw new Error(); - const node = createNodeFromEntity(this.target, context.neo4jGraphQLContext); + const targetNode = createNodeFromEntity(this.target, context.neo4jGraphQLContext); const relationship = new Cypher.Relationship({ type: this.relationship.type }); const relDirection = this.relationship.getCypherDirection(this.directed); @@ -145,26 +145,28 @@ export class ConnectionReadOperation extends Operation { .withoutLabels() .related(relationship) .withDirection(relDirection) - .to(node); + .to(targetNode); - const nestedContext = context.push({ target: node, relationship }); + const nestedContext = context.push({ target: targetNode, relationship }); const { preSelection, selectionClause: clause } = this.getSelectionClauses(nestedContext, pattern); - const predicates = this.filters.map((f) => f.getPredicate(nestedContext)); - const authPredicate = this.getAuthFilterPredicate(nestedContext); + const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext).map((sq) => + new Cypher.Call(sq).innerWith(targetNode) + ); - const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext); + const nodeProjectionSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.nodeFields, [targetNode]); + const predicates = this.filters.map((f) => f.getPredicate(nestedContext)); + const authPredicate = this.getAuthFilterPredicate(nestedContext); const filters = Cypher.and(...predicates, ...authPredicate); - const nodeProjectionSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.nodeFields, [node]); const nodeProjectionMap = new Cypher.Map(); this.nodeFields - .map((f) => f.getProjectionField(node)) + .map((f) => f.getProjectionField(targetNode)) .forEach((p) => { if (typeof p === "string") { - nodeProjectionMap.set(p, node.property(p)); + nodeProjectionMap.set(p, targetNode.property(p)); } else { nodeProjectionMap.set(p); } @@ -174,7 +176,7 @@ export class ConnectionReadOperation extends Operation { const targetNodeName = this.target.name; nodeProjectionMap.set({ __resolveType: new Cypher.Literal(targetNodeName), - __id: Cypher.id(node), + __id: Cypher.id(targetNode), }); } @@ -216,9 +218,13 @@ export class ConnectionReadOperation extends Operation { let extraWithOrder: Cypher.Clause | undefined; if (this.sortFields.length > 0) { - const sortFields = this.getSortFields({ context: nestedContext, nodeVar: node, edgeVar: relationship }); + const sortFields = this.getSortFields({ + context: nestedContext, + nodeVar: targetNode, + edgeVar: relationship, + }); - extraWithOrder = new Cypher.With(relationship, node).orderBy(...sortFields); + extraWithOrder = new Cypher.With(relationship, targetNode).orderBy(...sortFields); } const projectionClauses = new Cypher.With([edgeProjectionMap, edgeVar]) @@ -254,19 +260,25 @@ export class ConnectionReadOperation extends Operation { if (this.relationship) { return this.transpileNested(context); } - if (!hasTarget(context)) throw new Error("No parent node found!"); + if (!hasTarget(context)) { + throw new Error("No parent node found!"); + } - const node = createNodeFromEntity(this.target, context.neo4jGraphQLContext, this.nodeAlias); + const targetNode = createNodeFromEntity(this.target, context.neo4jGraphQLContext, this.nodeAlias); - const { preSelection, selectionClause } = this.getSelectionClauses(context, node); + const { preSelection, selectionClause } = this.getSelectionClauses(context, targetNode); - const predicates = this.filters.map((f) => f.getPredicate(context)); - const authPredicate = this.getAuthFilterPredicate(context); + const { prePaginationSubqueries, postPaginationSubqueries } = this.getPreAndPostSubqueries(context); + + const authFilterSubqueries = this.getAuthFilterSubqueries(context).map((sq) => + new Cypher.Call(sq).innerWith(targetNode) + ); - const authFilterSubqueries = this.getAuthFilterSubqueries(context); + const authPredicate = this.getAuthFilterPredicate(context); + const predicates = this.filters.map((f) => f.getPredicate(context)); const filters = Cypher.and(...predicates, ...authPredicate); - const { prePaginationSubqueries, postPaginationSubqueries } = this.getPreAndPostSubqueries(context); + const nodeProjectionMap = this.getProjectionMap(context); const edgeVar = new Cypher.NamedVariable("edge"); @@ -286,12 +298,12 @@ export class ConnectionReadOperation extends Operation { selectionClause.where(filters); } } - const withNodeAndTotalCount = new Cypher.With([Cypher.collect(node), edgesVar]).with(edgesVar, [ + const withNodeAndTotalCount = new Cypher.With([Cypher.collect(targetNode), edgesVar]).with(edgesVar, [ Cypher.size(edgesVar), totalCount, ]); - const unwindClause = new Cypher.Unwind([edgesVar, node]).with(node, totalCount); + const unwindClause = new Cypher.Unwind([edgesVar, targetNode]).with(targetNode, totalCount); let paginationWith: Cypher.With | undefined; if (this.pagination || this.sortFields.length > 0) { paginationWith = new Cypher.With("*"); @@ -303,12 +315,12 @@ export class ConnectionReadOperation extends Operation { paginationWith.skip(paginationField.skip); } if (this.sortFields.length > 0) { - const sortFields = this.getSortFields({ context, nodeVar: node, aliased: true }); + const sortFields = this.getSortFields({ context, nodeVar: targetNode, aliased: true }); paginationWith.orderBy(...sortFields); } } - const withProjection = new Cypher.With([edgeProjectionMap, edgeVar], totalCount, node).with( + const withProjection = new Cypher.With([edgeProjectionMap, edgeVar], totalCount, targetNode).with( [Cypher.collect(edgeVar), edgesVar], totalCount ); diff --git a/packages/graphql/tests/integration/issues/4268.int.test.ts b/packages/graphql/tests/integration/issues/4268.int.test.ts index 85db1ce1f6a..f6f932729d1 100644 --- a/packages/graphql/tests/integration/issues/4268.int.test.ts +++ b/packages/graphql/tests/integration/issues/4268.int.test.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import { type Driver } from "neo4j-driver"; +import type { Driver } from "neo4j-driver"; import Neo4j from "../neo4j"; import { Neo4jGraphQL } from "../../../src/classes"; import gql from "graphql-tag"; @@ -112,9 +112,10 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const response = await graphql({ schema, source: query, - contextValue: neo4j.getContextValues({ jwt: { id: "some-id", email: "some-email", roles: ["not-an-admin"] } }), + contextValue: neo4j.getContextValues({ + jwt: { id: "some-id", email: "some-email", roles: ["not-an-admin"] }, + }), }); expect((response.errors as any[])[0].message).toBe("Forbidden"); - }); }); diff --git a/packages/graphql/tests/integration/issues/4292.int.test.ts b/packages/graphql/tests/integration/issues/4292.int.test.ts new file mode 100644 index 00000000000..08c4e392f94 --- /dev/null +++ b/packages/graphql/tests/integration/issues/4292.int.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Driver } from "neo4j-driver"; +import Neo4j from "../neo4j"; +import { Neo4jGraphQL } from "../../../src/classes"; +import { graphql } from "graphql"; +import { UniqueType } from "../../utils/graphql-types"; +import { cleanNodes } from "../../utils/clean-nodes"; + +describe("https://github.com/neo4j/graphql/issues/4292", () => { + let driver: Driver; + let neo4j: Neo4j; + let neo4jGraphql: Neo4jGraphQL; + const User = new UniqueType("User"); + const Group = new UniqueType("Group"); + const Person = new UniqueType("Person"); + const Admin = new UniqueType("Admin"); + const Contributor = new UniqueType("Contributor"); + + beforeAll(async () => { + neo4j = new Neo4j(); + driver = await neo4j.getDriver(); + const typeDefs = /* GraphQL */ ` + type JWT @jwt { + id: ID! + email: String! + roles: [String!]! + } + + type ${User.name} { + id: ID! @unique + email: String! @unique + name: String + creator: [${Group.name}!]! @relationship(type: "CREATOR_OF", direction: OUT) + admin: [${Admin.name}!]! @relationship(type: "IS_USER", direction: IN) + contributor: [${Contributor.name}!]! @relationship(type: "IS_USER", direction: IN) + invitations: [Invitee!]! @relationship(type: "CREATOR_OF", direction: OUT) + roles: [String!]! + } + + type ${Group.name} { + id: ID! @id @unique + name: String + members: [${Person.name}!]! @relationship(type: "MEMBER_OF", direction: IN) + creator: ${User.name}! + @relationship(type: "CREATOR_OF", direction: IN) + @settable(onCreate: true, onUpdate: true) + admins: [${Admin.name}!]! @relationship(type: "ADMIN_OF", direction: IN) + contributors: [${Contributor.name}!]! @relationship(type: "CONTRIBUTOR_TO", direction: IN) + } + + type ${Person.name} + @authorization( + validate: [ + { + operations: [CREATE] + where: { node: { group: { creator: { roles_INCLUDES: "plan:paid" } } } } + } + { + operations: [DELETE] + where: { + OR: [ + { node: { creator: { id: "$jwt.uid" } } } + { node: { group: { admins_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { creator: { id: "$jwt.uid" } } } } + ] + } + } + { + operations: [READ, UPDATE] + where: { + OR: [ + { node: { creator: { id: "$jwt.uid" } } } + { node: { group: { admins_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { contributors_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { creator: { id: "$jwt.uid" } } } } + ] + } + } + ] + ) { + id: ID! @id @unique + name: String! + creator: ${User.name}! + @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + @settable(onCreate: true, onUpdate: true) + group: ${Group.name}! @relationship(type: "MEMBER_OF", direction: OUT) + partners: [${Person.name}!]! + @relationship( + type: "PARTNER_OF" + queryDirection: UNDIRECTED_ONLY + direction: OUT + properties: "PartnerOf" + ) + } + + enum InviteeRole { + ADMIN + CONTRIBUTOR + } + + enum InviteeStatus { + INVITED + ACCEPTED + } + + interface Invitee { + id: ID! @id + email: String! + name: String + creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN) + group: ${Group.name}! @relationship(type: "ADMIN_OF", direction: OUT) + status: InviteeStatus! @default(value: INVITED) + user: ${User.name} @relationship(type: "IS_USER", direction: OUT) + role: InviteeRole! + } + + type ${Admin.name} implements Invitee { + id: ID! @unique + group: ${Group.name}! + creator: ${User.name}! + email: String! + name: String + status: InviteeStatus! + user: ${User.name} + role: InviteeRole! @default(value: ADMIN) + } + + type ${Contributor.name} implements Invitee { + id: ID! @unique + group: ${Group.name}! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) + creator: ${User.name}! + email: String! + name: String + status: InviteeStatus! + user: ${User.name} + role: InviteeRole! @default(value: CONTRIBUTOR) + } + + interface PartnerOf @relationshipProperties { + id: ID! @id + firstDay: Date + lastDay: Date + active: Boolean! @default(value: true) + } + + type JWT @jwt { + roles: [String!]! + } + + type Mutation { + sendInvite(id: ID!, role: InviteeRole!): Boolean! + } + + `; + neo4jGraphql = new Neo4jGraphQL({ + typeDefs, + driver, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const session = await neo4j.getSession(); + try { + await session.run( + ` + CREATE (m:${Person.name} {title: "SomeTitle", id: "person-1", name: "SomePerson"})<-[:CREATOR_OF]-(u:${User.name} { id: "user-1", email: "email-1", roles: ["admin"]}) + CREATE (g:${Group.name} { id: "family_id_1", name: "group-1" })<-[:MEMBER_OF]-(m) + `, + {} + ); + } finally { + await session.close(); + } + }); + + afterAll(async () => { + const session = await neo4j.getSession(); + try { + await cleanNodes(session, [User.name, Group.name, Person.name, Admin.name, Contributor.name]); + } finally { + await session.close(); + } + await driver.close(); + }); + + test("should return groups with valid JWT", async () => { + const schema = await neo4jGraphql.getSchema(); + + const query = /* GraphQL */ ` + query Groups { + ${Group.plural}(where: { id: "family_id_1" }) { + id + name + members { + id + name + partnersConnection { + edges { + active + firstDay + lastDay + } + } + } + } + } + `; + + const response = await graphql({ + schema, + source: query, + contextValue: neo4j.getContextValues({ jwt: { uid: "user-1", email: "some-email", roles: ["admin"] } }), + }); + expect(response.errors).toBeFalsy(); + expect(response.data?.[Group.plural]).toStrictEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "family_id_1", + name: "group-1", + members: [expect.objectContaining({ id: "person-1", name: "SomePerson" })], + }), + ]) + ); + }); + + test("should raise Forbidden with invalid JWT", async () => { + const schema = await neo4jGraphql.getSchema(); + + const query = /* GraphQL */ ` + query Groups { + ${Group.plural}(where: { id: "family_id_1" }) { + id + name + members { + id + name + partnersConnection { + edges { + active + firstDay + lastDay + } + } + } + } + } + `; + + const response = await graphql({ + schema, + source: query, + contextValue: neo4j.getContextValues({ jwt: { uid: "not-user-1", email: "some-email", roles: ["admin"] } }), + }); + expect(response.errors?.[0]?.message).toContain("Forbidden"); + }); +}); diff --git a/packages/graphql/tests/tck/issues/4292.test.ts b/packages/graphql/tests/tck/issues/4292.test.ts new file mode 100644 index 00000000000..451365dc788 --- /dev/null +++ b/packages/graphql/tests/tck/issues/4292.test.ts @@ -0,0 +1,276 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import gql from "graphql-tag"; +import { Neo4jGraphQL } from "../../../src"; +import { createBearerToken } from "../../utils/create-bearer-token"; +import { translateQuery, formatCypher, formatParams } from "../utils/tck-test-utils"; + +describe("https://github.com/neo4j/graphql/issues/4292", () => { + test("authorization subqueries should be wrapped in a Cypher.CALL", async () => { + const typeDefs = /* GraphQL */ ` + type User { + id: ID! @unique + + email: String! @unique + + name: String + + creator: [Group!]! @relationship(type: "CREATOR_OF", direction: OUT) + + admin: [Admin!]! @relationship(type: "IS_USER", direction: IN) + + contributor: [Contributor!]! @relationship(type: "IS_USER", direction: IN) + + invitations: [Invitee!]! @relationship(type: "CREATOR_OF", direction: OUT) + + roles: [String!]! + } + + type Group { + id: ID! @id @unique + + name: String + + members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) + + creator: User! + @relationship(type: "CREATOR_OF", direction: IN) + @settable(onCreate: true, onUpdate: true) + + admins: [Admin!]! @relationship(type: "ADMIN_OF", direction: IN) + + contributors: [Contributor!]! @relationship(type: "CONTRIBUTOR_TO", direction: IN) + } + + type Person + @authorization( + validate: [ + { + operations: [CREATE] + where: { node: { group: { creator: { roles_INCLUDES: "plan:paid" } } } } + } + { + operations: [DELETE] + where: { + OR: [ + { node: { creator: { id: "$jwt.uid" } } } + { node: { group: { admins_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { creator: { id: "$jwt.uid" } } } } + ] + } + } + { + operations: [READ, UPDATE] + where: { + OR: [ + { node: { creator: { id: "$jwt.uid" } } } + { node: { group: { admins_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { contributors_SOME: { user: { id: "$jwt.uid" } } } } } + { node: { group: { creator: { id: "$jwt.uid" } } } } + ] + } + } + ] + ) { + id: ID! @id @unique + + name: String! + + creator: User! + @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + @settable(onCreate: true, onUpdate: true) + + group: Group! @relationship(type: "MEMBER_OF", direction: OUT) + + partners: [Person!]! + @relationship( + type: "PARTNER_OF" + queryDirection: UNDIRECTED_ONLY + direction: OUT + properties: "PartnerOf" + ) + } + + enum InviteeRole { + ADMIN + CONTRIBUTOR + } + + enum InviteeStatus { + INVITED + ACCEPTED + } + + interface Invitee { + id: ID! @id + + email: String! + + name: String + + creator: User! @relationship(type: "CREATOR_OF", direction: IN) + + group: Group! @relationship(type: "ADMIN_OF", direction: OUT) + + status: InviteeStatus! @default(value: INVITED) + + user: User @relationship(type: "IS_USER", direction: OUT) + + role: InviteeRole! + } + + type Admin implements Invitee { + id: ID! @unique + group: Group! + creator: User! + email: String! + name: String + status: InviteeStatus! + user: User + role: InviteeRole! @default(value: ADMIN) + } + + type Contributor implements Invitee { + id: ID! @unique + group: Group! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) + creator: User! + email: String! + name: String + status: InviteeStatus! + user: User + role: InviteeRole! @default(value: CONTRIBUTOR) + } + + interface PartnerOf @relationshipProperties { + id: ID! @id + firstDay: Date + lastDay: Date + active: Boolean! @default(value: true) + } + + type JWT @jwt { + roles: [String!]! + } + + type Mutation { + sendInvite(id: ID!, role: InviteeRole!): Boolean! + } + + extend schema @authentication + `; + + const neoSchema = new Neo4jGraphQL({ typeDefs, features: { authorization: { key: "secret" } } }); + + const query = gql` + query Groups { + groups(where: { id: "family_id_1" }) { + id + name + members { + id + name + partnersConnection { + edges { + active + firstDay + lastDay + } + } + } + } + } + `; + + const token = createBearerToken("secret", { roles: ["admin"], id: "something", email: "something" }); + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "MATCH (this:Group) + WHERE this.id = $param0 + CALL { + WITH this + MATCH (this)<-[this0:MEMBER_OF]-(this1:Person) + OPTIONAL MATCH (this1)<-[:CREATOR_OF]-(this2:User) + WITH *, count(this2) AS creatorCount + OPTIONAL MATCH (this1)-[:MEMBER_OF]->(this3:Group) + WITH *, count(this3) AS groupCount + OPTIONAL MATCH (this1)-[:MEMBER_OF]->(this4:Group) + WITH *, count(this4) AS groupCount + WITH * + CALL { + WITH this1 + MATCH (this1)-[:MEMBER_OF]->(this5:Group) + OPTIONAL MATCH (this5)<-[:CREATOR_OF]-(this6:User) + WITH *, count(this6) AS creatorCount + WITH * + WHERE (creatorCount <> 0 AND ($jwt.uid IS NOT NULL AND this6.id = $jwt.uid)) + RETURN count(this5) = 1 AS var7 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((creatorCount <> 0 AND ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid)) OR (groupCount <> 0 AND size([(this3)<-[:ADMIN_OF]-(this9:Admin) WHERE single(this8 IN [(this9)-[:IS_USER]->(this8:User) WHERE ($jwt.uid IS NOT NULL AND this8.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR (groupCount <> 0 AND size([(this4)<-[:CONTRIBUTOR_TO]-(this11:Contributor) WHERE single(this10 IN [(this11)-[:IS_USER]->(this10:User) WHERE ($jwt.uid IS NOT NULL AND this10.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR var7 = true)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + CALL { + WITH this1 + MATCH (this1)-[this12:PARTNER_OF]-(this13:Person) + OPTIONAL MATCH (this13)<-[:CREATOR_OF]-(this14:User) + WITH *, count(this14) AS creatorCount + OPTIONAL MATCH (this13)-[:MEMBER_OF]->(this15:Group) + WITH *, count(this15) AS groupCount + OPTIONAL MATCH (this13)-[:MEMBER_OF]->(this16:Group) + WITH *, count(this16) AS groupCount + OPTIONAL MATCH (this13)-[:MEMBER_OF]->(this17:Group) + WITH *, count(this17) AS groupCount + WITH * + CALL { + WITH this13 + MATCH (this13)-[:MEMBER_OF]->(this18:Group) + OPTIONAL MATCH (this18)<-[:CREATOR_OF]-(this19:User) + WITH *, count(this19) AS creatorCount + WITH * + WHERE (creatorCount <> 0 AND ($jwt.uid IS NOT NULL AND this19.id = $jwt.uid)) + RETURN count(this18) = 1 AS var20 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((creatorCount <> 0 AND ($jwt.uid IS NOT NULL AND this14.id = $jwt.uid)) OR (groupCount <> 0 AND size([(this15)<-[:ADMIN_OF]-(this22:Admin) WHERE single(this21 IN [(this22)-[:IS_USER]->(this21:User) WHERE ($jwt.uid IS NOT NULL AND this21.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR (groupCount <> 0 AND size([(this16)<-[:CONTRIBUTOR_TO]-(this24:Contributor) WHERE single(this23 IN [(this24)-[:IS_USER]->(this23:User) WHERE ($jwt.uid IS NOT NULL AND this23.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR var20 = true)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH { active: this12.active, firstDay: this12.firstDay, lastDay: this12.lastDay, node: { __resolveType: \\"Person\\", __id: id(this13) } } AS edge + WITH collect(edge) AS edges + WITH edges, size(edges) AS totalCount + RETURN { edges: edges, totalCount: totalCount } AS var25 + } + WITH this1 { .id, .name, partnersConnection: var25 } AS this1 + RETURN collect(this1) AS var26 + } + RETURN this { .id, .name, members: var26 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"family_id_1\\", + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"id\\": \\"something\\", + \\"email\\": \\"something\\" + }, + \\"isAuthenticated\\": true + }" + `); + }); +}); From 6de8b3f72881d477f9aff6aa19721b6fd700bc87 Mon Sep 17 00:00:00 2001 From: MacondoExpress Date: Tue, 21 Nov 2023 14:59:09 +0000 Subject: [PATCH 04/72] migrate global node to use new translation-layer (#4318) Co-authored-by: Michael Webb <28074382+mjfwebb@users.noreply.github.com> --- packages/graphql/src/schema/resolvers/query/global-node.ts | 1 - packages/graphql/src/translate/translate-read.ts | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/graphql/src/schema/resolvers/query/global-node.ts b/packages/graphql/src/schema/resolvers/query/global-node.ts index 64ad8d33ed8..520eb3d454a 100644 --- a/packages/graphql/src/schema/resolvers/query/global-node.ts +++ b/packages/graphql/src/schema/resolvers/query/global-node.ts @@ -71,7 +71,6 @@ export function globalNodeResolver({ nodes, entities }: { nodes: Node[]; entitie const { cypher, params } = translateRead({ context: context as Neo4jGraphQLTranslationContext, node, - isGlobalNode: true, entityAdapter, }); const executeResult = await execute({ diff --git a/packages/graphql/src/translate/translate-read.ts b/packages/graphql/src/translate/translate-read.ts index 15b8952cb81..16877625ddf 100644 --- a/packages/graphql/src/translate/translate-read.ts +++ b/packages/graphql/src/translate/translate-read.ts @@ -87,18 +87,17 @@ export function translateRead( node, context, isRootConnectionField, - isGlobalNode, entityAdapter, }: { context: Neo4jGraphQLTranslationContext; node?: Node; isRootConnectionField?: boolean; - isGlobalNode?: boolean; entityAdapter: EntityAdapter; }, varName = "this" ): Cypher.CypherResult { - if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase && !isGlobalNode) { + + if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { return translateQuery({ context, entityAdapter }); } if (isRootConnectionField) { From 7b847dfc298cd37cc3f393b7e5c59ab3fcc5dfec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:16:52 +0000 Subject: [PATCH 05/72] chore(config): migrate config renovate.json --- renovate.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/renovate.json b/renovate.json index 996e471d767..f9144f7b378 100644 --- a/renovate.json +++ b/renovate.json @@ -7,7 +7,11 @@ "automerge": false }, "timezone": "Europe/London", - "schedule": ["after 10pm every weekday", "before 5am every weekday", "every weekend"], + "schedule": [ + "after 10pm every weekday", + "before 5am every weekday", + "every weekend" + ], "ignorePaths": [ "**/node_modules/**", "**/bower_components/**", @@ -34,7 +38,9 @@ }, { "matchPackagePatterns": ["graphql"], - "matchFiles": ["packages/package-tests/graphql-15/package.json"], + "matchFileNames": [ + "packages/package-tests/graphql-15/package.json" + ], "allowedVersions": "15" } ] From f0544044c86a272e173c671de3f35236c2c344da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:16:33 +0000 Subject: [PATCH 06/72] chore(deps): pin dependencies --- .github/workflows/changesets.yml | 10 +++++----- .github/workflows/cla-check.yml | 4 ++-- .github/workflows/dev.yml | 2 +- .github/workflows/lint-github-actions.yml | 2 +- .github/workflows/lint-markdown.yml | 4 ++-- .../workflows/performance-tests-comment.yml | 2 +- .github/workflows/performance-tests.yml | 8 ++++---- .../post-federation-test-results.yml | 2 +- .github/workflows/pull-requests.yml | 10 +++++----- .../workflows/reusable-api-library-tests.yml | 20 +++++++++---------- .github/workflows/reusable-aura-tests.yml | 8 ++++---- .../workflows/reusable-codeql-analysis.yml | 8 ++++---- .../workflows/reusable-federation-tests.yml | 6 +++--- ...able-integration-tests-on-prem-nightly.yml | 12 +++++------ .../reusable-integration-tests-on-prem.yml | 8 ++++---- .github/workflows/reusable-package-tests.yml | 4 ++-- ...ble-subscriptions-plugin-amqp-e2e-test.yml | 8 ++++---- .github/workflows/reusable-toolbox-tests.yml | 6 +++--- .github/workflows/reusable-unit-tests.yml | 8 ++++---- .github/workflows/sonarcloud.yml | 2 +- .github/workflows/toolbox-build.yml | 6 +++--- .github/workflows/toolbox-deploy.yml | 4 ++-- .github/workflows/toolbox-publish.yml | 4 ++-- .github/workflows/toolbox-teardown.yml | 2 +- Dockerfile | 2 +- examples/neo-place/docker-compose.yml | 2 +- .../apollo_rabbitmq/docker-compose.yml | 2 +- .../Dockerfile | 2 +- .../docker-compose.yml | 2 +- .../qpid-docker/Dockerfile | 2 +- 30 files changed, 81 insertions(+), 81 deletions(-) diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index b5b68eb1f62..5072dad78d0 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -24,13 +24,13 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: fetch-depth: 0 persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* @@ -78,7 +78,7 @@ jobs: steps: - name: Send Slack announcement of release if: matrix.package.name == '@neo4j/graphql' - uses: slackapi/slack-github-action@v1.24.0 + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 with: payload: '{"version":"${{ matrix.package.version }}"}' env: @@ -94,7 +94,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: ref: master fetch-depth: 0 @@ -122,7 +122,7 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: fetch-depth: 0 diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml index 3c297233e86..254377baf70 100644 --- a/.github/workflows/cla-check.yml +++ b/.github/workflows/cla-check.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: repository: neo-technology/whitelist-check token: ${{ secrets.NEO4J_TEAM_GRAPHQL_PERSONAL_ACCESS_TOKEN }} - - uses: actions/setup-python@v4 + - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4 with: python-version: 3 - name: Install dependencies diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 9afa050f99c..b69c2ae2dfd 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -70,7 +70,7 @@ jobs: id: check - name: Send Slack announcement of pipeline failure if: steps.check.outputs.status == 'failure' && github.event_name == 'schedule' - uses: slackapi/slack-github-action@v1.24.0 + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 with: payload: '{"url":"https://github.com/neo4j/graphql/actions/runs/${{ github.run_id }}"}' env: diff --git a/.github/workflows/lint-github-actions.yml b/.github/workflows/lint-github-actions.yml index ce65c7cbe20..a2ccf0684ab 100644 --- a/.github/workflows/lint-github-actions.yml +++ b/.github/workflows/lint-github-actions.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - uses: reviewdog/action-actionlint@82693e9e3b239f213108d6e412506f8b54003586 # v1.39.1 with: reporter: github-check diff --git a/.github/workflows/lint-markdown.yml b/.github/workflows/lint-markdown.yml index f0824f31a6f..432fe1ccc78 100644 --- a/.github/workflows/lint-markdown.yml +++ b/.github/workflows/lint-markdown.yml @@ -13,8 +13,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Install markdownlint diff --git a/.github/workflows/performance-tests-comment.yml b/.github/workflows/performance-tests-comment.yml index bca04cf6000..6e9278a9b65 100644 --- a/.github/workflows/performance-tests-comment.yml +++ b/.github/workflows/performance-tests-comment.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Download performance report" - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 3a1d2f73c16..d404e7a2899 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -24,12 +24,12 @@ jobs: steps: - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Target Branch - Check out - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: ref: ${{ github.base_ref }} fetch-depth: 0 @@ -71,7 +71,7 @@ jobs: NEO_URL: bolt://localhost:7687 - name: PR Branch - Check out - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: ref: ${{ github.ref }} clean: false @@ -119,7 +119,7 @@ jobs: working-directory: packages/graphql - name: Archive performance test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: performance path: packages/graphql/performance/ diff --git a/.github/workflows/post-federation-test-results.yml b/.github/workflows/post-federation-test-results.yml index 46d56f2fc22..ea16f2b3932 100644 --- a/.github/workflows/post-federation-test-results.yml +++ b/.github/workflows/post-federation-test-results.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Download Apollo Federation Subgraph Compatibility results" - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index bc1b08e7117..f896438ec48 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -17,8 +17,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: 18.13.0 cache: yarn @@ -44,7 +44,7 @@ jobs: typescript_files: ${{ steps.filter.outputs.typescript_files }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # tag=v2.11.1 id: filter with: @@ -60,8 +60,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 with: go-version: "^1.17.0" - name: Install addlicense diff --git a/.github/workflows/reusable-api-library-tests.yml b/.github/workflows/reusable-api-library-tests.yml index aa9c9cb5c82..9098da99311 100644 --- a/.github/workflows/reusable-api-library-tests.yml +++ b/.github/workflows/reusable-api-library-tests.yml @@ -47,8 +47,8 @@ jobs: - 7687:7687 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -63,7 +63,7 @@ jobs: NEO_USER: neo4j - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/graphql/coverage/ @@ -84,8 +84,8 @@ jobs: - 7687:7687 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -95,7 +95,7 @@ jobs: run: yarn --cwd packages/graphql run test e2e --coverage - if: ${{ env.CODECOV_TOKEN != '' }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/graphql/coverage/ @@ -106,8 +106,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -117,7 +117,7 @@ jobs: run: yarn --cwd packages/graphql run test:schema --coverage - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/graphql/coverage/ @@ -125,7 +125,7 @@ jobs: fail_ci_if_error: true - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload }} name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: api-library-coverage-graphql path: packages/graphql/coverage/ diff --git a/.github/workflows/reusable-aura-tests.yml b/.github/workflows/reusable-aura-tests.yml index 2d6c696c8a8..9ca743d6ce8 100644 --- a/.github/workflows/reusable-aura-tests.yml +++ b/.github/workflows/reusable-aura-tests.yml @@ -26,10 +26,10 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: ref: ${{ inputs.BRANCH || github.ref }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -63,8 +63,8 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn diff --git a/.github/workflows/reusable-codeql-analysis.yml b/.github/workflows/reusable-codeql-analysis.yml index 6337769ee68..51d71640199 100644 --- a/.github/workflows/reusable-codeql-analysis.yml +++ b/.github/workflows/reusable-codeql-analysis.yml @@ -9,14 +9,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2 with: config-file: ./.github/codeql/codeql-config.yml languages: javascript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2 diff --git a/.github/workflows/reusable-federation-tests.yml b/.github/workflows/reusable-federation-tests.yml index 9f13911e24b..dc59fe2c450 100644 --- a/.github/workflows/reusable-federation-tests.yml +++ b/.github/workflows/reusable-federation-tests.yml @@ -9,8 +9,8 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -37,7 +37,7 @@ jobs: mkdir prnumber echo "$PULL_REQUEST_NUMBER" > ./prnumber/prnumber - name: Archive PR number - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: prnumber path: prnumber/ diff --git a/.github/workflows/reusable-integration-tests-on-prem-nightly.yml b/.github/workflows/reusable-integration-tests-on-prem-nightly.yml index 291d394aac8..f2d06131ab1 100644 --- a/.github/workflows/reusable-integration-tests-on-prem-nightly.yml +++ b/.github/workflows/reusable-integration-tests-on-prem-nightly.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Login to ECR - uses: aws-actions/amazon-ecr-login@v2 + uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2 id: login-to-ecr env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -76,14 +76,14 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - name: Setting up Node.js with version ${{ matrix.node }} - uses: actions/setup-node@v4 + uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: ${{ matrix.node }} cache: yarn - name: Login to ECR - uses: docker/login-action@v3 + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 with: registry: 535893049302.dkr.ecr.eu-west-1.amazonaws.com username: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -116,7 +116,7 @@ jobs: NEO_URL: neo4j://localhost:7687 - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.packages.package == 'graphql' }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/${{ matrix.packages.package }}/coverage-nightly/ @@ -124,7 +124,7 @@ jobs: fail_ci_if_error: true - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.packages.package == 'graphql' }} name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: integration-nightly-coverage-${{ matrix.packages.package }} path: packages/${{ matrix.packages.package }}/coverage/ diff --git a/.github/workflows/reusable-integration-tests-on-prem.yml b/.github/workflows/reusable-integration-tests-on-prem.yml index 974cade1447..124f7a1815d 100644 --- a/.github/workflows/reusable-integration-tests-on-prem.yml +++ b/.github/workflows/reusable-integration-tests-on-prem.yml @@ -52,8 +52,8 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -72,7 +72,7 @@ jobs: NEO_URL: bolt://localhost:7687 - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.packages.package == 'graphql' }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/${{ matrix.packages.package }}/coverage-${{ matrix.neo4j-version }}/ @@ -80,7 +80,7 @@ jobs: fail_ci_if_error: true - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.packages.package == 'graphql' }} name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: integration-coverage-${{ matrix.packages.package }} path: packages/${{ matrix.packages.package }}/coverage/ diff --git a/.github/workflows/reusable-package-tests.yml b/.github/workflows/reusable-package-tests.yml index 54552607475..98fe65896f2 100644 --- a/.github/workflows/reusable-package-tests.yml +++ b/.github/workflows/reusable-package-tests.yml @@ -8,8 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: 18.13.0 cache: yarn diff --git a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml index 5e2ee2a22ba..eead7ce859e 100644 --- a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml +++ b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml @@ -26,7 +26,7 @@ jobs: ports: - 7687:7687 rabbitmq: - image: rabbitmq + image: rabbitmq@sha256:ff6380f9596a3875bcaa03836c2c39b0ade0e34d2148bca8b5bf407d23f908d5 env: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest @@ -36,8 +36,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -56,7 +56,7 @@ jobs: RABBITMQ_USER: guest RABBITMQ_PASSWORD: guest - name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: e2e-coverage-graphql-amqp-subscriptions-engine path: packages/graphql-amqp-subscriptions-engine/coverage/ diff --git a/.github/workflows/reusable-toolbox-tests.yml b/.github/workflows/reusable-toolbox-tests.yml index a912151fa73..eef6527dfbf 100644 --- a/.github/workflows/reusable-toolbox-tests.yml +++ b/.github/workflows/reusable-toolbox-tests.yml @@ -19,8 +19,8 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -44,7 +44,7 @@ jobs: NEO_URL: bolt://localhost:7687 - name: Upload playwright report on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: playwright-test-failure-report path: packages/graphql-toolbox/tests/artifacts diff --git a/.github/workflows/reusable-unit-tests.yml b/.github/workflows/reusable-unit-tests.yml index e0b5a4aad3f..01ca66b6eee 100644 --- a/.github/workflows/reusable-unit-tests.yml +++ b/.github/workflows/reusable-unit-tests.yml @@ -29,8 +29,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* cache: yarn @@ -41,7 +41,7 @@ jobs: working-directory: packages/${{ matrix.package }} - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.package == 'graphql' }} name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/${{ matrix.package }}/coverage/ @@ -49,7 +49,7 @@ jobs: fail_ci_if_error: true - if: ${{ env.CODECOV_TOKEN != '' && !inputs.disable-code-cov-upload && matrix.package == 'graphql' }} name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: unit-coverage-${{ matrix.package }} path: packages/${{ matrix.package }}/coverage/ diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 21a9dc9d959..e19ec08ecee 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -24,7 +24,7 @@ jobs: name: sonarcloud steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Uppercase package name diff --git a/.github/workflows/toolbox-build.yml b/.github/workflows/toolbox-build.yml index 8eb347532ed..1ea45714b3c 100644 --- a/.github/workflows/toolbox-build.yml +++ b/.github/workflows/toolbox-build.yml @@ -13,8 +13,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Install dependencies @@ -31,7 +31,7 @@ jobs: echo "$PULL_REQUEST_NUMBER" > ./dist/prnumber working-directory: packages/graphql-toolbox - name: Archive Toolbox build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: graphqltoolbox path: packages/graphql-toolbox/dist diff --git a/.github/workflows/toolbox-deploy.yml b/.github/workflows/toolbox-deploy.yml index 394f1eb8537..b76547eb176 100644 --- a/.github/workflows/toolbox-deploy.yml +++ b/.github/workflows/toolbox-deploy.yml @@ -15,8 +15,8 @@ jobs: environment: aws steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Install dependencies diff --git a/.github/workflows/toolbox-publish.yml b/.github/workflows/toolbox-publish.yml index b8a8722982a..8da0670ccfd 100644 --- a/.github/workflows/toolbox-publish.yml +++ b/.github/workflows/toolbox-publish.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Download built Toolbox - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -39,7 +39,7 @@ jobs: number=$(> "$GITHUB_OUTPUT" - - uses: actions/setup-node@v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Publish the Toolbox to surge.sh diff --git a/.github/workflows/toolbox-teardown.yml b/.github/workflows/toolbox-teardown.yml index deda15a4e7c..6015fb2f185 100644 --- a/.github/workflows/toolbox-teardown.yml +++ b/.github/workflows/toolbox-teardown.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v4 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4 with: node-version: lts/* - name: Teardown graphql-toolbox diff --git a/Dockerfile b/Dockerfile index 796117ef63c..ebef9c1d9c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.9.0-buster-slim +FROM node:20.9.0-buster-slim@sha256:050917322f944bd3d42d5d654cb1f4865fe2680c9981e9197fcadea770bb6003 WORKDIR /app diff --git a/examples/neo-place/docker-compose.yml b/examples/neo-place/docker-compose.yml index a79f6023c03..ddd872b502c 100644 --- a/examples/neo-place/docker-compose.yml +++ b/examples/neo-place/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management + image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c ports: - "5672:5672" - "15672:15672" diff --git a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml index a79f6023c03..ddd872b502c 100644 --- a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml +++ b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management + image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c ports: - "5672:5672" - "15672:15672" diff --git a/packages/apollo-federation-subgraph-compatibility/Dockerfile b/packages/apollo-federation-subgraph-compatibility/Dockerfile index 8222ecc5532..4b0e9dd9984 100644 --- a/packages/apollo-federation-subgraph-compatibility/Dockerfile +++ b/packages/apollo-federation-subgraph-compatibility/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts +FROM node:lts@sha256:715ecafe1d62e5d452f7945c32f81733425aba550e40dbf2ba5b9596e73bdb92 WORKDIR /app diff --git a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml index a6ec7d8f56d..8edb8919a82 100644 --- a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml +++ b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.5' # This is just for local testing services: rabbitmq: - image: rabbitmq:3.12-management + image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c ports: - "5672:5672" - "15672:15672" diff --git a/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile b/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile index e9cb678d5c5..09ad14c1c80 100644 --- a/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile +++ b/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ibmjava:8-jre +FROM ibmjava:8-jre@sha256:6e861dd25acbd010a0072838fb9e2bc78fddb6df4917367b9ec128d409a94d05 WORKDIR /usr/local/qpid RUN apt-get update && apt-get install -y curl \ && curl https://dlcdn.apache.org/qpid/broker-j/8.0.6/binaries/apache-qpid-broker-j-8.0.6-bin.tar.gz \ From ee27464ad1569ed956f0f457991d06e52bba43d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:25:21 +0000 Subject: [PATCH 07/72] chore(deps): update dependency @types/react-dom to v18.2.16 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index dd07e47a206..b1ac2c3448a 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -77,7 +77,7 @@ "@types/lodash.debounce": "4.0.9", "@types/markdown-it": "13.0.7", "@types/prettier": "2.7.3", - "@types/react-dom": "18.2.15", + "@types/react-dom": "18.2.16", "@types/webpack": "5.28.5", "autoprefixer": "10.4.16", "compression-webpack-plugin": "10.0.0", diff --git a/yarn.lock b/yarn.lock index 66ec503d482..902ac8dbc2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3604,7 +3604,7 @@ __metadata: "@types/lodash.debounce": 4.0.9 "@types/markdown-it": 13.0.7 "@types/prettier": 2.7.3 - "@types/react-dom": 18.2.15 + "@types/react-dom": 18.2.16 "@types/webpack": 5.28.5 autoprefixer: 10.4.16 classnames: 2.3.2 @@ -7714,12 +7714,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:18.2.15": - version: 18.2.15 - resolution: "@types/react-dom@npm:18.2.15" +"@types/react-dom@npm:18.2.16": + version: 18.2.16 + resolution: "@types/react-dom@npm:18.2.16" dependencies: "@types/react": "*" - checksum: 8e9631600c21ff561328e38a951d1991b3b3b20f538af4c0efbd1327c883a5573a63f50e1b945c34fa51b114b30e1ca5e62317bd54f21e063d6697b4be843a03 + checksum: 0d79f6e121155003840414e3cb1f5d70eef3a7f03450f7837c806b319b9e737880e368def615e2bf49159ed4041899e77b1002902e1285dd656783d2a836eb73 languageName: node linkType: hard From d06a6650b754a0f796f1837e863fe5401473494a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:08:16 +0000 Subject: [PATCH 08/72] chore(deps): update dependency @types/semver to v7.5.6 --- packages/graphql/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 8f3d49d014f..975ff2a9646 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -59,7 +59,7 @@ "@types/node": "20.9.3", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", - "@types/semver": "7.5.5", + "@types/semver": "7.5.6", "@types/supertest": "2.0.16", "@types/ws": "8.5.9", "dedent": "1.5.1", diff --git a/yarn.lock b/yarn.lock index 902ac8dbc2f..028e78e6ece 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3673,7 +3673,7 @@ __metadata: "@types/node": 20.9.3 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 - "@types/semver": 7.5.5 + "@types/semver": 7.5.6 "@types/supertest": 2.0.16 "@types/ws": 8.5.9 camelcase: ^6.3.0 @@ -7773,10 +7773,10 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.5": - version: 7.5.5 - resolution: "@types/semver@npm:7.5.5" - checksum: 533e6c93d1262d65f449423d94a445f7f3db0672e7429f21b6a1636d6051dbab3a2989ddcda9b79c69bb37830931d09fc958a65305a891357f5cea3257c297f5 +"@types/semver@npm:7.5.6": + version: 7.5.6 + resolution: "@types/semver@npm:7.5.6" + checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 languageName: node linkType: hard From 3e39981b981047c690c9b99fa896d8842d06da27 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:18:13 +0000 Subject: [PATCH 09/72] chore(deps): update dependency @types/ws to v8.5.10 --- packages/graphql/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 975ff2a9646..5f2ca6ecd25 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -61,7 +61,7 @@ "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", "@types/supertest": "2.0.16", - "@types/ws": "8.5.9", + "@types/ws": "8.5.10", "dedent": "1.5.1", "graphql-middleware": "6.1.35", "graphql-tag": "2.12.6", diff --git a/yarn.lock b/yarn.lock index 028e78e6ece..0adf8b56534 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3675,7 +3675,7 @@ __metadata: "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 "@types/supertest": 2.0.16 - "@types/ws": 8.5.9 + "@types/ws": 8.5.10 camelcase: ^6.3.0 debug: ^4.3.4 dedent: 1.5.1 @@ -7900,12 +7900,12 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:8.5.9": - version: 8.5.9 - resolution: "@types/ws@npm:8.5.9" +"@types/ws@npm:8.5.10": + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" dependencies: "@types/node": "*" - checksum: 83f436b731d2cdc49a45ced31a0a65cdd2e39c24d7b882776c26efa190dad6553e266d624c7a7089f36ad3ed471e02e729f3219282c80689b435f665df4a2b0b + checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e languageName: node linkType: hard From 09589be051f8272937ff8be9a9fe6326e006e989 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 03:27:55 +0000 Subject: [PATCH 10/72] chore(deps): update node.js to 146bbe4 --- packages/apollo-federation-subgraph-compatibility/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-federation-subgraph-compatibility/Dockerfile b/packages/apollo-federation-subgraph-compatibility/Dockerfile index 4b0e9dd9984..98791e74806 100644 --- a/packages/apollo-federation-subgraph-compatibility/Dockerfile +++ b/packages/apollo-federation-subgraph-compatibility/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts@sha256:715ecafe1d62e5d452f7945c32f81733425aba550e40dbf2ba5b9596e73bdb92 +FROM node:lts@sha256:146bbe4eaee99ae885be2a0a767f63a4b96032141d70d80e590f10e0d7ebabcb WORKDIR /app From 416fee93f9235d97e813bf3605300a9de1f65b1a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 03:37:03 +0000 Subject: [PATCH 11/72] chore(deps): update dependency @types/codemirror to v5.60.15 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index b1ac2c3448a..bcd6b539f0d 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -73,7 +73,7 @@ "devDependencies": { "@playwright/test": "1.40.0", "@tsconfig/create-react-app": "2.0.1", - "@types/codemirror": "5.60.14", + "@types/codemirror": "5.60.15", "@types/lodash.debounce": "4.0.9", "@types/markdown-it": "13.0.7", "@types/prettier": "2.7.3", diff --git a/yarn.lock b/yarn.lock index 0adf8b56534..04266db6b76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3600,7 +3600,7 @@ __metadata: "@neo4j/introspector": 2.0.0 "@playwright/test": 1.40.0 "@tsconfig/create-react-app": 2.0.1 - "@types/codemirror": 5.60.14 + "@types/codemirror": 5.60.15 "@types/lodash.debounce": 4.0.9 "@types/markdown-it": 13.0.7 "@types/prettier": 2.7.3 @@ -7256,12 +7256,12 @@ __metadata: languageName: node linkType: hard -"@types/codemirror@npm:5.60.14": - version: 5.60.14 - resolution: "@types/codemirror@npm:5.60.14" +"@types/codemirror@npm:5.60.15": + version: 5.60.15 + resolution: "@types/codemirror@npm:5.60.15" dependencies: "@types/tern": "*" - checksum: b9d8e5fb62e5441b4842c1fcd85dab2d671aff56f6f636dd1c09ac43316f9a9ded3f83782b1f3ecd16e34f1169b1c2743e6b28ea95733925115839512332e3f6 + checksum: cfad3f569de48fba3efa44fdfeba77933e231486a52cc80cff7ce6eeeed5b447a5bc2b11e2226bc00ccee332c661e53e35a15cf14eb835f434a6a402d9462f5f languageName: node linkType: hard From dbce9176c226a8c3597bbc01e333bfc1e6da21de Mon Sep 17 00:00:00 2001 From: Michael Webb <28074382+mjfwebb@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:29:20 +0100 Subject: [PATCH 12/72] test: standardise session and context in bigint test (#4329) --- packages/graphql/tests/integration/types/bigint.int.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/graphql/tests/integration/types/bigint.int.test.ts b/packages/graphql/tests/integration/types/bigint.int.test.ts index 184aaf9e6e2..b080cf83d6c 100644 --- a/packages/graphql/tests/integration/types/bigint.int.test.ts +++ b/packages/graphql/tests/integration/types/bigint.int.test.ts @@ -153,7 +153,7 @@ describe("BigInt", () => { }); test("should successfully query an node with a BigInt property using in where", async () => { - const session = driver.session(); + const session = await neo4j.getSession(); const File = new UniqueType("File"); const typeDefs = ` @@ -190,7 +190,7 @@ describe("BigInt", () => { const gqlResult = await graphql({ schema: await neoSchema.getSchema(), source: query, - contextValue: { executionContext: driver, sessionConfig: { bookmarks: session.lastBookmark() } }, + contextValue: neo4j.getContextValues(), }); expect(gqlResult.errors).toBeFalsy(); From 7ed39ba237d0f1685f0b8b38fd5af07f8928f98d Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 15 Nov 2023 16:54:43 +0000 Subject: [PATCH 13/72] WIP FulltextOperation on new translation layer --- .../annotation/FullTextAnnotation.ts | 4 +- .../src/translate/queryAST/ast/QueryAST.ts | 15 +- .../queryAST/ast/fields/FulltextScoreField.ts | 42 ++++++ .../ast/operations/FulltextOperation.ts | 132 ++++++++++++++++++ .../queryAST/ast/operations/ReadOperation.ts | 6 +- .../queryAST/factory/OperationFactory.ts | 130 ++++++++++++++++- .../queryAST/factory/QueryASTFactory.ts | 3 +- .../graphql/src/translate/translate-read.ts | 5 +- .../fulltext/fulltext-query.int.test.ts | 12 +- .../graphql/tests/tck/fulltext/auth.test.ts | 58 +++++--- .../graphql/tests/tck/fulltext/match.test.ts | 10 +- .../tests/tck/fulltext/node-labels.test.ts | 2 +- .../graphql/tests/tck/fulltext/score.test.ts | 103 ++++++++++++++ 13 files changed, 468 insertions(+), 54 deletions(-) create mode 100644 packages/graphql/src/translate/queryAST/ast/fields/FulltextScoreField.ts create mode 100644 packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts create mode 100644 packages/graphql/tests/tck/fulltext/score.test.ts diff --git a/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts b/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts index 3ce9f8b8c41..0738b621b35 100644 --- a/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts +++ b/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts @@ -18,9 +18,9 @@ */ export type FullTextField = { - name: string; + name?: string; fields: string[]; - queryName: string; + queryName?: string; indexName: string; }; diff --git a/packages/graphql/src/translate/queryAST/ast/QueryAST.ts b/packages/graphql/src/translate/queryAST/ast/QueryAST.ts index 5188995fa19..ab3e7216e44 100644 --- a/packages/graphql/src/translate/queryAST/ast/QueryAST.ts +++ b/packages/graphql/src/translate/queryAST/ast/QueryAST.ts @@ -34,15 +34,15 @@ export class QueryAST { this.operation = operation; } - public build(neo4jGraphQLContext: Neo4jGraphQLTranslationContext): Cypher.Clause { - const context = this.buildQueryASTContext(neo4jGraphQLContext); + public build(neo4jGraphQLContext: Neo4jGraphQLTranslationContext, varName?: string): Cypher.Clause { + const context = this.buildQueryASTContext(neo4jGraphQLContext, varName); return Cypher.concat(...this.transpile(context).clauses); } // TODO: refactor other top level operations to use this method instead of build - public buildNew(neo4jGraphQLContext: Neo4jGraphQLTranslationContext): Cypher.Clause { - const context = this.buildQueryASTContext(neo4jGraphQLContext); + public buildNew(neo4jGraphQLContext: Neo4jGraphQLTranslationContext, varName?: string): Cypher.Clause { + const context = this.buildQueryASTContext(neo4jGraphQLContext, varName); const { clauses, projectionExpr } = this.transpile(context); const returnClause = new Cypher.Return(projectionExpr); @@ -57,9 +57,12 @@ export class QueryAST { return this.operation.transpile(context); } - public buildQueryASTContext(neo4jGraphQLContext: Neo4jGraphQLTranslationContext): QueryASTContext { + public buildQueryASTContext( + neo4jGraphQLContext: Neo4jGraphQLTranslationContext, + varName = "this" + ): QueryASTContext { const queryASTEnv = new QueryASTEnv(); - const returnVariable = new Cypher.NamedVariable("this"); + const returnVariable = new Cypher.NamedVariable(varName); const node = this.getTargetFromOperation(neo4jGraphQLContext); return new QueryASTContext({ target: node, diff --git a/packages/graphql/src/translate/queryAST/ast/fields/FulltextScoreField.ts b/packages/graphql/src/translate/queryAST/ast/fields/FulltextScoreField.ts new file mode 100644 index 00000000000..343070fc8b7 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/fields/FulltextScoreField.ts @@ -0,0 +1,42 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type Cypher from "@neo4j/cypher-builder"; +import type { Variable } from "@neo4j/cypher-builder"; +import type { QueryASTNode } from "../QueryASTNode"; +import { Field } from "./Field"; + +export class FulltextScoreField extends Field { + private score: Cypher.Variable; + + constructor({ alias, score }: { alias: string; score: Cypher.Variable }) { + super(alias); + this.score = score; + } + + public getProjectionField(_variable: Variable): Record<"score", Cypher.Variable> { + return { + score: this.score, + }; + } + + public getChildren(): QueryASTNode[] { + return []; + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts new file mode 100644 index 00000000000..f4d66302a62 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts @@ -0,0 +1,132 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; +import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; +import { mapLabelsWithContext } from "../../../../schema-model/utils/map-labels-with-context"; +import { filterTruthy } from "../../../../utils/utils"; +import type { QueryASTContext } from "../QueryASTContext"; +import type { QueryASTNode } from "../QueryASTNode"; +import type { FulltextScoreField } from "../fields/FulltextScoreField"; +import { ReadOperation } from "./ReadOperation"; + +export type FulltextOptions = { + index: string; + phrase: string; + score?: Cypher.Variable; +}; + +export class FulltextOperation extends ReadOperation { + private fulltext: FulltextOptions; + + private scoreField: FulltextScoreField | undefined; + + constructor({ + target, + relationship, + directed, + fulltext, + scoreField, + }: { + target: ConcreteEntityAdapter; + relationship?: RelationshipAdapter; + directed?: boolean; + fulltext: FulltextOptions; + scoreField: FulltextScoreField | undefined; + }) { + super({ + target, + directed, + relationship, + }); + + this.fulltext = fulltext; + this.scoreField = scoreField; + } + + public getChildren(): QueryASTNode[] { + return filterTruthy([...super.getChildren(), this.scoreField]); + } + + protected getSelectionClauses( + context: QueryASTContext, + node: Cypher.Node | Cypher.Pattern + ): { + preSelection: Array; + selectionClause: Cypher.Yield | Cypher.With; + } { + if (!this.nodeAlias) { + throw new Error("Node alias missing on top level fulltext"); + } + + if (node instanceof Cypher.Pattern) { + throw new Error("Nested not supported in aggregations"); + } + + const phraseParam = new Cypher.Param(this.fulltext.phrase); + const indexName = new Cypher.Literal(this.fulltext.index); + + let fulltextClause: Cypher.Yield | Cypher.With = Cypher.db.index.fulltext + .queryNodes(indexName, phraseParam) + .yield(["node", node]); + + if (this.scoreField) { + const scoreProjection = this.scoreField.getProjectionField(node); + + fulltextClause = Cypher.db.index.fulltext + .queryNodes(indexName, phraseParam) + .yield(["node", node], ["score", scoreProjection.score]); + } + + const expectedLabels = mapLabelsWithContext(this.target.getLabels(), context.neo4jGraphQLContext); + + const whereOperators = expectedLabels.map((label) => { + return Cypher.in(new Cypher.Param(label), Cypher.labels(node)); + }); + + fulltextClause.where(Cypher.and(...whereOperators)); + + let extraMatches: Array = this.getChildren().flatMap((f) => { + return f.getSelection(context); + }); + + if (extraMatches.length > 0) { + extraMatches = [fulltextClause, ...extraMatches]; + fulltextClause = new Cypher.With("*"); + } + + return { + preSelection: extraMatches, + selectionClause: fulltextClause, + }; + } + + protected getReturnStatement(context: QueryASTContext, returnVariable: Cypher.Variable): Cypher.Return { + const returnClause = super.getReturnStatement(context, returnVariable); + + if (this.scoreField) { + const scoreProjection = this.scoreField.getProjectionField(returnVariable); + + returnClause.addColumns([scoreProjection.score, "score"]); + } + + return returnClause; + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index de6e022db2c..c8d9a4f38a5 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -195,8 +195,8 @@ export class ReadOperation extends Operation { context: QueryASTContext, node: Cypher.Node | Cypher.Pattern ): { - preSelection: Array; - selectionClause: Cypher.Match | Cypher.With; + preSelection: Array; + selectionClause: Cypher.Match | Cypher.With | Cypher.Yield; } { let matchClause: Cypher.Match | Cypher.With = new Cypher.Match(node); @@ -293,7 +293,7 @@ export class ReadOperation extends Operation { }; } - private getReturnStatement(context: QueryASTContext, returnVariable): Cypher.Return { + protected getReturnStatement(context: QueryASTContext, returnVariable: Cypher.Variable): Cypher.Return { const projection = this.getProjectionMap(context); if (context.shouldCollect) { return new Cypher.Return([Cypher.collect(projection), returnVariable]); diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index fa0a298f703..5e996a5a8e1 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -18,6 +18,7 @@ */ import { mergeDeep } from "@graphql-tools/utils"; +import type * as Cypher from "@neo4j/cypher-builder"; import type { ResolveTree } from "graphql-parse-resolve-info"; import { cursorToOffset } from "graphql-relay"; import { Integer } from "neo4j-driver"; @@ -31,10 +32,13 @@ import type { AuthorizationOperation } from "../../../types/authorization"; import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { filterTruthy, isObject, isString } from "../../../utils/utils"; import { checkEntityAuthentication } from "../../authorization/check-authentication"; +import { FulltextScoreField } from "../ast/fields/FulltextScoreField"; import type { AuthorizationFilters } from "../ast/filters/authorization-filters/AuthorizationFilters"; import { AggregationOperation } from "../ast/operations/AggregationOperation"; import { ConnectionReadOperation } from "../ast/operations/ConnectionReadOperation"; import { CreateOperation } from "../ast/operations/CreateOperation"; +import type { FulltextOptions } from "../ast/operations/FulltextOperation"; +import { FulltextOperation } from "../ast/operations/FulltextOperation"; import { ReadOperation } from "../ast/operations/ReadOperation"; import { CompositeAggregationOperation } from "../ast/operations/composite/CompositeAggregationOperation"; import { CompositeAggregationPartial } from "../ast/operations/composite/CompositeAggregationPartial"; @@ -78,11 +82,33 @@ export class OperationsFactory { context: Neo4jGraphQLTranslationContext ): Operation { if (isConcreteEntity(entity)) { + // Handles deprecated top level fulltext + if (context.resolveTree.args.phrase) { + if (!context.fulltext) { + throw new Error("Failed to get context fulltext"); + } + const indexName = context.fulltext.indexName || context.fulltext.name; + if (indexName === undefined) { + throw new Error("The name of the fulltext index should be defined using the indexName argument."); + } + + const op = this.createFulltextOperation(entity, resolveTree, context); + op.nodeAlias = TOP_LEVEL_NODE_NAME; + return op; + } + const operationMatch = parseOperationField(resolveTree.name, entity); + if (operationMatch.isCreate) { return this.createCreateOperation(entity, resolveTree, context); // TODO: move this to separate method? } else if (operationMatch.isRead) { - const op = this.createReadOperation(entity, resolveTree, context) as ReadOperation; + let op: ReadOperation; + if (context.resolveTree.args.fulltext || context.resolveTree.args.phrase) { + op = this.createFulltextOperation(entity, resolveTree, context); + } else { + op = this.createReadOperation(entity, resolveTree, context) as ReadOperation; + } + op.nodeAlias = TOP_LEVEL_NODE_NAME; return op; } else if (operationMatch.isConnection) { @@ -95,7 +121,7 @@ export class OperationsFactory { op.nodeAlias = TOP_LEVEL_NODE_NAME; return op; } else if (operationMatch.isAggregation) { - const op = this.createAggregationOperation(entity, resolveTree, context, true); + const op = this.createAggregationOperation(entity, resolveTree, context); op.nodeAlias = TOP_LEVEL_NODE_NAME; return op; } @@ -152,6 +178,102 @@ export class OperationsFactory { return createOP; } + private getFulltextOptions(context: Neo4jGraphQLTranslationContext): FulltextOptions { + if (context.fulltext) { + const indexName = context.fulltext.indexName || context.fulltext.name; + if (indexName === undefined) { + throw new Error("The name of the fulltext index should be defined using the indexName argument."); + } + const phrase = context.resolveTree.args.phrase; + if (!phrase || typeof phrase !== "string") { + throw new Error("Invalid phrase"); + } + + return { + index: indexName, + phrase, + score: context.fulltext.scoreVariable, + }; + } + + const entries = Object.entries(context.resolveTree.args.fulltext || {}); + if (entries.length > 1) { + throw new Error("Can only call one search at any given time"); + } + const [indexName, indexInput] = entries[0] as [string, { phrase: string }]; + return { + index: indexName, + phrase: indexInput.phrase, + }; + } + + private createFulltextScoreField(field: ResolveTree, scoreVar: Cypher.Variable): FulltextScoreField { + return new FulltextScoreField({ + alias: field.alias, + score: scoreVar, + }); + } + + public createFulltextOperation( + entityOrRel: EntityAdapter | RelationshipAdapter, + resolveTree: ResolveTree, + context: Neo4jGraphQLTranslationContext + ): FulltextOperation { + const entity = entityOrRel instanceof RelationshipAdapter ? entityOrRel.target : entityOrRel; + const relationship = entityOrRel instanceof RelationshipAdapter ? entityOrRel : undefined; + + let resolveTreeWhere: Record = isObject(resolveTree.args.where) ? resolveTree.args.where : {}; + if (isConcreteEntity(entity)) { + const fulltextOptions = this.getFulltextOptions(context); + let scoreField: FulltextScoreField | undefined; + + // Compatibility of top level operations + const fulltextOperationDeprecatedFields = + resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.result]; + + if (fulltextOperationDeprecatedFields) { + resolveTreeWhere = resolveTreeWhere[entity.singular] || {}; + + const scoreRawField = fulltextOperationDeprecatedFields.score; + resolveTree = fulltextOperationDeprecatedFields[entity.singular]!; + + if (scoreRawField) { + if (!fulltextOptions.score) { + throw new Error("Missing fulltext score variable"); + } + scoreField = this.createFulltextScoreField(scoreRawField, fulltextOptions.score); + } + } + + checkEntityAuthentication({ + entity: entity.entity, + targetOperations: ["READ"], + context, + }); + const operation = new FulltextOperation({ + target: entity, + relationship, + directed: Boolean(resolveTree.args?.directed ?? true), + fulltext: fulltextOptions, + scoreField, + }); + + // const resolveTreeWhere: Record = isObject(resolveTree.args.where) + // ? resolveTree.args.where + // : {}; + + return this.hydrateReadOperation({ + operation, + entity, + resolveTree, + context, + whereArgs: resolveTreeWhere, + }); + } else { + throw new Error("Fulltext nor supported on interfaces"); + } + } + public createReadOperation( entityOrRel: EntityAdapter | RelationshipAdapter, resolveTree: ResolveTree, @@ -214,8 +336,7 @@ export class OperationsFactory { public createAggregationOperation( entityOrRel: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter, resolveTree: ResolveTree, - context: Neo4jGraphQLTranslationContext, - topLevel = false + context: Neo4jGraphQLTranslationContext ): AggregationOperation | CompositeAggregationOperation { let entity: ConcreteEntityAdapter | InterfaceEntityAdapter; if (entityOrRel instanceof RelationshipAdapter) { @@ -630,7 +751,6 @@ export class OperationsFactory { whereArgs: Record; }): T { let projectionFields = { ...resolveTree.fieldsByTypeName[entity.name] }; - // Get the abstract types of the interface const entityInterfaces = entity.compositeEntities; diff --git a/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts b/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts index 61e08fcbbab..456ea373239 100644 --- a/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts @@ -22,11 +22,11 @@ import type { Neo4jGraphQLSchemaModel } from "../../../schema-model/Neo4jGraphQL import type { EntityAdapter } from "../../../schema-model/entity/EntityAdapter"; import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { QueryAST } from "../ast/QueryAST"; -import { OperationsFactory } from "./OperationFactory"; import { AuthFilterFactory } from "./AuthFilterFactory"; import { AuthorizationFactory } from "./AuthorizationFactory"; import { FieldFactory } from "./FieldFactory"; import { FilterFactory } from "./FilterFactory"; +import { OperationsFactory } from "./OperationFactory"; import { SortAndPaginationFactory } from "./SortAndPaginationFactory"; export class QueryASTFactory { @@ -54,7 +54,6 @@ export class QueryASTFactory { context: Neo4jGraphQLTranslationContext ): QueryAST { const operation = this.operationsFactory.createTopLevelOperation(entityAdapter, resolveTree, context); - return new QueryAST(operation); } } diff --git a/packages/graphql/src/translate/translate-read.ts b/packages/graphql/src/translate/translate-read.ts index 16877625ddf..a3b5963b54b 100644 --- a/packages/graphql/src/translate/translate-read.ts +++ b/packages/graphql/src/translate/translate-read.ts @@ -42,9 +42,11 @@ const debug = Debug(DEBUG_TRANSLATE); function translateQuery({ context, entityAdapter, + resultVarName, }: { context: Neo4jGraphQLTranslationContext; entityAdapter: EntityAdapter; + resultVarName: string; }): Cypher.CypherResult { const { resolveTree } = context; // TODO: Rename QueryAST to OperationsTree @@ -53,7 +55,7 @@ function translateQuery({ if (!entityAdapter) throw new Error("Entity not found"); const queryAST = queryASTFactory.createQueryAST(resolveTree, entityAdapter, context); debug(queryAST.print()); - const clause = queryAST.build(context); + const clause = queryAST.build(context, resultVarName); return clause.build(); } @@ -96,7 +98,6 @@ export function translateRead( }, varName = "this" ): Cypher.CypherResult { - if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { return translateQuery({ context, entityAdapter }); } diff --git a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts index ea696b825cd..bef3b271eb8 100644 --- a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts +++ b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts @@ -17,19 +17,19 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; -import type { Driver, Session } from "neo4j-driver"; import type { GraphQLSchema } from "graphql"; import { graphql } from "graphql"; +import { gql } from "graphql-tag"; +import type { Driver, Session } from "neo4j-driver"; import { generate } from "randomstring"; -import Neo4j from "../../neo4j"; import { Neo4jGraphQL } from "../../../../src/classes"; -import { UniqueType } from "../../../utils/graphql-types"; +import { SCORE_FIELD } from "../../../../src/graphql/directives/fulltext"; import { upperFirst } from "../../../../src/utils/upper-first"; import { delay } from "../../../../src/utils/utils"; -import { isMultiDbUnsupportedError } from "../../../utils/is-multi-db-unsupported-error"; -import { SCORE_FIELD } from "../../../../src/graphql/directives/fulltext"; import { createBearerToken } from "../../../utils/create-bearer-token"; +import { UniqueType } from "../../../utils/graphql-types"; +import { isMultiDbUnsupportedError } from "../../../utils/is-multi-db-unsupported-error"; +import Neo4j from "../../neo4j"; function generatedTypeDefs(personType: UniqueType, movieType: UniqueType): string { return ` diff --git a/packages/graphql/tests/tck/fulltext/auth.test.ts b/packages/graphql/tests/tck/fulltext/auth.test.ts index 80f2def26e4..431d9854960 100644 --- a/packages/graphql/tests/tck/fulltext/auth.test.ts +++ b/packages/graphql/tests/tck/fulltext/auth.test.ts @@ -19,8 +19,8 @@ import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery } from "../utils/tck-test-utils"; import { createBearerToken } from "../../utils/create-bearer-token"; +import { formatCypher, translateQuery } from "../utils/tck-test-utils"; describe("Cypher -> fulltext -> Auth", () => { let verifyTCK; @@ -76,8 +76,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0)) + WHERE ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0) RETURN this { .title } AS this" `); @@ -131,8 +132,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -188,8 +190,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -247,8 +250,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -309,8 +313,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -372,8 +377,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE ($param3 IS NOT NULL AND this0.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE ($param3 IS NOT NULL AND this0.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -432,8 +438,9 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE NOT ($param3 IS NOT NULL AND this0.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE NOT ($param3 IS NOT NULL AND this0.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -486,11 +493,12 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND ($isAuthenticated = true AND EXISTS { + WHERE ($isAuthenticated = true AND EXISTS { MATCH (this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) - })) + }) RETURN this { .title } AS this" `); @@ -544,11 +552,12 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -604,14 +613,15 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) } AND NOT (EXISTS { MATCH (this)<-[:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -669,11 +679,12 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -734,14 +745,15 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) } AND NOT (EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -803,11 +815,12 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE ($param3 IS NOT NULL AND this0.year = $param3) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -866,14 +879,15 @@ describe("Cypher -> fulltext -> Auth", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + WHERE $param1 IN labels(this) WITH * - WHERE ($param1 IN labels(this) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE ($param3 IS NOT NULL AND this0.year = $param3) } AND NOT (EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) WHERE NOT ($param3 IS NOT NULL AND this0.year = $param3) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); diff --git a/packages/graphql/tests/tck/fulltext/match.test.ts b/packages/graphql/tests/tck/fulltext/match.test.ts index e81ea669503..ab874389085 100644 --- a/packages/graphql/tests/tck/fulltext/match.test.ts +++ b/packages/graphql/tests/tck/fulltext/match.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery, formatParams } from "../utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("Cypher -> fulltext -> Match", () => { let typeDefs: DocumentNode; @@ -79,15 +79,15 @@ describe("Cypher -> fulltext -> Match", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this - WHERE (this.title = $param1 AND $param2 IN labels(this)) + WHERE ($param1 IN labels(this) AND this.title = $param2) RETURN this { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": \\"something AND something\\", - \\"param1\\": \\"some-title\\", - \\"param2\\": \\"Movie\\" + \\"param1\\": \\"Movie\\", + \\"param2\\": \\"some-title\\" }" `); }); diff --git a/packages/graphql/tests/tck/fulltext/node-labels.test.ts b/packages/graphql/tests/tck/fulltext/node-labels.test.ts index 69bacf2938f..0373a947106 100644 --- a/packages/graphql/tests/tck/fulltext/node-labels.test.ts +++ b/packages/graphql/tests/tck/fulltext/node-labels.test.ts @@ -19,8 +19,8 @@ import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery, formatParams } from "../utils/tck-test-utils"; import { createBearerToken } from "../../utils/create-bearer-token"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("Cypher -> fulltext -> Additional Labels", () => { test("simple match with single fulltext property and static additionalLabels", async () => { diff --git a/packages/graphql/tests/tck/fulltext/score.test.ts b/packages/graphql/tests/tck/fulltext/score.test.ts new file mode 100644 index 00000000000..81f7b2ce178 --- /dev/null +++ b/packages/graphql/tests/tck/fulltext/score.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; +import { Neo4jGraphQL } from "../../../src"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; + +describe("Cypher -> fulltext -> Score", () => { + let typeDefs: DocumentNode; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = gql` + type Movie @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) { + title: String + released: Int + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + }); + + test("simple match with single property and score", async () => { + const query = gql` + query { + moviesFulltextMovieTitle(phrase: "a different name") { + score + movie { + title + released + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE $param1 IN labels(this) + RETURN this { .title, .released } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\" + }" + `); + }); + + test("simple match with single property and score and filter", async () => { + const query = gql` + query { + moviesFulltextMovieTitle(phrase: "a different name", where: { movie: { released_GT: 2000 } }) { + score + movie { + title + released + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE ($param1 IN labels(this) AND this.released > $param2) + RETURN this { .title, .released } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\", + \\"param2\\": { + \\"low\\": 2000, + \\"high\\": 0 + } + }" + `); + }); +}); From 1cc03ef89a009d539b8afb72ffa2a7a3187e20c0 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Tue, 21 Nov 2023 16:49:52 +0000 Subject: [PATCH 14/72] Fix sorting and filtering by score --- .../property-filters/FulltextScoreFilter.ts | 104 ++++++++++++++ .../ast/operations/FulltextOperation.ts | 16 +-- .../queryAST/ast/operations/ReadOperation.ts | 5 + .../queryAST/ast/sort/FulltextScoreSort.ts | 47 +++++++ .../queryAST/factory/OperationFactory.ts | 132 ++++++++++++++---- .../factory/SortAndPaginationFactory.ts | 21 ++- .../fulltext/fulltext-query.int.test.ts | 3 +- .../graphql/tests/tck/fulltext/auth.test.ts | 82 +++++------ .../graphql/tests/tck/fulltext/match.test.ts | 4 +- .../tests/tck/fulltext/node-labels.test.ts | 4 +- .../graphql/tests/tck/fulltext/score.test.ts | 122 ++++++++++++++++ 11 files changed, 456 insertions(+), 84 deletions(-) create mode 100644 packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts create mode 100644 packages/graphql/src/translate/queryAST/ast/sort/FulltextScoreSort.ts diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts new file mode 100644 index 00000000000..54b21b94eee --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts @@ -0,0 +1,104 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { QueryASTContext } from "../../QueryASTContext"; +import type { QueryASTNode } from "../../QueryASTNode"; +import { Filter } from "../Filter"; + +/** A property which comparison has already been parsed into a Param */ +export class FulltextScoreFilter extends Filter { + private scoreVariable: Cypher.Variable; + private min?: number; + private max?: number; + + constructor({ scoreVariable, min, max }: { scoreVariable: Cypher.Variable; min?: number; max?: number }) { + super(); + this.scoreVariable = scoreVariable; + this.min = min; + this.max = max; + } + + public getChildren(): QueryASTNode[] { + return []; + } + + // constructor(options: { + // attribute: AttributeAdapter; + // comparisonValue: CypherVariable; + // operator: FilterOperator; + // isNot: boolean; + // attachedTo?: "node" | "relationship"; + // }) { + // super(options); + // this.comparisonValue = options.comparisonValue; + // } + + public getPredicate(_queryASTContext: QueryASTContext): Cypher.Predicate { + const predicates: Cypher.Predicate[] = []; + + if (this.max || this.max === 0) { + const maxPredicate = Cypher.lte(this.scoreVariable, new Cypher.Param(this.max)); + predicates.push(maxPredicate); + } + if (this.min || this.min === 0) { + const minPredicate = Cypher.gte(this.scoreVariable, new Cypher.Param(this.min)); + predicates.push(minPredicate); + } + + return Cypher.and(...predicates); + + // const predicate = super.getPredicate(queryASTContext); + + // // NOTE: Should this check be a different Filter? + // return Cypher.and(Cypher.isNotNull(this.comparisonValue), predicate); + } + + // protected getOperation(): Cypher.ComparisonOp { + // const comparisonParam = new Cypher.Param(this.comparisonValue); + // return createComparisonOperation({ + // operator: this.operator, + // property: this.score, + // param: comparisonParam, + // }); + // if (whereInput?.[SCORE_FIELD]) { + // if (whereInput[SCORE_FIELD].min || whereInput[SCORE_FIELD].min === 0) { + // const scoreMinOp = Cypher.gte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].min)); + // if (scoreMinOp) whereOperators.push(scoreMinOp); + // } + // if (whereInput[SCORE_FIELD].max || whereInput[SCORE_FIELD].max === 0) { + // const scoreMaxOp = Cypher.lte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].max)); + // if (scoreMaxOp) whereOperators.push(scoreMaxOp); + // } + // } +} + +/** Returns the default operation for a given filter */ +// protected createBaseOperation({ +// operator, +// property, +// param, +// }: { +// operator: FilterOperator; +// property: Cypher.Expr; +// param: Cypher.Expr; +// }): Cypher.ComparisonOp { + +// return createComparisonOperation({ operator, property: coalesceProperty, param }); +// } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts index f4d66302a62..526f1ce9820 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts @@ -30,13 +30,14 @@ import { ReadOperation } from "./ReadOperation"; export type FulltextOptions = { index: string; phrase: string; - score?: Cypher.Variable; + score: Cypher.Variable; }; export class FulltextOperation extends ReadOperation { private fulltext: FulltextOptions; private scoreField: FulltextScoreField | undefined; + private scoreVariable: Cypher.Variable; constructor({ target, @@ -44,12 +45,14 @@ export class FulltextOperation extends ReadOperation { directed, fulltext, scoreField, + scoreVariable, }: { target: ConcreteEntityAdapter; relationship?: RelationshipAdapter; directed?: boolean; fulltext: FulltextOptions; scoreField: FulltextScoreField | undefined; + scoreVariable: Cypher.Variable; }) { super({ target, @@ -59,6 +62,7 @@ export class FulltextOperation extends ReadOperation { this.fulltext = fulltext; this.scoreField = scoreField; + this.scoreVariable = scoreVariable; } public getChildren(): QueryASTNode[] { @@ -85,15 +89,7 @@ export class FulltextOperation extends ReadOperation { let fulltextClause: Cypher.Yield | Cypher.With = Cypher.db.index.fulltext .queryNodes(indexName, phraseParam) - .yield(["node", node]); - - if (this.scoreField) { - const scoreProjection = this.scoreField.getProjectionField(node); - - fulltextClause = Cypher.db.index.fulltext - .queryNodes(indexName, phraseParam) - .yield(["node", node], ["score", scoreProjection.score]); - } + .yield(["node", node], ["score", this.scoreVariable]); const expectedLabels = mapLabelsWithContext(this.target.getLabels(), context.neo4jGraphQLContext); diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index c8d9a4f38a5..53ada8c4e7f 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -78,10 +78,15 @@ export class ReadOperation extends Operation { this.pagination = pagination; } + // TODO: avoid this filter override as it makes harder to compose in the factory public setFilters(filters: Filter[]) { this.filters = filters; } + public addFilter(filter: Filter) { + this.filters.push(filter); + } + public addAuthFilters(...filter: AuthorizationFilters[]) { this.authFilters.push(...filter); } diff --git a/packages/graphql/src/translate/queryAST/ast/sort/FulltextScoreSort.ts b/packages/graphql/src/translate/queryAST/ast/sort/FulltextScoreSort.ts new file mode 100644 index 00000000000..35b144dbded --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/sort/FulltextScoreSort.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type Cypher from "@neo4j/cypher-builder"; +import type { QueryASTContext } from "../QueryASTContext"; +import type { QueryASTNode } from "../QueryASTNode"; +import type { SortField } from "./Sort"; +import { Sort } from "./Sort"; + +export class FulltextScoreSort extends Sort { + private direction: Cypher.Order; + private scoreVariable: Cypher.Variable; + + constructor({ scoreVariable, direction }: { scoreVariable: Cypher.Variable; direction: Cypher.Order }) { + super(); + this.scoreVariable = scoreVariable; + this.direction = direction; + } + + public getChildren(): QueryASTNode[] { + return []; + } + + public getSortFields(_context: QueryASTContext, _variable: Cypher.Variable | Cypher.Property): SortField[] { + return [[this.scoreVariable, this.direction]]; + } + + public getProjectionField(_context: QueryASTContext): string | Record { + return {}; + } +} diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 5e996a5a8e1..6c5e52ce48a 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -18,8 +18,8 @@ */ import { mergeDeep } from "@graphql-tools/utils"; -import type * as Cypher from "@neo4j/cypher-builder"; -import type { ResolveTree } from "graphql-parse-resolve-info"; +import * as Cypher from "@neo4j/cypher-builder"; +import type { FieldsByTypeName, ResolveTree } from "graphql-parse-resolve-info"; import { cursorToOffset } from "graphql-relay"; import { Integer } from "neo4j-driver"; import type { EntityAdapter } from "../../../schema-model/entity/EntityAdapter"; @@ -34,6 +34,7 @@ import { filterTruthy, isObject, isString } from "../../../utils/utils"; import { checkEntityAuthentication } from "../../authorization/check-authentication"; import { FulltextScoreField } from "../ast/fields/FulltextScoreField"; import type { AuthorizationFilters } from "../ast/filters/authorization-filters/AuthorizationFilters"; +import { FulltextScoreFilter } from "../ast/filters/property-filters/FulltextScoreFilter"; import { AggregationOperation } from "../ast/operations/AggregationOperation"; import { ConnectionReadOperation } from "../ast/operations/ConnectionReadOperation"; import { CreateOperation } from "../ast/operations/CreateOperation"; @@ -204,6 +205,7 @@ export class OperationsFactory { return { index: indexName, phrase: indexInput.phrase, + score: new Cypher.Variable(), }; } @@ -223,26 +225,43 @@ export class OperationsFactory { const relationship = entityOrRel instanceof RelationshipAdapter ? entityOrRel : undefined; let resolveTreeWhere: Record = isObject(resolveTree.args.where) ? resolveTree.args.where : {}; + let sortOptions: Record = (resolveTree.args.options as Record) || {}; + let fieldsByTypeName = resolveTree.fieldsByTypeName; + let resolverArgs = resolveTree.args; if (isConcreteEntity(entity)) { const fulltextOptions = this.getFulltextOptions(context); let scoreField: FulltextScoreField | undefined; + let scoreFilter: FulltextScoreFilter | undefined; // Compatibility of top level operations const fulltextOperationDeprecatedFields = resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.result]; if (fulltextOperationDeprecatedFields) { + const scoreWhere = resolveTreeWhere.score; resolveTreeWhere = resolveTreeWhere[entity.singular] || {}; const scoreRawField = fulltextOperationDeprecatedFields.score; - resolveTree = fulltextOperationDeprecatedFields[entity.singular]!; + const nestedResolveTree: Record = fulltextOperationDeprecatedFields[entity.singular] || {}; + resolverArgs = { ...(nestedResolveTree?.args || {}), ...resolveTree.args }; + + sortOptions = { + limit: sortOptions.limit, + offset: sortOptions.offset, + sort: filterTruthy((sortOptions.sort || []).map((field) => field[entity.singular] || field)), + }; + fieldsByTypeName = nestedResolveTree.fieldsByTypeName || {}; if (scoreRawField) { - if (!fulltextOptions.score) { - throw new Error("Missing fulltext score variable"); - } scoreField = this.createFulltextScoreField(scoreRawField, fulltextOptions.score); } + if (scoreWhere) { + scoreFilter = new FulltextScoreFilter({ + scoreVariable: fulltextOptions.score, + min: scoreWhere.min, + max: scoreWhere.max, + }); + } } checkEntityAuthentication({ @@ -250,25 +269,45 @@ export class OperationsFactory { targetOperations: ["READ"], context, }); + const operation = new FulltextOperation({ target: entity, relationship, - directed: Boolean(resolveTree.args?.directed ?? true), + directed: Boolean(resolverArgs.directed ?? true), fulltext: fulltextOptions, scoreField, + scoreVariable: fulltextOptions.score, }); - // const resolveTreeWhere: Record = isObject(resolveTree.args.where) - // ? resolveTree.args.where - // : {}; - - return this.hydrateReadOperation({ + this.hydrateOperation({ operation, entity, - resolveTree, + fieldsByTypeName: fieldsByTypeName, context, whereArgs: resolveTreeWhere, }); + + if (scoreFilter) { + operation.addFilter(scoreFilter); + } + // Override sort to support score + const sortOptions2 = this.getOptions(entity, sortOptions); + + if (sortOptions2) { + const sort = this.sortAndPaginationFactory.createSortFields( + sortOptions2, + entity, + fulltextOptions.score + ); + operation.addSort(...sort); + + const pagination = this.sortAndPaginationFactory.createPagination(sortOptions2); + if (pagination) { + operation.addPagination(pagination); + } + } + + return operation; } else { throw new Error("Fulltext nor supported on interfaces"); } @@ -737,31 +776,38 @@ export class OperationsFactory { }; } - private hydrateReadOperation({ + private hydrateOperation({ entity, operation, - resolveTree, - context, whereArgs, + context, + sortArgs, + fieldsByTypeName, }: { entity: ConcreteEntityAdapter; operation: T; - resolveTree: ResolveTree; context: Neo4jGraphQLTranslationContext; whereArgs: Record; + sortArgs?: Record; + fieldsByTypeName: FieldsByTypeName; }): T { - let projectionFields = { ...resolveTree.fieldsByTypeName[entity.name] }; + const concreteProjectionFields = { ...fieldsByTypeName[entity.name] }; // Get the abstract types of the interface const entityInterfaces = entity.compositeEntities; - const interfacesFields = filterTruthy(entityInterfaces.map((i) => resolveTree.fieldsByTypeName[i.name])); - - projectionFields = mergeDeep[]>([...interfacesFields, projectionFields]); + const interfacesFields = filterTruthy(entityInterfaces.map((i) => fieldsByTypeName[i.name])); + const projectionFields = mergeDeep[]>([ + ...interfacesFields, + concreteProjectionFields, + ]); const fields = this.fieldFactory.createFields(entity, projectionFields, context); + const filters = this.filterFactory.createNodeFilters(entity, whereArgs); + const authFilters = this.authorizationFactory.createEntityAuthFilters(entity, ["READ"], context); const authValidate = this.authorizationFactory.createEntityAuthValidate(entity, ["READ"], context, "BEFORE"); + const authAttributeFilters = this.createAttributeAuthFilters({ entity, context, @@ -774,8 +820,6 @@ export class OperationsFactory { when: "BEFORE", }); - const filters = this.filterFactory.createNodeFilters(entity, whereArgs); - operation.setFields(fields); operation.setFilters(filters); if (authFilters) { @@ -790,11 +834,46 @@ export class OperationsFactory { if (authAttributeValidate) { operation.addAuthFilters(...authAttributeValidate); } - this.hydrateCompositeReadOperationWithPagination(entity, operation, resolveTree); + if (sortArgs) { + const sortOptions = this.getOptions(entity, sortArgs); + + if (sortOptions) { + const sort = this.sortAndPaginationFactory.createSortFields(sortOptions, entity); + operation.addSort(...sort); + + const pagination = this.sortAndPaginationFactory.createPagination(sortOptions); + if (pagination) { + operation.addPagination(pagination); + } + } + } return operation; } + private hydrateReadOperation({ + entity, + operation, + resolveTree, + context, + whereArgs, + }: { + entity: ConcreteEntityAdapter; + operation: T; + resolveTree: ResolveTree; + context: Neo4jGraphQLTranslationContext; + whereArgs: Record; + }): T { + return this.hydrateOperation({ + entity, + operation, + context, + whereArgs, + fieldsByTypeName: resolveTree.fieldsByTypeName, + sortArgs: (resolveTree.args.options as Record) || {}, + }); + } + private hydrateAggregationOperation({ relationship, entity, @@ -892,7 +971,10 @@ export class OperationsFactory { return operation; } - private getOptions(entity: EntityAdapter, options: Record): GraphQLOptionsArg | undefined { + private getOptions(entity: EntityAdapter, options?: Record): GraphQLOptionsArg | undefined { + if (!options) { + return undefined; + } const limitDirective = isUnionEntity(entity) ? undefined : entity.annotations.limit; let limit: Integer | number | undefined = options?.limit ?? limitDirective?.default ?? limitDirective?.max; diff --git a/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts b/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts index d7bfb688521..ccc97a988ea 100644 --- a/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts @@ -17,6 +17,8 @@ * limitations under the License. */ +import type Cypher from "@neo4j/cypher-builder"; +import { SCORE_FIELD } from "../../../graphql/directives/fulltext"; import type { ConcreteEntityAdapter } from "../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { InterfaceEntityAdapter } from "../../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import type { UnionEntityAdapter } from "../../../schema-model/entity/model-adapters/UnionEntityAdapter"; @@ -24,6 +26,7 @@ import type { RelationshipAdapter } from "../../../schema-model/relationship/mod import type { ConnectionSortArg, GraphQLOptionsArg, GraphQLSortArg } from "../../../types"; import { Pagination } from "../ast/pagination/Pagination"; import { CypherPropertySort } from "../ast/sort/CypherPropertySort"; +import { FulltextScoreSort } from "../ast/sort/FulltextScoreSort"; import { PropertySort } from "../ast/sort/PropertySort"; import type { Sort } from "../ast/sort/Sort"; import { isConcreteEntity } from "../utils/is-concrete-entity"; @@ -32,9 +35,12 @@ import { isUnionEntity } from "../utils/is-union-entity"; export class SortAndPaginationFactory { public createSortFields( options: GraphQLOptionsArg, - entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter | UnionEntityAdapter + entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter | UnionEntityAdapter, + scoreVariable?: Cypher.Variable ): Sort[] { - return (options.sort || [])?.flatMap((s) => this.createPropertySort(s, entity)); + return (options.sort || [])?.flatMap((s) => { + return this.createPropertySort(s, entity, scoreVariable); + }); } public createConnectionSortFields( @@ -67,13 +73,22 @@ export class SortAndPaginationFactory { private createPropertySort( optionArg: GraphQLSortArg, - entity: ConcreteEntityAdapter | InterfaceEntityAdapter | RelationshipAdapter | UnionEntityAdapter + entity: ConcreteEntityAdapter | InterfaceEntityAdapter | RelationshipAdapter | UnionEntityAdapter, + scoreVariable?: Cypher.Variable ): Sort[] { if (isUnionEntity(entity)) { return []; } return Object.entries(optionArg).map(([fieldName, sortDir]) => { + // TODO: fix conflict with a a "score" fieldname + if (fieldName === SCORE_FIELD && scoreVariable) { + return new FulltextScoreSort({ + scoreVariable, + direction: sortDir, + }); + } + const attribute = entity.findAttribute(fieldName); if (!attribute) throw new Error(`no filter attribute ${fieldName}`); if (attribute.annotations.cypher) { diff --git a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts index bef3b271eb8..6a656340304 100644 --- a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts +++ b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts @@ -37,7 +37,7 @@ function generatedTypeDefs(personType: UniqueType, movieType: UniqueType): strin name: String! born: Int! actedInMovies: [${movieType.name}!]! @relationship(type: "ACTED_IN", direction: OUT) - } + } type ${movieType.name} { title: String! @@ -387,6 +387,7 @@ describe("@fulltext directive", () => { } } `; + const gqlResult = await graphql({ schema: generatedSchema, source: query, diff --git a/packages/graphql/tests/tck/fulltext/auth.test.ts b/packages/graphql/tests/tck/fulltext/auth.test.ts index 431d9854960..e5c842d6750 100644 --- a/packages/graphql/tests/tck/fulltext/auth.test.ts +++ b/packages/graphql/tests/tck/fulltext/auth.test.ts @@ -75,10 +75,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0) + WHERE ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0) RETURN this { .title } AS this" `); @@ -131,10 +131,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -189,10 +189,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -249,10 +249,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this2:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -312,10 +312,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this2:DIRECTED]-(this1:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -376,10 +376,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE ($param3 IS NOT NULL AND this0.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this2:Person) WHERE ($param3 IS NOT NULL AND this1.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -437,10 +437,10 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this0:DIRECTED]-(this1:Person) WHERE NOT ($param3 IS NOT NULL AND this0.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this2:Person) WHERE NOT ($param3 IS NOT NULL AND this1.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -492,12 +492,12 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[:DIRECTED]-(this0:Person) - WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + MATCH (this)<-[:DIRECTED]-(this1:Person) + WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) }) RETURN this { .title } AS this" `); @@ -551,12 +551,12 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[:DIRECTED]-(this0:Person) - WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + MATCH (this)<-[:DIRECTED]-(this1:Person) + WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -612,15 +612,15 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[:DIRECTED]-(this0:Person) - WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + MATCH (this)<-[:DIRECTED]-(this1:Person) + WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) } AND NOT (EXISTS { - MATCH (this)<-[:DIRECTED]-(this0:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + MATCH (this)<-[:DIRECTED]-(this1:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -678,12 +678,12 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -744,15 +744,15 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) } AND NOT (EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -814,12 +814,12 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE ($param3 IS NOT NULL AND this0.year = $param3) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE ($param3 IS NOT NULL AND this1.year = $param3) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -878,15 +878,15 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE ($param3 IS NOT NULL AND this0.year = $param3) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE ($param3 IS NOT NULL AND this1.year = $param3) } AND NOT (EXISTS { - MATCH (this)<-[this0:DIRECTED]-(this1:Person) - WHERE NOT ($param3 IS NOT NULL AND this0.year = $param3) + MATCH (this)<-[this1:DIRECTED]-(this2:Person) + WHERE NOT ($param3 IS NOT NULL AND this1.year = $param3) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); diff --git a/packages/graphql/tests/tck/fulltext/match.test.ts b/packages/graphql/tests/tck/fulltext/match.test.ts index ab874389085..9bef2efe409 100644 --- a/packages/graphql/tests/tck/fulltext/match.test.ts +++ b/packages/graphql/tests/tck/fulltext/match.test.ts @@ -50,7 +50,7 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE $param1 IN labels(this) RETURN this { .title } AS this" `); @@ -78,7 +78,7 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE ($param1 IN labels(this) AND this.title = $param2) RETURN this { .title } AS this" `); diff --git a/packages/graphql/tests/tck/fulltext/node-labels.test.ts b/packages/graphql/tests/tck/fulltext/node-labels.test.ts index 0373a947106..4aa8abf72db 100644 --- a/packages/graphql/tests/tck/fulltext/node-labels.test.ts +++ b/packages/graphql/tests/tck/fulltext/node-labels.test.ts @@ -47,7 +47,7 @@ describe("Cypher -> fulltext -> Additional Labels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE ($param1 IN labels(this) AND $param2 IN labels(this)) RETURN this { .title } AS this" `); @@ -93,7 +93,7 @@ describe("Cypher -> fulltext -> Additional Labels", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 WHERE ($param1 IN labels(this) AND $param2 IN labels(this)) RETURN this { .title } AS this" `); diff --git a/packages/graphql/tests/tck/fulltext/score.test.ts b/packages/graphql/tests/tck/fulltext/score.test.ts index 81f7b2ce178..a8ad7932254 100644 --- a/packages/graphql/tests/tck/fulltext/score.test.ts +++ b/packages/graphql/tests/tck/fulltext/score.test.ts @@ -100,4 +100,126 @@ describe("Cypher -> fulltext -> Score", () => { }" `); }); + + test("with score filtering", async () => { + const query = gql` + query { + moviesFulltextMovieTitle(phrase: "a different name", where: { score: { min: 0.5 } }) { + score + movie { + title + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE ($param1 IN labels(this) AND var0 >= $param2) + RETURN this { .title } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\", + \\"param2\\": 0.5 + }" + `); + }); + + test("with sorting", async () => { + const query = gql` + query { + moviesFulltextMovieTitle(phrase: "a different name", sort: { movie: { title: DESC } }) { + score + movie { + title + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE $param1 IN labels(this) + WITH * + ORDER BY this.title DESC + RETURN this { .title } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\" + }" + `); + }); + + test("with score sorting", async () => { + const query = gql` + query { + moviesFulltextMovieTitle(phrase: "a different name", sort: { score: ASC }) { + score + movie { + title + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE $param1 IN labels(this) + WITH * + ORDER BY var0 ASC + RETURN this { .title } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\" + }" + `); + }); + + test("with score and normal sorting", async () => { + const query = gql` + query { + moviesFulltextMovieTitle( + phrase: "a different name" + sort: [{ score: ASC }, { movie: { title: DESC } }] + ) { + score + movie { + title + } + } + } + `; + + const result = await translateQuery(neoSchema, query, {}); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 + WHERE $param1 IN labels(this) + WITH * + ORDER BY var0 ASC, this.title DESC + RETURN this { .title } AS movie, var0 AS score" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a different name\\", + \\"param1\\": \\"Movie\\" + }" + `); + }); }); From 2bc4716b8ef209f0f401f274221f060702efa4e2 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 22 Nov 2023 11:32:02 +0000 Subject: [PATCH 15/72] Remove legacy translate read --- .../graphql/src/translate/translate-read.ts | 229 +----------------- 1 file changed, 7 insertions(+), 222 deletions(-) diff --git a/packages/graphql/src/translate/translate-read.ts b/packages/graphql/src/translate/translate-read.ts index a3b5963b54b..f7bd764372b 100644 --- a/packages/graphql/src/translate/translate-read.ts +++ b/packages/graphql/src/translate/translate-read.ts @@ -17,73 +17,16 @@ * limitations under the License. */ -import Cypher from "@neo4j/cypher-builder"; +import type Cypher from "@neo4j/cypher-builder"; import Debug from "debug"; -import type { ResolveTree } from "graphql-parse-resolve-info"; -import { cursorToOffset } from "graphql-relay"; import type { Node } from "../classes"; import { DEBUG_TRANSLATE } from "../constants"; -import { SCORE_FIELD } from "../graphql/directives/fulltext"; import type { EntityAdapter } from "../schema-model/entity/EntityAdapter"; -import type { ConcreteEntityAdapter } from "../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import type { InterfaceEntityAdapter } from "../schema-model/entity/model-adapters/InterfaceEntityAdapter"; -import type { UnionEntityAdapter } from "../schema-model/entity/model-adapters/UnionEntityAdapter"; -import type { CypherFieldReferenceMap, GraphQLOptionsArg, GraphQLWhereArg } from "../types"; import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { compileCypher } from "../utils/compile-cypher"; -import createProjectionAndParams from "./create-projection-and-params"; -import { addSortAndLimitOptionsToClause } from "./projection/subquery/add-sort-and-limit-to-clause"; import { QueryASTFactory } from "./queryAST/factory/QueryASTFactory"; -import { isConcreteEntity } from "./queryAST/utils/is-concrete-entity"; -import { createMatchClause } from "./translate-top-level-match"; const debug = Debug(DEBUG_TRANSLATE); -function translateQuery({ - context, - entityAdapter, - resultVarName, -}: { - context: Neo4jGraphQLTranslationContext; - entityAdapter: EntityAdapter; - resultVarName: string; -}): Cypher.CypherResult { - const { resolveTree } = context; - // TODO: Rename QueryAST to OperationsTree - const queryASTFactory = new QueryASTFactory(context.schemaModel); - - if (!entityAdapter) throw new Error("Entity not found"); - const queryAST = queryASTFactory.createQueryAST(resolveTree, entityAdapter, context); - debug(queryAST.print()); - const clause = queryAST.build(context, resultVarName); - return clause.build(); -} - -/** - * This function maintains the old behavior where the resolveTree in the context was mutated by the connection resolver, - * in the new way all resolvers will use the queryASTFactory which doesn't requires this anymore . - **/ -function getConnectionResolveTree({ - context, - entityAdapter, -}: { - context: Neo4jGraphQLTranslationContext; - entityAdapter: ConcreteEntityAdapter | UnionEntityAdapter | InterfaceEntityAdapter; -}): ResolveTree { - if (isConcreteEntity(entityAdapter)) { - const edgeTree = context.resolveTree.fieldsByTypeName[`${entityAdapter.upperFirstPlural}Connection`]?.edges; - const nodeTree = edgeTree?.fieldsByTypeName[`${entityAdapter.name}Edge`]?.node; - const resolveTreeForContext = nodeTree || context.resolveTree; - - return { - ...resolveTreeForContext, - args: context.resolveTree.args, - }; - } else { - throw new Error("Root connection fields are not yet supported for interfaces and unions."); - } -} - export function translateRead( { node, @@ -98,170 +41,12 @@ export function translateRead( }, varName = "this" ): Cypher.CypherResult { - if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { - return translateQuery({ context, entityAdapter }); - } - if (isRootConnectionField) { - context.resolveTree = getConnectionResolveTree({ context, entityAdapter }); - } - const { resolveTree } = context; - if (!node) { - throw new Error("Translating Read: Node cannot be undefined."); - } - const matchNode = new Cypher.NamedNode(varName, { labels: node.getLabels(context) }); - - const cypherFieldAliasMap: CypherFieldReferenceMap = {}; - - const where = resolveTree.args.where as GraphQLWhereArg | undefined; - - let projAuth: Cypher.Clause | undefined; - - const { - matchClause: topLevelMatch, - preComputedWhereFieldSubqueries, - whereClause: topLevelWhereClause, - } = createMatchClause({ - matchNode, - node, - context, - operation: "READ", - where, - }); - - const projection = createProjectionAndParams({ - node, - context, - resolveTree, - varName: new Cypher.NamedNode(varName), - cypherFieldAliasMap, - }); - - const predicates: Cypher.Predicate[] = []; - - predicates.push(...projection.predicates); - - if (predicates.length) { - projAuth = new Cypher.With("*").where(Cypher.and(...predicates)); - } - - const projectionSubqueries = Cypher.concat(...projection.subqueries); - const projectionSubqueriesBeforeSort = Cypher.concat(...projection.subqueriesBeforeSort); + const operationsTreeFactory = new QueryASTFactory(context.schemaModel); - let orderClause: Cypher.Clause | Cypher.With | undefined; - - const optionsInput = (resolveTree.args.options || {}) as GraphQLOptionsArg; - - if (context.fulltext) { - optionsInput.sort = optionsInput.sort?.[node?.singular] || optionsInput.sort; - } - - if (node.limit) { - optionsInput.limit = node.limit.getLimit(optionsInput.limit); - resolveTree.args.options = resolveTree.args.options || {}; - (resolveTree.args.options as Record).limit = optionsInput.limit; - } - - const hasOrdering = optionsInput.sort || optionsInput.limit || optionsInput.offset; - - if (hasOrdering) { - orderClause = new Cypher.With("*"); - addSortAndLimitOptionsToClause({ - optionsInput, - target: matchNode, - projectionClause: orderClause as Cypher.With, - nodeField: node.singular, - fulltextScoreVariable: context.fulltext?.scoreVariable, - cypherFields: node.cypherFields, - cypherFieldAliasMap, - graphElement: node, - }); - } - - const projectionExpression = new Cypher.RawCypher((env) => { - return [`${varName} ${compileCypher(projection.projection, env)}`, projection.params]; - }); - - let returnClause = new Cypher.Return([projectionExpression, varName]); - - if (context.fulltext?.scoreVariable) { - returnClause = new Cypher.Return( - [projectionExpression, varName], - [context.fulltext?.scoreVariable, SCORE_FIELD] - ); - } - - let projectionClause: Cypher.Clause = returnClause; // TODO avoid reassign - let connectionPreClauses: Cypher.Clause | undefined; - - if (isRootConnectionField) { - const hasConnectionOrdering = resolveTree.args.first || resolveTree.args.after || resolveTree.args.sort; - if (hasConnectionOrdering) { - const afterInput = resolveTree.args.after as string | undefined; - const offset = afterInput ? cursorToOffset(afterInput) + 1 : undefined; - orderClause = new Cypher.With("*"); - addSortAndLimitOptionsToClause({ - optionsInput: { - sort: resolveTree.args.sort as any, - limit: resolveTree.args.first as any, - offset, - }, - target: matchNode, - projectionClause: orderClause as Cypher.With, - nodeField: node.singular, - fulltextScoreVariable: context.fulltext?.scoreVariable, - cypherFields: node.cypherFields, - cypherFieldAliasMap, - graphElement: node, - }); - } - - // TODO: unify with createConnectionClause - const edgesVar = new Cypher.NamedVariable("edges"); - const edgeVar = new Cypher.NamedVariable("edge"); - const totalCountVar = new Cypher.NamedVariable("totalCount"); - - const withCollect = new Cypher.With([Cypher.collect(matchNode), edgesVar]).with(edgesVar, [ - Cypher.size(edgesVar), - totalCountVar, - ]); - - const unwind = new Cypher.Unwind([edgesVar, matchNode]).with(matchNode, totalCountVar); - connectionPreClauses = Cypher.concat(withCollect, unwind); - - const connectionEdge = new Cypher.Map({ - node: projectionExpression, - }); - - const withTotalCount = new Cypher.With([connectionEdge, edgeVar], totalCountVar, matchNode); - const returnClause = new Cypher.With([Cypher.collect(edgeVar), edgesVar], totalCountVar).return([ - new Cypher.Map({ - edges: edgesVar, - totalCount: totalCountVar, - }), - matchNode, - ]); - - projectionClause = Cypher.concat(withTotalCount, returnClause); - } - - const preComputedWhereFields: Cypher.Clause | undefined = - preComputedWhereFieldSubqueries && !preComputedWhereFieldSubqueries.empty - ? Cypher.concat(preComputedWhereFieldSubqueries, topLevelWhereClause) - : topLevelWhereClause; - - const readQuery = Cypher.concat( - topLevelMatch, - preComputedWhereFields, - projAuth, - connectionPreClauses, - projectionSubqueriesBeforeSort, - orderClause, // Required for performance optimization - projectionSubqueries, - projectionClause - ); - - const result = readQuery.build(); - - return result; + if (!entityAdapter) throw new Error("Entity not found"); + const operationsTree = operationsTreeFactory.createQueryAST(resolveTree, entityAdapter, context); + debug(operationsTree.print()); + const clause = operationsTree.build(context, varName); + return clause.build(); } From 98060cc3e1399209ba14f0196ef9d74900a3d30f Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 22 Nov 2023 11:40:34 +0000 Subject: [PATCH 16/72] Change setFilters for addFilters in queryAST --- .../property-filters/FulltextScoreFilter.ts | 48 ------------------- .../ast/operations/AggregationOperation.ts | 4 +- .../ast/operations/ConnectionReadOperation.ts | 28 +++++------ .../queryAST/ast/operations/ReadOperation.ts | 9 +--- .../CompositeAggregationOperation.ts | 6 +-- .../queryAST/factory/OperationFactory.ts | 17 +++---- 6 files changed, 29 insertions(+), 83 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts index 54b21b94eee..ea400bc2c5a 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/FulltextScoreFilter.ts @@ -39,17 +39,6 @@ export class FulltextScoreFilter extends Filter { return []; } - // constructor(options: { - // attribute: AttributeAdapter; - // comparisonValue: CypherVariable; - // operator: FilterOperator; - // isNot: boolean; - // attachedTo?: "node" | "relationship"; - // }) { - // super(options); - // this.comparisonValue = options.comparisonValue; - // } - public getPredicate(_queryASTContext: QueryASTContext): Cypher.Predicate { const predicates: Cypher.Predicate[] = []; @@ -63,42 +52,5 @@ export class FulltextScoreFilter extends Filter { } return Cypher.and(...predicates); - - // const predicate = super.getPredicate(queryASTContext); - - // // NOTE: Should this check be a different Filter? - // return Cypher.and(Cypher.isNotNull(this.comparisonValue), predicate); } - - // protected getOperation(): Cypher.ComparisonOp { - // const comparisonParam = new Cypher.Param(this.comparisonValue); - // return createComparisonOperation({ - // operator: this.operator, - // property: this.score, - // param: comparisonParam, - // }); - // if (whereInput?.[SCORE_FIELD]) { - // if (whereInput[SCORE_FIELD].min || whereInput[SCORE_FIELD].min === 0) { - // const scoreMinOp = Cypher.gte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].min)); - // if (scoreMinOp) whereOperators.push(scoreMinOp); - // } - // if (whereInput[SCORE_FIELD].max || whereInput[SCORE_FIELD].max === 0) { - // const scoreMaxOp = Cypher.lte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].max)); - // if (scoreMaxOp) whereOperators.push(scoreMaxOp); - // } - // } } - -/** Returns the default operation for a given filter */ -// protected createBaseOperation({ -// operator, -// property, -// param, -// }: { -// operator: FilterOperator; -// property: Cypher.Expr; -// param: Cypher.Expr; -// }): Cypher.ComparisonOp { - -// return createComparisonOperation({ operator, property: coalesceProperty, param }); -// } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts index 948519e38b2..06586620063 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts @@ -76,8 +76,8 @@ export class AggregationOperation extends Operation { this.pagination = pagination; } - public setFilters(filters: Filter[]) { - this.filters = filters; + public addFilters(...filters: Filter[]) { + this.filters.push(...filters); } public addAuthFilters(...filter: AuthorizationFilters[]) { diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts index 33d33c3ab09..f4808d8ef00 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts @@ -17,24 +17,24 @@ * limitations under the License. */ -import { createNodeFromEntity } from "../../utils/create-node-from-entity"; -import type { Field } from "../fields/Field"; -import type { Filter } from "../filters/Filter"; import Cypher from "@neo4j/cypher-builder"; -import type { OperationTranspileResult } from "./operations"; -import { Operation } from "./operations"; -import type { Pagination, PaginationField } from "../pagination/Pagination"; -import type { Sort, SortField } from "../sort/Sort"; -import type { QueryASTContext } from "../QueryASTContext"; -import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters"; +import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { filterTruthy } from "../../../../utils/utils"; -import type { QueryASTNode } from "../QueryASTNode"; import { hasTarget } from "../../utils/context-has-target"; +import { createNodeFromEntity } from "../../utils/create-node-from-entity"; +import { wrapSubqueriesInCypherCalls } from "../../utils/wrap-subquery-in-calls"; +import type { QueryASTContext } from "../QueryASTContext"; +import type { QueryASTNode } from "../QueryASTNode"; +import type { Field } from "../fields/Field"; import { CypherAttributeField } from "../fields/attribute-fields/CypherAttributeField"; +import type { Filter } from "../filters/Filter"; +import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters"; +import type { Pagination, PaginationField } from "../pagination/Pagination"; import { CypherPropertySort } from "../sort/CypherPropertySort"; -import { wrapSubqueriesInCypherCalls } from "../../utils/wrap-subquery-in-calls"; +import type { Sort, SortField } from "../sort/Sort"; +import type { OperationTranspileResult } from "./operations"; +import { Operation } from "./operations"; export class ConnectionReadOperation extends Operation { public readonly relationship: RelationshipAdapter | undefined; @@ -68,8 +68,8 @@ export class ConnectionReadOperation extends Operation { this.nodeFields = fields; } - public setFilters(filters: Filter[]) { - this.filters = filters; + public addFilters(...filters: Filter[]) { + this.filters.push(...filters); } public setEdgeFields(fields: Field[]) { diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index 53ada8c4e7f..8e532cf366c 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -78,13 +78,8 @@ export class ReadOperation extends Operation { this.pagination = pagination; } - // TODO: avoid this filter override as it makes harder to compose in the factory - public setFilters(filters: Filter[]) { - this.filters = filters; - } - - public addFilter(filter: Filter) { - this.filters.push(filter); + public addFilters(...filters: Filter[]) { + this.filters.push(...filters); } public addAuthFilters(...filter: AuthorizationFilters[]) { diff --git a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts index 00ff8b9b921..3786a4d7bc6 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts @@ -119,11 +119,9 @@ export class CompositeAggregationOperation extends Operation { public addPagination(pagination: Pagination): void { this.pagination = pagination; } - - public setFilters(filters: Filter[]) { - this.filters = filters; + public addFilters(...filters: Filter[]) { + this.filters.push(...filters); } - public addAuthFilters(...filter: AuthorizationFilters[]) { this.authFilters.push(...filter); } diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 6c5e52ce48a..c7c851f3851 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -279,6 +279,10 @@ export class OperationsFactory { scoreVariable: fulltextOptions.score, }); + if (scoreFilter) { + operation.addFilters(scoreFilter); + } + this.hydrateOperation({ operation, entity, @@ -287,9 +291,6 @@ export class OperationsFactory { whereArgs: resolveTreeWhere, }); - if (scoreFilter) { - operation.addFilter(scoreFilter); - } // Override sort to support score const sortOptions2 = this.getOptions(entity, sortOptions); @@ -486,7 +487,7 @@ export class OperationsFactory { const filters = this.filterFactory.createNodeFilters(entity, whereArgs); // Aggregation filters only apply to target node - operation.setFilters(filters); + operation.addFilters(...filters); if (authFilters.length > 0 || authValidate.length > 0) { operation.addAuthFilters(...authFilters); @@ -732,7 +733,7 @@ export class OperationsFactory { operation.setNodeFields(nodeFields); operation.setEdgeFields(edgeFields); - operation.setFilters(filters); + operation.addFilters(...filters); if (authFilters) { operation.addAuthFilters(authFilters); } @@ -821,7 +822,7 @@ export class OperationsFactory { }); operation.setFields(fields); - operation.setFilters(filters); + operation.addFilters(...filters); if (authFilters) { operation.addAuthFilters(authFilters); } @@ -924,7 +925,7 @@ export class OperationsFactory { operation.setFields(fields); operation.setNodeFields(nodeFields); operation.setEdgeFields(edgeFields); - operation.setFilters(filters); + operation.addFilters(...filters); if (authFilters) { operation.addAuthFilters(authFilters); @@ -947,7 +948,7 @@ export class OperationsFactory { ); const filters = this.filterFactory.createNodeFilters(entity, whereArgs); // Aggregation filters only apply to target node operation.setFields(fields); - operation.setFilters(filters); + operation.addFilters(...filters); if (authFilters) { operation.addAuthFilters(authFilters); From 226e5edd22d4bff0767392079bedb58313dd606d Mon Sep 17 00:00:00 2001 From: Darrell Warde <8117355+darrellwarde@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:25:59 +0000 Subject: [PATCH 17/72] Fix in authorization context creation (#4247) * Small fix in authorization context creation * Tweak logic to allow JWT without authorization settings * Update TCK tests --- .changeset/afraid-countries-pretend.md | 5 ++ .../utils/get-authorization-context.test.ts | 69 +++++++++++++++++++ .../utils/get-authorization-context.ts | 50 ++++++++------ .../graphql/tests/tck/issues/4118.test.ts | 3 +- .../graphql/tests/tck/issues/4170.test.ts | 3 +- .../graphql/tests/tck/issues/4223.test.ts | 3 +- 6 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 .changeset/afraid-countries-pretend.md create mode 100644 packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.test.ts diff --git a/.changeset/afraid-countries-pretend.md b/.changeset/afraid-countries-pretend.md new file mode 100644 index 00000000000..f6d7686ac4b --- /dev/null +++ b/.changeset/afraid-countries-pretend.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Fix issue in authorization context generation. diff --git a/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.test.ts b/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.test.ts new file mode 100644 index 00000000000..92cdc5dacbb --- /dev/null +++ b/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createBearerToken } from "../../../../../tests/utils/create-bearer-token"; +import { Neo4jGraphQLAuthorization } from "../../../../classes/authorization/Neo4jGraphQLAuthorization"; +import { getAuthorizationContext } from "./get-authorization-context"; + +describe("getAuthorizationContext", () => { + const secret = "secret"; + + test("no authorization settings and token returns unauthorized context", async () => { + const context = await getAuthorizationContext({ token: createBearerToken(secret, { sub: "user" }) }, undefined); + expect(context.isAuthenticated).toBe(false); + }); + + test("no authorization settings and jwt returns authorized context", async () => { + const context = await getAuthorizationContext({ jwt: { sub: "user" } }, undefined); + expect(context.isAuthenticated).toBe(true); + expect(context.jwt?.sub).toBe("user"); + }); + + test("authorization settings but no jwt or token returns unauthorized context", async () => { + const context = await getAuthorizationContext({}, new Neo4jGraphQLAuthorization({ key: secret })); + expect(context.isAuthenticated).toBe(false); + }); + + test("decoded jwt returns authorized context", async () => { + const context = await getAuthorizationContext( + { jwt: { sub: "user" } }, + new Neo4jGraphQLAuthorization({ key: secret }) + ); + expect(context.isAuthenticated).toBe(true); + expect(context.jwt?.sub).toBe("user"); + }); + + test("token returns authorized context", async () => { + const context = await getAuthorizationContext( + { token: createBearerToken(secret, { sub: "user" }) }, + new Neo4jGraphQLAuthorization({ key: secret }) + ); + expect(context.isAuthenticated).toBe(true); + expect(context.jwt?.sub).toBe("user"); + }); + + test("decoded jwt and token returns authorized context using jwt", async () => { + const context = await getAuthorizationContext( + { jwt: { sub: "user1" }, token: createBearerToken(secret, { sub: "user2" }) }, + new Neo4jGraphQLAuthorization({ key: secret }) + ); + expect(context.isAuthenticated).toBe(true); + expect(context.jwt?.sub).toBe("user1"); + }); +}); diff --git a/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.ts b/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.ts index e044c792e9a..3793ec89585 100644 --- a/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.ts +++ b/packages/graphql/src/schema/resolvers/composition/utils/get-authorization-context.ts @@ -28,12 +28,37 @@ import { debugObject } from "../../../../debug/debug-object"; const debug = Debug(DEBUG_AUTH); +const unauthorizedContext = { + isAuthenticated: false, + jwtParam: new Cypher.NamedParam("jwt", {}), + isAuthenticatedParam: new Cypher.NamedParam("isAuthenticated", false), +}; + export async function getAuthorizationContext( context: Neo4jGraphQLContext | Neo4jGraphQLSubscriptionsConnectionParams, authorization?: Neo4jGraphQLAuthorization, jwtClaimsMap?: Map ): Promise { - if (!context.jwt && authorization) { + if (context.jwt) { + const isAuthenticated = true; + const jwt = context.jwt; + + debugObject(debug, "using JWT provided in context", jwt); + + return { + isAuthenticated, + jwt, + jwtParam: new Cypher.NamedParam("jwt", jwt), + isAuthenticatedParam: new Cypher.NamedParam("isAuthenticated", isAuthenticated), + }; + } + + if (!authorization) { + debug("authorization settings not specified, request not authenticated"); + return unauthorizedContext; + } + + if (context.token) { const jwt = await authorization.decode(context); if (jwt) { context.jwt = jwt; @@ -48,28 +73,9 @@ export async function getAuthorizationContext( isAuthenticatedParam: new Cypher.NamedParam("isAuthenticated", isAuthenticated), claims: jwtClaimsMap, }; - } else { - const isAuthenticated = false; - - debug("request not authenticated"); - - return { - isAuthenticated, - jwtParam: new Cypher.NamedParam("jwt", {}), - isAuthenticatedParam: new Cypher.NamedParam("isAuthenticated", isAuthenticated), - }; } } - const isAuthenticated = true; - const jwt = context.jwt; - - debugObject(debug, "using JWT provided in context", jwt); - - return { - isAuthenticated, - jwt, - jwtParam: new Cypher.NamedParam("jwt", jwt), - isAuthenticatedParam: new Cypher.NamedParam("isAuthenticated", isAuthenticated), - }; + debug("request not authenticated"); + return unauthorizedContext; } diff --git a/packages/graphql/tests/tck/issues/4118.test.ts b/packages/graphql/tests/tck/issues/4118.test.ts index b75774a250b..db800738d35 100644 --- a/packages/graphql/tests/tck/issues/4118.test.ts +++ b/packages/graphql/tests/tck/issues/4118.test.ts @@ -223,7 +223,8 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"isAuthenticated\\": true, + \\"isAuthenticated\\": false, + \\"jwt\\": {}, \\"create_param2\\": \\"overlord\\", \\"this0_host_connect0_node_param0\\": \\"userid\\", \\"authorization_0_0_0_0_before_param2\\": \\"overlord\\", diff --git a/packages/graphql/tests/tck/issues/4170.test.ts b/packages/graphql/tests/tck/issues/4170.test.ts index f1185230c88..1351b88f20b 100644 --- a/packages/graphql/tests/tck/issues/4170.test.ts +++ b/packages/graphql/tests/tck/issues/4170.test.ts @@ -227,7 +227,8 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"isAuthenticated\\": true, + \\"isAuthenticated\\": false, + \\"jwt\\": {}, \\"this0_settings0_node_openingDays0_node_open0_node_name\\": \\"lambo\\", \\"this0_admins0_node_userId\\": \\"123\\", \\"resolvedCallbacks\\": { diff --git a/packages/graphql/tests/tck/issues/4223.test.ts b/packages/graphql/tests/tck/issues/4223.test.ts index b0205c522ed..babf4cfc3b2 100644 --- a/packages/graphql/tests/tck/issues/4223.test.ts +++ b/packages/graphql/tests/tck/issues/4223.test.ts @@ -273,7 +273,8 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"isAuthenticated\\": true, + \\"isAuthenticated\\": false, + \\"jwt\\": {}, \\"this0_settings0_node_openingDays0_node_open0_node_name\\": \\"lambo\\", \\"this0_settings0_node_myWorkspace0_node_workspace\\": \\"myWorkspace\\", \\"this0_admins0_node_userId\\": \\"123\\", From c09aa9bb1a6ee3d13f918b0fed483893055fb1f1 Mon Sep 17 00:00:00 2001 From: Darrell Warde <8117355+darrellwarde@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:41:37 +0000 Subject: [PATCH 18/72] Fix `events` functionality of `@subscriptionsAuthorization` (#4144) * Fix `events` functionality of `@subscriptionsAuthorization` * Don't subscribe twice in test * Simplify switch * Don't reimplement types --- .changeset/happy-singers-fail.md | 5 + .../static-definitions.ts | 13 ++ .../subscriptions-authorization.ts | 5 + .../SubscriptionsAuthorizationAnnotation.ts | 10 +- .../subscriptions/where/authorization.ts | 22 ++- .../authorization/filter/events.e2e.test.ts | 159 ++++++++++++++++++ 6 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 .changeset/happy-singers-fail.md create mode 100644 packages/graphql/tests/e2e/subscriptions/authorization/filter/events.e2e.test.ts diff --git a/.changeset/happy-singers-fail.md b/.changeset/happy-singers-fail.md new file mode 100644 index 00000000000..2089a341775 --- /dev/null +++ b/.changeset/happy-singers-fail.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Include the `@subscriptionsAuthorization` `events` argument in validation. diff --git a/packages/graphql/src/graphql/directives/type-dependant-directives/static-definitions.ts b/packages/graphql/src/graphql/directives/type-dependant-directives/static-definitions.ts index 81c79361674..88b399c879b 100644 --- a/packages/graphql/src/graphql/directives/type-dependant-directives/static-definitions.ts +++ b/packages/graphql/src/graphql/directives/type-dependant-directives/static-definitions.ts @@ -73,6 +73,17 @@ export const AUTHENTICATION_OPERATION = new GraphQLEnumType({ }, }); +export const SUBSCRIPTIONS_AUTHORIZATION_FILTER_EVENT = new GraphQLEnumType({ + name: "SubscriptionsAuthorizationFilterEvent", + values: { + CREATED: { value: "CREATED" }, + UPDATED: { value: "UPDATED" }, + DELETED: { value: "DELETED" }, + RELATIONSHIP_CREATED: { value: "RELATIONSHIP_CREATED" }, + RELATIONSHIP_DELETED: { value: "RELATIONSHIP_DELETED" }, + }, +}); + export function getStaticAuthorizationDefinitions( JWTPayloadDefinition?: ObjectTypeDefinitionNode ): Array { @@ -81,11 +92,13 @@ export function getStaticAuthorizationDefinitions( const authorizationValidateOperation = astFromEnumType(AUTHORIZATION_VALIDATE_OPERATION, schema); const authorizationFilterOperation = astFromEnumType(AUTHORIZATION_FILTER_OPERATION, schema); const authenticationOperation = astFromEnumType(AUTHENTICATION_OPERATION, schema); + const subscriptionsAuthorizationFilterOperation = astFromEnumType(SUBSCRIPTIONS_AUTHORIZATION_FILTER_EVENT, schema); const ASTs: Array = [ authorizationValidateStage, authorizationValidateOperation, authorizationFilterOperation, authenticationOperation, + subscriptionsAuthorizationFilterOperation, ]; const JWTPayloadWhere = createJWTPayloadWhere(schema, JWTPayloadDefinition); diff --git a/packages/graphql/src/graphql/directives/type-dependant-directives/subscriptions-authorization.ts b/packages/graphql/src/graphql/directives/type-dependant-directives/subscriptions-authorization.ts index 63edda2d8e0..35005204d12 100644 --- a/packages/graphql/src/graphql/directives/type-dependant-directives/subscriptions-authorization.ts +++ b/packages/graphql/src/graphql/directives/type-dependant-directives/subscriptions-authorization.ts @@ -29,6 +29,7 @@ import { GraphQLSchema, GraphQLString, } from "graphql"; +import { SUBSCRIPTIONS_AUTHORIZATION_FILTER_EVENT } from "./static-definitions"; function createSubscriptionsAuthorizationWhere( typeDefinitionName: string, @@ -88,6 +89,10 @@ function createSubscriptionsAuthorizationFilterRule( name: `${typeDefinitionName}SubscriptionsAuthorizationFilterRule`, fields() { return { + events: { + type: new GraphQLList(SUBSCRIPTIONS_AUTHORIZATION_FILTER_EVENT), + defaultValue: ["CREATED", "UPDATED", "DELETED", "RELATIONSHIP_CREATED", "RELATIONSHIP_DELETED"], + }, requireAuthentication: { type: GraphQLBoolean, defaultValue: true, diff --git a/packages/graphql/src/schema-model/annotation/SubscriptionsAuthorizationAnnotation.ts b/packages/graphql/src/schema-model/annotation/SubscriptionsAuthorizationAnnotation.ts index 6b8d74ab2f5..8c60f58a897 100644 --- a/packages/graphql/src/schema-model/annotation/SubscriptionsAuthorizationAnnotation.ts +++ b/packages/graphql/src/schema-model/annotation/SubscriptionsAuthorizationAnnotation.ts @@ -22,11 +22,11 @@ import type { GraphQLWhereArg } from "../../types"; export const SubscriptionsAuthorizationAnnotationArguments = ["filter"] as const; export const SubscriptionsAuthorizationFilterEventRule = [ - "CREATE", - "UPDATE", - "DELETE", - "CREATE_RELATIONSHIP", - "DELETE_RELATIONSHIP", + "CREATED", + "UPDATED", + "DELETED", + "RELATIONSHIP_CREATED", + "RELATIONSHIP_DELETED", ] as const; export type SubscriptionsAuthorizationFilterEvent = (typeof SubscriptionsAuthorizationFilterEventRule)[number]; diff --git a/packages/graphql/src/schema/resolvers/subscriptions/where/authorization.ts b/packages/graphql/src/schema/resolvers/subscriptions/where/authorization.ts index aff0ec89134..c30b957a1bf 100644 --- a/packages/graphql/src/schema/resolvers/subscriptions/where/authorization.ts +++ b/packages/graphql/src/schema/resolvers/subscriptions/where/authorization.ts @@ -17,9 +17,11 @@ * limitations under the License. */ +import type { SubscriptionsAuthorizationFilterEvent } from "../../../../schema-model/annotation/SubscriptionsAuthorizationAnnotation"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { SubscriptionsEvent } from "../../../../types"; import type { Neo4jGraphQLComposedSubscriptionsContext } from "../../composition/wrap-subscription"; +import type { SubscriptionEventType } from "../types"; import { filterByAuthorizationRules } from "./filters/filter-by-authorization-rules"; import { multipleConditionsAggregationMap } from "./utils/multiple-conditions-aggregation-map"; import { populateWhereParams } from "./utils/populate-where-params"; @@ -36,7 +38,7 @@ export function subscriptionAuthorization({ const subscriptionsAuthorization = entity.annotations.subscriptionsAuthorization; const matchedRules = (subscriptionsAuthorization?.filter || []).filter((rule) => - rule.events.some((e) => e.toLowerCase() === event.event) + rule.events.some((e) => authorizationEventMatchesEvent(e, event.event)) ); if (!matchedRules.length) { @@ -60,3 +62,21 @@ export function subscriptionAuthorization({ return multipleConditionsAggregationMap.OR(results); } + +function authorizationEventMatchesEvent( + authorizationEvent: SubscriptionsAuthorizationFilterEvent, + event: SubscriptionEventType +): boolean { + switch (authorizationEvent) { + case "CREATED": + return event === "create"; + case "UPDATED": + return event === "update"; + case "DELETED": + return event === "delete"; + case "RELATIONSHIP_CREATED": + return event === "create_relationship"; + case "RELATIONSHIP_DELETED": + return event === "delete_relationship"; + } +} diff --git a/packages/graphql/tests/e2e/subscriptions/authorization/filter/events.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/authorization/filter/events.e2e.test.ts new file mode 100644 index 00000000000..05a638fd912 --- /dev/null +++ b/packages/graphql/tests/e2e/subscriptions/authorization/filter/events.e2e.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Driver } from "neo4j-driver"; +import type { Response } from "supertest"; +import supertest from "supertest"; +import { Neo4jGraphQL } from "../../../../../src/classes"; +import { UniqueType } from "../../../../utils/graphql-types"; +import type { TestGraphQLServer } from "../../../setup/apollo-server"; +import { ApolloTestServer } from "../../../setup/apollo-server"; +import { WebSocketTestClient } from "../../../setup/ws-client"; +import Neo4j from "../../../setup/neo4j"; +import { createJwtHeader } from "../../../../utils/create-jwt-request"; +import { Neo4jGraphQLSubscriptionsDefaultEngine } from "../../../../../src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine"; + +describe("Subscriptions authorization with create events", () => { + let neo4j: Neo4j; + let driver: Driver; + let server: TestGraphQLServer; + let wsClient: WebSocketTestClient; + let User: UniqueType; + let key: string; + + beforeEach(async () => { + key = "secret"; + + User = new UniqueType("User"); + + const typeDefs = `#graphql + type JWTPayload @jwt { + roles: [String!]! @jwtClaim(path: "myApplication.roles") + } + + type ${User} + @subscriptionsAuthorization( + filter: [ + { events: [CREATED], where: { node: { id: "$jwt.sub" } } } + ] + ) { + id: ID! + } + `; + + neo4j = new Neo4j(); + driver = await neo4j.getDriver(); + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + driver, + features: { + authorization: { key }, + subscriptions: new Neo4jGraphQLSubscriptionsDefaultEngine(), + }, + }); + + // eslint-disable-next-line @typescript-eslint/require-await + server = new ApolloTestServer(neoSchema, async ({ req }) => ({ + sessionConfig: { + database: neo4j.getIntegrationDatabaseName(), + }, + token: req.headers.authorization, + })); + await server.start(); + }); + + afterEach(async () => { + await wsClient.close(); + + await server.close(); + await driver.close(); + }); + + test("authorization filters don't apply to delete events", async () => { + const jwtToken = createJwtHeader(key, { sub: "user1", myApplication: { roles: ["user"] } }); + wsClient = new WebSocketTestClient(server.wsPath, jwtToken); + + await wsClient.subscribe(` + subscription { + ${User.operations.subscribe.deleted} { + ${User.operations.subscribe.payload.deleted} { + id + } + } + } + `); + + await createUser("user1"); + await createUser("user2"); + await deleteUser("user1"); + await deleteUser("user2"); + + await wsClient.waitForEvents(2); + + expect(wsClient.errors).toEqual([]); + expect(wsClient.events).toEqual([ + { + [User.operations.subscribe.deleted]: { + [User.operations.subscribe.payload.deleted]: { id: "user1" }, + }, + }, + { + [User.operations.subscribe.deleted]: { + [User.operations.subscribe.payload.deleted]: { id: "user2" }, + }, + }, + ]); + }); + + async function createUser(id: string): Promise { + const result = await supertest(server.path) + .post("") + .send({ + query: ` + mutation { + ${User.operations.create}(input: [{ id: "${id}" }]) { + ${User.plural} { + id + } + } + } + `, + }) + .expect(200); + return result; + } + + async function deleteUser(id: string): Promise { + const result = await supertest(server.path) + .post("") + .set("Authorization", createJwtHeader(key, { sub: "user1", myApplication: { roles: ["admin"] } })) + .send({ + query: ` + mutation { + ${User.operations.delete}(where: { id: "${id}" }) { + nodesDeleted + } + } + `, + }) + .expect(200); + return result; + } +}); From a2e9b6f74aa809a4be98d0d191748987d51a4dcb Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 22 Nov 2023 13:52:02 +0000 Subject: [PATCH 19/72] Reorder private methods in operation factory --- .../queryAST/factory/OperationFactory.ts | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index c7c851f3851..bf12fd08524 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -141,81 +141,6 @@ export class OperationsFactory { return this.createReadOperation(entity, resolveTree, context); } - // The current top-level Connection API is inconsistent with the rest of the API making the parsing more complex than it should be. - // This function temporary adjust some inconsistencies waiting for the new API. - // TODO: Remove it when the new API is ready. - private normalizeResolveTreeForTopLevelConnection(resolveTree: ResolveTree): ResolveTree { - const topLevelConnectionResolveTree = Object.assign({}, resolveTree); - // Move the sort arguments inside a "node" object. - if (topLevelConnectionResolveTree.args.sort) { - topLevelConnectionResolveTree.args.sort = (resolveTree.args.sort as any[]).map((sortField) => { - return { node: sortField }; - }); - } - // move the where arguments inside a "node" object. - if (topLevelConnectionResolveTree.args.where) { - topLevelConnectionResolveTree.args.where = { node: resolveTree.args.where }; - } - return topLevelConnectionResolveTree; - } - - private createCreateOperation( - entity: ConcreteEntityAdapter, - resolveTree: ResolveTree, - context: Neo4jGraphQLTranslationContext - ): CreateOperation { - const responseFields = Object.values( - resolveTree.fieldsByTypeName[entity.operations.mutationResponseTypeNames.create] ?? {} - ); - const createOP = new CreateOperation({ target: entity }); - const projectionFields = responseFields - .filter((f) => f.name === entity.plural) - .map((field) => { - const readOP = this.createReadOperation(entity, field, context) as ReadOperation; - return readOP; - }); - - createOP.addProjectionOperations(projectionFields); - return createOP; - } - - private getFulltextOptions(context: Neo4jGraphQLTranslationContext): FulltextOptions { - if (context.fulltext) { - const indexName = context.fulltext.indexName || context.fulltext.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - const phrase = context.resolveTree.args.phrase; - if (!phrase || typeof phrase !== "string") { - throw new Error("Invalid phrase"); - } - - return { - index: indexName, - phrase, - score: context.fulltext.scoreVariable, - }; - } - - const entries = Object.entries(context.resolveTree.args.fulltext || {}); - if (entries.length > 1) { - throw new Error("Can only call one search at any given time"); - } - const [indexName, indexInput] = entries[0] as [string, { phrase: string }]; - return { - index: indexName, - phrase: indexInput.phrase, - score: new Cypher.Variable(), - }; - } - - private createFulltextScoreField(field: ResolveTree, scoreVar: Cypher.Variable): FulltextScoreField { - return new FulltextScoreField({ - alias: field.alias, - score: scoreVar, - }); - } - public createFulltextOperation( entityOrRel: EntityAdapter | RelationshipAdapter, resolveTree: ResolveTree, @@ -621,6 +546,81 @@ export class OperationsFactory { }); } + // The current top-level Connection API is inconsistent with the rest of the API making the parsing more complex than it should be. + // This function temporary adjust some inconsistencies waiting for the new API. + // TODO: Remove it when the new API is ready. + private normalizeResolveTreeForTopLevelConnection(resolveTree: ResolveTree): ResolveTree { + const topLevelConnectionResolveTree = Object.assign({}, resolveTree); + // Move the sort arguments inside a "node" object. + if (topLevelConnectionResolveTree.args.sort) { + topLevelConnectionResolveTree.args.sort = (resolveTree.args.sort as any[]).map((sortField) => { + return { node: sortField }; + }); + } + // move the where arguments inside a "node" object. + if (topLevelConnectionResolveTree.args.where) { + topLevelConnectionResolveTree.args.where = { node: resolveTree.args.where }; + } + return topLevelConnectionResolveTree; + } + + private createCreateOperation( + entity: ConcreteEntityAdapter, + resolveTree: ResolveTree, + context: Neo4jGraphQLTranslationContext + ): CreateOperation { + const responseFields = Object.values( + resolveTree.fieldsByTypeName[entity.operations.mutationResponseTypeNames.create] ?? {} + ); + const createOP = new CreateOperation({ target: entity }); + const projectionFields = responseFields + .filter((f) => f.name === entity.plural) + .map((field) => { + const readOP = this.createReadOperation(entity, field, context) as ReadOperation; + return readOP; + }); + + createOP.addProjectionOperations(projectionFields); + return createOP; + } + + private getFulltextOptions(context: Neo4jGraphQLTranslationContext): FulltextOptions { + if (context.fulltext) { + const indexName = context.fulltext.indexName || context.fulltext.name; + if (indexName === undefined) { + throw new Error("The name of the fulltext index should be defined using the indexName argument."); + } + const phrase = context.resolveTree.args.phrase; + if (!phrase || typeof phrase !== "string") { + throw new Error("Invalid phrase"); + } + + return { + index: indexName, + phrase, + score: context.fulltext.scoreVariable, + }; + } + + const entries = Object.entries(context.resolveTree.args.fulltext || {}); + if (entries.length > 1) { + throw new Error("Can only call one search at any given time"); + } + const [indexName, indexInput] = entries[0] as [string, { phrase: string }]; + return { + index: indexName, + phrase: indexInput.phrase, + score: new Cypher.Variable(), + }; + } + + private createFulltextScoreField(field: ResolveTree, scoreVar: Cypher.Variable): FulltextScoreField { + return new FulltextScoreField({ + alias: field.alias, + score: scoreVar, + }); + } + // eslint-disable-next-line @typescript-eslint/comma-dangle private hydrateConnectionOperationsASTWithSort< T extends ConnectionReadOperation | CompositeConnectionReadOperation From 24728fedd50a8176c54f67009b2afc84dd91418e Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 22 Nov 2023 13:53:31 +0000 Subject: [PATCH 20/72] Add changeset --- .changeset/chilly-bugs-pump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilly-bugs-pump.md diff --git a/.changeset/chilly-bugs-pump.md b/.changeset/chilly-bugs-pump.md new file mode 100644 index 00000000000..e72b600425a --- /dev/null +++ b/.changeset/chilly-bugs-pump.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Update translation on fulltext to make it consistent for top level operations and phrase option From ed0d4182d3e30539e84b7341569e43c9fd2cba58 Mon Sep 17 00:00:00 2001 From: Alle <111279668+a-alle@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:57:16 +0000 Subject: [PATCH 21/72] Add console warn if experimental flag is true (#4298) * add console warn if experimental flag is true * transform to validator * Update packages/graphql/src/schema/validation/custom-rules/warnings/experimental-mode.ts * Apply suggestions from code review --------- Co-authored-by: Darrell Warde <8117355+darrellwarde@users.noreply.github.com> --- .../warnings/experimental-mode.ts | 39 +++++++ .../validation/validate-document.test.ts | 101 ++++++++++++++++-- .../schema/validation/validate-document.ts | 2 + 3 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 packages/graphql/src/schema/validation/custom-rules/warnings/experimental-mode.ts diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/experimental-mode.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/experimental-mode.ts new file mode 100644 index 00000000000..597804dfd1e --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/warnings/experimental-mode.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor } from "graphql"; + +export function WarnIfExperimentalMode(experimental: boolean) { + return function (): ASTVisitor { + let warningAlreadyIssued = false; + + return { + Document() { + if (experimental === true && !warningAlreadyIssued) { + console.warn( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); + + warningAlreadyIssued = true; + } + return false; + }, + }; + }; +} diff --git a/packages/graphql/src/schema/validation/validate-document.test.ts b/packages/graphql/src/schema/validation/validate-document.test.ts index e7f5fac34d9..d3822746ac5 100644 --- a/packages/graphql/src/schema/validation/validate-document.test.ts +++ b/packages/graphql/src/schema/validation/validate-document.test.ts @@ -37,6 +37,68 @@ const additionalDefinitions = { objects: [] as ObjectTypeDefinitionNode[], }; +describe("experimental flag warning", () => { + let warn: jest.SpyInstance; + + beforeEach(() => { + warn = jest.spyOn(console, "warn").mockImplementation(() => {}); + }); + + afterEach(() => { + warn.mockReset(); + }); + + test("experimental warning happens when flag is true", () => { + const doc = gql` + type Movie { + id: ID + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) + } + + type Actor { + name: String + } + `; + + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + experimental: true, + }); + + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); + expect(warn).toHaveBeenCalledOnce(); + }); + + test("experimental warning does not happen when flag is false", () => { + const doc = gql` + type Movie { + id: ID + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) + } + + type Actor { + name: String + } + `; + + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + experimental: true, + }); + + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); + expect(warn).toHaveBeenCalledOnce(); + }); +}); + describe("authorization warning", () => { let warn: jest.SpyInstance; @@ -158,7 +220,10 @@ describe("default max limit bypass warning", () => { experimental: true, }); - expect(warn).not.toHaveBeenCalled(); + expect(warn).toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); }); test("max limit on concrete should trigger warning if no limit on interface", () => { @@ -179,10 +244,13 @@ describe("default max limit bypass warning", () => { experimental: true, }); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); expect(warn).toHaveBeenCalledWith( "Max limit set on Movie may be bypassed by its interface Production. To fix this update the `@limit` max value on the interface type. Ignore this message if the behavior is intended!" ); - expect(warn).toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledTimes(2); }); test("max limit lower on interface than concrete does not trigger warning", () => { @@ -203,7 +271,10 @@ describe("default max limit bypass warning", () => { experimental: true, }); - expect(warn).not.toHaveBeenCalled(); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); + expect(warn).toHaveBeenCalledOnce(); }); test("Max limit higher on interface than concrete should trigger warning", () => { @@ -224,10 +295,13 @@ describe("default max limit bypass warning", () => { experimental: true, }); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); expect(warn).toHaveBeenCalledWith( "Max limit set on Movie may be bypassed by its interface Production. To fix this update the `@limit` max value on the interface type. Ignore this message if the behavior is intended!" ); - expect(warn).toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledTimes(2); }); test("Max limit higher on interface than concrete should not trigger warning if experimental: false", () => { @@ -248,7 +322,7 @@ describe("default max limit bypass warning", () => { experimental: false, }); - expect(warn).not.toHaveBeenCalledOnce(); + expect(warn).not.toHaveBeenCalled(); }); test("Max limit higher on interface than concrete should trigger warning - multiple implementing types", () => { @@ -273,10 +347,13 @@ describe("default max limit bypass warning", () => { experimental: true, }); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); expect(warn).toHaveBeenCalledWith( "Max limit set on Series may be bypassed by its interface Production. To fix this update the `@limit` max value on the interface type. Ignore this message if the behavior is intended!" ); - expect(warn).toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledTimes(2); }); test("Max limit higher on interface than concrete should trigger warning - on both implementing types", () => { @@ -301,13 +378,16 @@ describe("default max limit bypass warning", () => { experimental: true, }); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); expect(warn).toHaveBeenCalledWith( "Max limit set on Movie may be bypassed by its interface Production. To fix this update the `@limit` max value on the interface type. Ignore this message if the behavior is intended!" ); - expect(warn).toHaveBeenLastCalledWith( + expect(warn).toHaveBeenCalledWith( "Max limit set on Series may be bypassed by its interface Production. To fix this update the `@limit` max value on the interface type. Ignore this message if the behavior is intended!" ); - expect(warn).toHaveBeenCalledTimes(2); + expect(warn).toHaveBeenCalledTimes(3); }); test("Max limit on interface does not trigger warning if only default limit set on concrete", () => { @@ -332,7 +412,10 @@ describe("default max limit bypass warning", () => { experimental: true, }); - expect(warn).not.toHaveBeenCalledOnce(); + expect(warn).toHaveBeenCalledWith( + "You have enabled experimental features of the Neo4j GraphQL Library. Be aware that experimental features may be incomplete and/or contain bugs, and that breaking changes may be introduced at any time." + ); + expect(warn).toHaveBeenCalledOnce(); }); }); diff --git a/packages/graphql/src/schema/validation/validate-document.ts b/packages/graphql/src/schema/validation/validate-document.ts index 1019f64a16c..aefb7c1dbe6 100644 --- a/packages/graphql/src/schema/validation/validate-document.ts +++ b/packages/graphql/src/schema/validation/validate-document.ts @@ -64,6 +64,7 @@ import { ValidDirectiveAtFieldLocation } from "./custom-rules/directives/valid-d import { WarnIfAuthorizationFeatureDisabled } from "./custom-rules/warnings/authorization-feature-disabled"; import { WarnIfListOfListsFieldDefinition } from "./custom-rules/warnings/list-of-lists"; import { WarnIfAMaxLimitCanBeBypassedThroughInterface } from "./custom-rules/warnings/limit-max-can-be-bypassed"; +import { WarnIfExperimentalMode } from "./custom-rules/warnings/experimental-mode"; function filterDocument(document: DocumentNode): DocumentNode { const nodeNames = document.definitions @@ -211,6 +212,7 @@ function runValidationRulesOnFilteredDocument({ WarnIfAuthorizationFeatureDisabled(features?.authorization), WarnIfListOfListsFieldDefinition, WarnIfAMaxLimitCanBeBypassedThroughInterface(experimental), + WarnIfExperimentalMode(experimental), ], schema ); From 7b310d6d150c788e04af64f69029740913ddffad Mon Sep 17 00:00:00 2001 From: Michael Webb <28074382+mjfwebb@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:50:43 +0100 Subject: [PATCH 22/72] Feature: filter on interface aggregation (#4308) * feat: add filters to aggregation operations * test: add aggregation filter tests * feat: update composite aggregation to use edge and node --- .changeset/strong-dodos-live.md | 5 + .../queryAST/ast/operations/ReadOperation.ts | 2 +- .../CompositeAggregationOperation.ts | 115 ++++++----- .../composite/CompositeAggregationPartial.ts | 20 +- ...gation-interfaces-field-level.int.test.ts} | 6 +- ...gregation-interfaces-top-level.int.test.ts | 10 +- ...egation-interfaces-field-level.int.test.ts | 195 ++++++++++++++++++ ...gregation-interfaces-top-level.int.test.ts | 145 +++++++++++++ ...ggregation-interfaces-field-level.test.ts} | 170 +++++++-------- .../aggregation-interfaces-top-level.test.ts} | 38 ++-- ...aggregation-interfaces-field-level.test.ts | 192 +++++++++++++++++ ...r-aggregation-interfaces-top-level.test.ts | 131 ++++++++++++ 12 files changed, 860 insertions(+), 169 deletions(-) create mode 100644 .changeset/strong-dodos-live.md rename packages/graphql/tests/integration/experimental/{interface-field-level-aggregations.int.test.ts => aggegations/aggregation-interfaces-field-level.int.test.ts} (98%) rename packages/graphql/tests/integration/experimental/{ => aggegations}/aggregation-interfaces-top-level.int.test.ts (94%) create mode 100644 packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-field-level.int.test.ts create mode 100644 packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-top-level.int.test.ts rename packages/graphql/tests/tck/experimental/{interface-field-level-aggregations.test.ts => aggregations/aggregation-interfaces-field-level.test.ts} (70%) rename packages/graphql/tests/tck/experimental/{aggregation-top-level-interface.test.ts => aggregations/aggregation-interfaces-top-level.test.ts} (79%) create mode 100644 packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-field-level.test.ts create mode 100644 packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-top-level.test.ts diff --git a/.changeset/strong-dodos-live.md b/.changeset/strong-dodos-live.md new file mode 100644 index 00000000000..62bb7e6bfd4 --- /dev/null +++ b/.changeset/strong-dodos-live.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Add filtering to interface aggregations diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index de6e022db2c..7acb4c5be09 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -188,7 +188,7 @@ export class ReadOperation extends Operation { } protected getPredicates(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { - return Cypher.and(...[...this.filters].map((f) => f.getPredicate(queryASTContext))); + return Cypher.and(...this.filters.map((f) => f.getPredicate(queryASTContext))); } protected getSelectionClauses( diff --git a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts index 00ff8b9b921..46e53672b4c 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationOperation.ts @@ -35,9 +35,9 @@ import type { CompositeAggregationPartial } from "./CompositeAggregationPartial" export class CompositeAggregationOperation extends Operation { private children: CompositeAggregationPartial[]; protected directed: boolean; - public fields: AggregationField[] = []; // Aggregation fields - public nodeFields: AggregationField[] = []; // Aggregation node fields - public edgeFields: AggregationField[] = []; // Aggregation node fields + public fields: AggregationField[] = []; + public nodeFields: AggregationField[] = []; + public edgeFields: AggregationField[] = []; private entity: InterfaceEntityAdapter | UnionEntityAdapter; @@ -46,6 +46,10 @@ export class CompositeAggregationOperation extends Operation { protected filters: Filter[] = []; protected pagination: Pagination | undefined; protected sortFields: Sort[] = []; + private addWith: boolean = true; + private aggregationProjectionMap: Cypher.Map = new Cypher.Map(); + private nodeMap = new Cypher.Map(); + private edgeMap = new Cypher.Map(); public nodeAlias: string | undefined; // This is just to maintain naming with the old way (this), remove after refactor @@ -170,84 +174,91 @@ export class CompositeAggregationOperation extends Operation { return field.getAggregationProjection(target, returnVariable); } - private transpileAggregationOperation(context, addWith = true): OperationTranspileResult { - const aggregationProjectionMap: Cypher.Map = new Cypher.Map(); - const nodeMap = new Cypher.Map(); - const edgeMap = new Cypher.Map(); + private transpileAggregationOperation(context: QueryASTContext, addWith = true): OperationTranspileResult { + this.addWith = addWith; - const fieldSubqueries = this.fields.map((field) => { - const returnVariable = new Cypher.Variable(); - const nestedContext = context.setReturn(returnVariable); + const fieldSubqueries = this.createSubqueries(this.fields, context, this.aggregationProjectionMap); + const nodeFieldSubqueries = this.createSubqueries(this.nodeFields, context, this.nodeMap); + const edgeFieldSubqueries = this.createSubqueries( + this.edgeFields, + context, + this.edgeMap, + new Cypher.NamedNode("edge") + ); - const nestedSubquery = this.createSubquery(field, nestedContext, aggregationProjectionMap, addWith); + if (this.nodeMap.size > 0) { + this.aggregationProjectionMap.set("node", this.nodeMap); + } - const aggrProjection = field.getAggregationProjection( - nestedContext.returnVariable, - nestedContext.returnVariable - ); - return Cypher.concat(nestedSubquery, aggrProjection); - }); + if (this.edgeMap.size > 0) { + this.aggregationProjectionMap.set("edge", this.edgeMap); + } - const nodeFieldSubqueries = this.nodeFields.map((field) => { - const returnVariable = new Cypher.Variable(); + return { + clauses: [...fieldSubqueries, ...nodeFieldSubqueries, ...edgeFieldSubqueries], + projectionExpr: this.aggregationProjectionMap, + }; + } + private createSubqueries( + fields: AggregationField[], + context: QueryASTContext, + projectionMap: Cypher.Map, + target: Cypher.NamedNode = new Cypher.NamedNode("node") + ): Cypher.CompositeClause[] { + return fields.map((field) => { + const returnVariable = new Cypher.Node(); const nestedContext = context.setReturn(returnVariable); + const withClause: Cypher.With | undefined = this.createWithClause(context); - const nestedSubquery = this.createSubquery(field, nestedContext, nodeMap, addWith); - return Cypher.concat( - nestedSubquery, - field.getAggregationProjection(nestedContext.returnVariable, nestedContext.returnVariable) - ); - }); - - if (nodeMap.size > 0) { - aggregationProjectionMap.set("node", nodeMap); - } + const nestedSubquery = this.createNestedSubquery(nestedContext, target); - const edgeFieldSubqueries = this.edgeFields.map((field) => { - const returnVariable = new Cypher.Variable(); - const nestedContext = context.setReturn(returnVariable); + projectionMap.set(field.getProjectionField(nestedContext.returnVariable)); - const nestedSubquery = this.createSubquery(field, nestedContext, edgeMap, addWith, true); return Cypher.concat( nestedSubquery, - field.getAggregationProjection(nestedContext.returnVariable, nestedContext.returnVariable) + withClause, + field.getAggregationProjection(target, nestedContext.returnVariable) ); }); + } - if (edgeMap.size > 0) { - aggregationProjectionMap.set("edge", edgeMap); - } + private createWithClause(context: QueryASTContext): Cypher.With | undefined { + const node = new Cypher.NamedNode("node"); + const filterContext = new QueryASTContext({ + neo4jGraphQLContext: context.neo4jGraphQLContext, + target: node, + }); + const filterPredicates = this.getPredicates(filterContext); - return { - clauses: [...fieldSubqueries, ...nodeFieldSubqueries, ...edgeFieldSubqueries], - projectionExpr: aggregationProjectionMap, - }; + let withClause: Cypher.With | undefined; + if (filterPredicates) { + withClause = new Cypher.With("*"); + withClause.where(filterPredicates); + } + return withClause; } - private createSubquery( - field: AggregationField, - context: QueryASTContext, - addToMap: Cypher.Map, - addWith = true, - useRelationshipVariable = false - ) { + private createNestedSubquery(context: QueryASTContext, target: Cypher.NamedNode): Cypher.Call { const parentNode = context.target; const nestedSubqueries = this.children.flatMap((c) => { - c.setAttachedTo(useRelationshipVariable ? "relationship" : "node"); + if (target.name === "edge") { + c.setAttachedTo("relationship"); + } else { + c.setAttachedTo("node"); + } + const result = c.getSubqueries(context); let clauses = result; - if (parentNode && addWith) { + if (parentNode && this.addWith) { clauses = clauses.map((sq) => Cypher.concat(new Cypher.With(parentNode), sq)); } return clauses; }); - addToMap.set(field.getProjectionField(context.returnVariable)); - return new Cypher.Call(new Cypher.Union(...nestedSubqueries)); } } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts index 45bc8e2168d..c6d175dad25 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts @@ -72,14 +72,26 @@ export class CompositeAggregationPartial extends QueryASTNode { .related(relVar) .withDirection(relDirection) .to(targetNode); + + const matchClause = new Cypher.Match(pattern); + + const nestedSubqueries = wrapSubqueriesInCypherCalls(context, this.getChildren(), [target]); + + return [ + Cypher.concat( + matchClause, + ...nestedSubqueries, + new Cypher.Return([targetNode, "node"], [relVar, "edge"]) + ), + ]; } else { pattern = new Cypher.Pattern(targetNode); - } - const matchClause = new Cypher.Match(pattern); + const matchClause = new Cypher.Match(pattern); - const nestedSubqueries = wrapSubqueriesInCypherCalls(context, this.getChildren(), [target]); + const nestedSubqueries = wrapSubqueriesInCypherCalls(context, this.getChildren(), [target]); - return [Cypher.concat(matchClause, ...nestedSubqueries, new Cypher.Return([target, context.returnVariable]))]; + return [Cypher.concat(matchClause, ...nestedSubqueries, new Cypher.Return([targetNode, "node"]))]; + } } public print(): string { diff --git a/packages/graphql/tests/integration/experimental/interface-field-level-aggregations.int.test.ts b/packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-field-level.int.test.ts similarity index 98% rename from packages/graphql/tests/integration/experimental/interface-field-level-aggregations.int.test.ts rename to packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-field-level.int.test.ts index b219c71a067..c90a4cb82a5 100644 --- a/packages/graphql/tests/integration/experimental/interface-field-level-aggregations.int.test.ts +++ b/packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-field-level.int.test.ts @@ -19,9 +19,9 @@ import { graphql } from "graphql"; import type { Driver, Session } from "neo4j-driver"; -import { Neo4jGraphQL } from "../../../src/classes"; -import { UniqueType } from "../../utils/graphql-types"; -import Neo4j from "../neo4j"; +import { Neo4jGraphQL } from "../../../../src/classes"; +import { UniqueType } from "../../../utils/graphql-types"; +import Neo4j from "../../neo4j"; describe("Interface Field Level Aggregations", () => { let driver: Driver; diff --git a/packages/graphql/tests/integration/experimental/aggregation-interfaces-top-level.int.test.ts b/packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-top-level.int.test.ts similarity index 94% rename from packages/graphql/tests/integration/experimental/aggregation-interfaces-top-level.int.test.ts rename to packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-top-level.int.test.ts index 4a87cd41458..d16ecd7f3bc 100644 --- a/packages/graphql/tests/integration/experimental/aggregation-interfaces-top-level.int.test.ts +++ b/packages/graphql/tests/integration/experimental/aggegations/aggregation-interfaces-top-level.int.test.ts @@ -20,11 +20,11 @@ import type { GraphQLSchema } from "graphql"; import { graphql } from "graphql"; import type { Driver } from "neo4j-driver"; -import { Neo4jGraphQL } from "../../../src"; -import { cleanNodes } from "../../utils/clean-nodes"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { UniqueType } from "../../utils/graphql-types"; -import Neo4j from "../neo4j"; +import { Neo4jGraphQL } from "../../../../src"; +import { cleanNodes } from "../../../utils/clean-nodes"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { UniqueType } from "../../../utils/graphql-types"; +import Neo4j from "../../neo4j"; describe("Top-level interface query fields", () => { const secret = "the-secret"; diff --git a/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-field-level.int.test.ts b/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-field-level.int.test.ts new file mode 100644 index 00000000000..9fd2176c30c --- /dev/null +++ b/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-field-level.int.test.ts @@ -0,0 +1,195 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLSchema } from "graphql"; +import { graphql } from "graphql"; +import type { Driver } from "neo4j-driver"; +import { Neo4jGraphQL } from "../../../../src"; +import { cleanNodes } from "../../../utils/clean-nodes"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { UniqueType } from "../../../utils/graphql-types"; +import Neo4j from "../../neo4j"; + +describe("Field-level filter interface query fields", () => { + const secret = "the-secret"; + + let schema: GraphQLSchema; + let neo4j: Neo4j; + let driver: Driver; + let typeDefs: string; + + const Production = new UniqueType("Production"); + const Movie = new UniqueType("Movie"); + const Actor = new UniqueType("Actor"); + const Series = new UniqueType("Series"); + + async function graphqlQuery(query: string, token: string) { + return graphql({ + schema, + source: query, + contextValue: neo4j.getContextValues({ token }), + }); + } + + beforeAll(async () => { + neo4j = new Neo4j(); + driver = await neo4j.getDriver(); + + typeDefs = /* GraphQL */ ` + interface ${Production} { + title: String! + cost: Float! + } + + type ${Movie} implements ${Production} { + title: String! + cost: Float! + runtime: Int! + ${Actor.plural}: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${Series} implements ${Production} { + title: String! + cost: Float! + episodes: Int! + } + + interface ActedIn @relationshipProperties { + screenTime: Int! + } + + type ${Actor} { + name: String! + actedIn: [${Production}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + `; + + const session = await neo4j.getSession(); + + try { + await session.run(` + // Create Movies + CREATE (m1:${Movie} { title: "The Movie One", cost: 10000000, runtime: 120 }) + CREATE (m2:${Movie} { title: "The Movie Two", cost: 20000000, runtime: 90 }) + CREATE (m3:${Movie} { title: "The Movie Three", cost: 12000000, runtime: 70 }) + + // Create Series + CREATE (s1:${Series} { title: "The Series One", cost: 10000000, episodes: 10 }) + CREATE (s2:${Series} { title: "The Series Two", cost: 20000000, episodes: 20 }) + CREATE (s3:${Series} { title: "The Series Three", cost: 20000000, episodes: 15 }) + + // Create Actors + CREATE (a1:${Actor} { name: "Actor One" }) + CREATE (a2:${Actor} { name: "Actor Two" }) + + // Associate Actor 1 with Movies and Series + CREATE (a1)-[:ACTED_IN { screenTime: 100 }]->(m1) + CREATE (a1)-[:ACTED_IN { screenTime: 82 }]->(s1) + CREATE (a1)-[:ACTED_IN { screenTime: 20 }]->(m3) + CREATE (a1)-[:ACTED_IN { screenTime: 22 }]->(s3) + + // Associate Actor 2 with Movies and Series + CREATE (a2)-[:ACTED_IN { screenTime: 240 }]->(m2) + CREATE (a2)-[:ACTED_IN { screenTime: 728 }]->(s2) + CREATE (a2)-[:ACTED_IN { screenTime: 728 }]->(m3) + CREATE (a2)-[:ACTED_IN { screenTime: 88 }]->(s3) + `); + } finally { + await session.close(); + } + + const neoGraphql = new Neo4jGraphQL({ + typeDefs, + driver, + experimental: true, + }); + schema = await neoGraphql.getSchema(); + }); + + afterAll(async () => { + const session = await neo4j.getSession(); + await cleanNodes(session, [Movie, Series]); + await session.close(); + await driver.close(); + }); + + test("complex query on nested aggregation", async () => { + const query = /* GraphQL */ ` + query { + ${Actor.plural} { + actedInAggregate(where: { title_STARTS_WITH: "The" }) { + edge { + screenTime { + min + max + } + } + node { + title { + longest + shortest + } + } + } + name, + } + } + `; + + const token = createBearerToken(secret, {}); + const queryResult = await graphqlQuery(query, token); + expect(queryResult.errors).toBeUndefined(); + expect((queryResult as any).data[Actor.plural]).toIncludeSameMembers([ + { + actedInAggregate: { + edge: { + screenTime: { + max: 100, + min: 20, + }, + }, + node: { + title: { + longest: "The Series Three", + shortest: "The Movie One", + }, + }, + }, + name: "Actor One", + }, + { + actedInAggregate: { + edge: { + screenTime: { + max: 728, + min: 88, + }, + }, + node: { + title: { + longest: "The Series Three", + shortest: "The Movie Two", + }, + }, + }, + name: "Actor Two", + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-top-level.int.test.ts b/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-top-level.int.test.ts new file mode 100644 index 00000000000..f284917224b --- /dev/null +++ b/packages/graphql/tests/integration/experimental/aggegations/filter-aggregation-interfaces-top-level.int.test.ts @@ -0,0 +1,145 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLSchema } from "graphql"; +import { graphql } from "graphql"; +import type { Driver } from "neo4j-driver"; +import { Neo4jGraphQL } from "../../../../src"; +import { cleanNodes } from "../../../utils/clean-nodes"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { UniqueType } from "../../../utils/graphql-types"; +import Neo4j from "../../neo4j"; + +describe("Top-level filter interface query fields", () => { + const secret = "the-secret"; + + let schema: GraphQLSchema; + let neo4j: Neo4j; + let driver: Driver; + let typeDefs: string; + + const Movie = new UniqueType("Movie"); + const Series = new UniqueType("Series"); + + async function graphqlQuery(query: string, token: string) { + return graphql({ + schema, + source: query, + contextValue: neo4j.getContextValues({ token }), + }); + } + + beforeAll(async () => { + neo4j = new Neo4j(); + driver = await neo4j.getDriver(); + + typeDefs = ` + interface Production { + title: String! + cost: Float! + } + + type ${Movie} implements Production { + title: String! + cost: Float! + runtime: Int + } + + type ${Series} implements Production { + title: String! + cost: Float! + episodes: Int + } + `; + + const session = await neo4j.getSession(); + + try { + await session.run(` + CREATE(m1:${Movie} {title: "A Movie", cost: 10}) + CREATE(m2:${Movie} {title: "The Matrix is a very interesting movie: The Documentary", cost: 20}) + + CREATE(s1:${Series} {title: "The Show", cost: 1}) + CREATE(s2:${Series} {title: "A Series 2", cost: 2}) + `); + } finally { + await session.close(); + } + + const neoGraphql = new Neo4jGraphQL({ + typeDefs, + driver, + experimental: true, + }); + schema = await neoGraphql.getSchema(); + }); + + afterAll(async () => { + const session = await neo4j.getSession(); + await cleanNodes(session, [Movie, Series]); + await session.close(); + await driver.close(); + }); + + test("top level count", async () => { + const query = ` + query { + productionsAggregate(where: { title: "The Show" }) { + count + } + } + `; + + const token = createBearerToken(secret, {}); + const queryResult = await graphqlQuery(query, token); + expect(queryResult.errors).toBeUndefined(); + expect(queryResult.data).toEqual({ + productionsAggregate: { + count: 1, + }, + }); + }); + + test("top level count and string fields", async () => { + const query = ` + query { + productionsAggregate(where: { title_STARTS_WITH: "The" }) { + count + title { + longest + shortest + } + } + } + `; + + const token = createBearerToken(secret, {}); + const queryResult = await graphqlQuery(query, token); + expect(queryResult.errors).toBeUndefined(); + expect(queryResult.data).toEqual({ + productionsAggregate: { + count: 2, + title: { + longest: "The Matrix is a very interesting movie: The Documentary", + shortest: "The Show", + }, + }, + }); + }); +}); diff --git a/packages/graphql/tests/tck/experimental/interface-field-level-aggregations.test.ts b/packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-field-level.test.ts similarity index 70% rename from packages/graphql/tests/tck/experimental/interface-field-level-aggregations.test.ts rename to packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-field-level.test.ts index 94a8fabd5da..51524478a77 100644 --- a/packages/graphql/tests/tck/experimental/interface-field-level-aggregations.test.ts +++ b/packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-field-level.test.ts @@ -19,8 +19,8 @@ import type { DocumentNode } from "graphql"; import { gql } from "graphql-tag"; -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; describe("Interface Field Level Aggregations", () => { let typeDefs: DocumentNode; @@ -82,15 +82,15 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN count(var2) AS var2 + RETURN count(node) AS this4 } - RETURN this { actedInAggregate: { count: var2 } } AS this" + RETURN this { actedInAggregate: { count: this4 } } AS this" `); }); @@ -116,15 +116,15 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN count(var2) AS var2 + RETURN count(node) AS this4 } - RETURN this { actedInAggregate: { count: var2 } } AS this" + RETURN this { actedInAggregate: { count: this4 } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -158,15 +158,15 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN { min: min(var2.cost) } AS var2 + RETURN { min: min(node.cost) } AS this4 } - RETURN this { actedInAggregate: { node: { cost: var2 } } } AS this" + RETURN this { actedInAggregate: { node: { cost: this4 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -196,15 +196,15 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN { max: max(var2.cost) } AS var2 + RETURN { max: max(node.cost) } AS this4 } - RETURN this { actedInAggregate: { node: { cost: var2 } } } AS this" + RETURN this { actedInAggregate: { node: { cost: this4 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -229,35 +229,35 @@ describe("Interface Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - CALL { - WITH this - CALL { - WITH this - MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 - UNION - WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 - } - RETURN count(var2) AS var2 - } - CALL { - WITH this - CALL { - WITH this - MATCH (this)-[this5:ACTED_IN]->(this6:Movie) - RETURN this6 AS var7 - UNION - WITH this - MATCH (this)-[this8:ACTED_IN]->(this9:Series) - RETURN this9 AS var7 - } - RETURN { max: max(var7.cost) } AS var7 - } - RETURN this { actedInAggregate: { count: var2, node: { cost: var7 } } } AS this" - `); + "MATCH (this:Actor) + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + RETURN this1 AS node, this0 AS edge + UNION + WITH this + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge + } + RETURN count(node) AS this4 + } + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this5:ACTED_IN]->(this6:Movie) + RETURN this6 AS node, this5 AS edge + UNION + WITH this + MATCH (this)-[this7:ACTED_IN]->(this8:Series) + RETURN this8 AS node, this7 AS edge + } + RETURN { max: max(node.cost) } AS this9 + } + RETURN this { actedInAggregate: { count: this4, node: { cost: this9 } } } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); }); @@ -286,18 +286,18 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - WITH var2 - ORDER BY size(var2.title) DESC - WITH collect(var2.title) AS list - RETURN { longest: head(list) } AS var2 + WITH node + ORDER BY size(node.title) DESC + WITH collect(node.title) AS list + RETURN { longest: head(list) } AS this4 } - RETURN this { actedInAggregate: { node: { title: var2 } } } AS this" + RETURN this { actedInAggregate: { node: { title: this4 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -333,18 +333,18 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this1 MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) - RETURN this3 AS var4 + RETURN this3 AS node, this2 AS edge UNION WITH this1 - MATCH (this1)-[this5:ACTED_IN]->(this6:Series) - RETURN this6 AS var4 + MATCH (this1)-[this4:ACTED_IN]->(this5:Series) + RETURN this5 AS node, this4 AS edge } - WITH var4 - ORDER BY size(var4.title) DESC - WITH collect(var4.title) AS list - RETURN { longest: head(list) } AS var4 + WITH node + ORDER BY size(node.title) DESC + WITH collect(node.title) AS list + RETURN { longest: head(list) } AS this6 } - WITH this1 { actedInAggregate: { node: { title: var4 } } } AS this1 + WITH this1 { actedInAggregate: { node: { title: this6 } } } AS this1 RETURN collect(this1) AS var7 } RETURN this { actors: var7 } AS this" @@ -377,15 +377,15 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this0 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this3 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN { sum: sum(var2.screenTime) } AS var2 + RETURN { sum: sum(edge.screenTime) } AS this4 } - RETURN this { actedInAggregate: { edge: { screenTime: var2 } } } AS this" + RETURN this { actedInAggregate: { edge: { screenTime: this4 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -421,41 +421,41 @@ describe("Interface Field Level Aggregations", () => { CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) - RETURN this1 AS var2 + RETURN this1 AS node, this0 AS edge UNION WITH this - MATCH (this)-[this3:ACTED_IN]->(this4:Series) - RETURN this4 AS var2 + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge } - RETURN count(var2) AS var2 + RETURN count(node) AS this4 } CALL { WITH this CALL { WITH this MATCH (this)-[this5:ACTED_IN]->(this6:Movie) - RETURN this6 AS var7 + RETURN this6 AS node, this5 AS edge UNION WITH this - MATCH (this)-[this8:ACTED_IN]->(this9:Series) - RETURN this9 AS var7 + MATCH (this)-[this7:ACTED_IN]->(this8:Series) + RETURN this8 AS node, this7 AS edge } - RETURN { sum: sum(var7.cost) } AS var7 + RETURN { sum: sum(node.cost) } AS this9 } CALL { WITH this CALL { WITH this MATCH (this)-[this10:ACTED_IN]->(this11:Movie) - RETURN this10 AS var12 + RETURN this11 AS node, this10 AS edge UNION WITH this - MATCH (this)-[this13:ACTED_IN]->(this14:Series) - RETURN this13 AS var12 + MATCH (this)-[this12:ACTED_IN]->(this13:Series) + RETURN this13 AS node, this12 AS edge } - RETURN { sum: sum(var12.screenTime) } AS var12 + RETURN { sum: sum(edge.screenTime) } AS this14 } - RETURN this { actedInAggregate: { count: var2, node: { cost: var7 }, edge: { screenTime: var12 } } } AS this" + RETURN this { actedInAggregate: { count: this4, node: { cost: this9 }, edge: { screenTime: this14 } } } AS this" `); }); }); diff --git a/packages/graphql/tests/tck/experimental/aggregation-top-level-interface.test.ts b/packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-top-level.test.ts similarity index 79% rename from packages/graphql/tests/tck/experimental/aggregation-top-level-interface.test.ts rename to packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-top-level.test.ts index 37f50797106..c6e4cba3d49 100644 --- a/packages/graphql/tests/tck/experimental/aggregation-top-level-interface.test.ts +++ b/packages/graphql/tests/tck/experimental/aggregations/aggregation-interfaces-top-level.test.ts @@ -19,8 +19,8 @@ import type { DocumentNode } from "graphql"; import { gql } from "graphql-tag"; -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; describe("Top level aggregation interfaces", () => { let typeDefs: DocumentNode; @@ -76,14 +76,14 @@ describe("Top level aggregation interfaces", () => { "CALL { CALL { MATCH (this0:Movie) - RETURN this0 AS var1 + RETURN this0 AS node UNION - MATCH (this2:Series) - RETURN this2 AS var1 + MATCH (this1:Series) + RETURN this1 AS node } - RETURN count(var1) AS var1 + RETURN count(node) AS this2 } - RETURN { count: var1 }" + RETURN { count: this2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -108,27 +108,27 @@ describe("Top level aggregation interfaces", () => { "CALL { CALL { MATCH (this0:Movie) - RETURN this0 AS var1 + RETURN this0 AS node UNION - MATCH (this2:Series) - RETURN this2 AS var1 + MATCH (this1:Series) + RETURN this1 AS node } - RETURN count(var1) AS var1 + RETURN count(node) AS this2 } CALL { CALL { MATCH (this3:Movie) - RETURN this3 AS var4 + RETURN this3 AS node UNION - MATCH (this5:Series) - RETURN this5 AS var4 + MATCH (this4:Series) + RETURN this4 AS node } - WITH var4 - ORDER BY size(var4.title) DESC - WITH collect(var4.title) AS list - RETURN { longest: head(list), shortest: last(list) } AS var4 + WITH node + ORDER BY size(node.title) DESC + WITH collect(node.title) AS list + RETURN { longest: head(list), shortest: last(list) } AS this5 } - RETURN { count: var1, title: var4 }" + RETURN { count: this2, title: this5 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-field-level.test.ts b/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-field-level.test.ts new file mode 100644 index 00000000000..a541177ae35 --- /dev/null +++ b/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-field-level.test.ts @@ -0,0 +1,192 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Interface Field Level Aggregations", () => { + let typeDefs: DocumentNode; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = gql` + interface Production { + title: String! + cost: Float! + } + + type Movie implements Production { + title: String! + cost: Float! + runtime: Int! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type Series implements Production { + title: String! + cost: Float! + episodes: Int! + } + + interface ActedIn @relationshipProperties { + screenTime: Int! + } + + type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + experimental: true, + }); + }); + + test("Count with where", async () => { + const query = gql` + { + actors { + actedInAggregate(where: { title: "The Matrix" }) { + count + } + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "MATCH (this:Actor) + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + RETURN this1 AS node, this0 AS edge + UNION + WITH this + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge + } + WITH * + WHERE node.title = $param0 + RETURN count(node) AS this4 + } + RETURN this { actedInAggregate: { count: this4 } } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\" + }" + `); + }); + + test("Count with where and string aggregation", async () => { + const query = gql` + { + actors { + actedInAggregate(where: { title_STARTS_WITH: "The" }) { + count + edge { + screenTime { + min + max + } + } + node { + title { + longest + shortest + } + } + } + name + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "MATCH (this:Actor) + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + RETURN this1 AS node, this0 AS edge + UNION + WITH this + MATCH (this)-[this2:ACTED_IN]->(this3:Series) + RETURN this3 AS node, this2 AS edge + } + WITH * + WHERE node.title STARTS WITH $param0 + RETURN count(node) AS this4 + } + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this5:ACTED_IN]->(this6:Movie) + RETURN this6 AS node, this5 AS edge + UNION + WITH this + MATCH (this)-[this7:ACTED_IN]->(this8:Series) + RETURN this8 AS node, this7 AS edge + } + WITH * + WHERE node.title STARTS WITH $param1 + WITH node + ORDER BY size(node.title) DESC + WITH collect(node.title) AS list + RETURN { longest: head(list), shortest: last(list) } AS this9 + } + CALL { + WITH this + CALL { + WITH this + MATCH (this)-[this10:ACTED_IN]->(this11:Movie) + RETURN this11 AS node, this10 AS edge + UNION + WITH this + MATCH (this)-[this12:ACTED_IN]->(this13:Series) + RETURN this13 AS node, this12 AS edge + } + WITH * + WHERE node.title STARTS WITH $param2 + RETURN { min: min(edge.screenTime), max: max(edge.screenTime) } AS this14 + } + RETURN this { .name, actedInAggregate: { count: this4, node: { title: this9 }, edge: { screenTime: this14 } } } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The\\", + \\"param1\\": \\"The\\", + \\"param2\\": \\"The\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-top-level.test.ts b/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-top-level.test.ts new file mode 100644 index 00000000000..94f7b43f175 --- /dev/null +++ b/packages/graphql/tests/tck/experimental/aggregations/filter-aggregation-interfaces-top-level.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Top level filter on aggregation interfaces", () => { + let typeDefs: DocumentNode; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = gql` + interface Production { + title: String! + cost: Float! + } + + type Movie implements Production { + title: String! + cost: Float! + runtime: Int! + } + + type Series implements Production { + title: String! + cost: Float! + episodes: Int! + } + + interface ActedIn @relationshipProperties { + screenTime: Int! + } + + type Actor { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + experimental: true, + }); + }); + + test("top level count", async () => { + const query = gql` + { + productionsAggregate(where: { title: "The Matrix" }) { + count + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL { + CALL { + MATCH (this0:Movie) + RETURN this0 AS node + UNION + MATCH (this1:Series) + RETURN this1 AS node + } + WITH * + WHERE node.title = $param0 + RETURN count(node) AS this2 + } + RETURN { count: this2 }" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\" + }" + `); + }); + + test("top level count and string fields", async () => { + const query = gql` + { + productionsAggregate(where: { title: "The Matrix" }) { + count + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CALL { + CALL { + MATCH (this0:Movie) + RETURN this0 AS node + UNION + MATCH (this1:Series) + RETURN this1 AS node + } + WITH * + WHERE node.title = $param0 + RETURN count(node) AS this2 + } + RETURN { count: this2 }" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\" + }" + `); + }); +}); From afce28ca7eecb2d84b4ad8263a59c7b217bfbf85 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Wed, 22 Nov 2023 16:40:36 +0000 Subject: [PATCH 23/72] Minor cleanup on createFulltextOperation --- .../queryAST/factory/OperationFactory.ts | 134 ++++++++---------- 1 file changed, 61 insertions(+), 73 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index bf12fd08524..2855073b4a4 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -142,101 +142,89 @@ export class OperationsFactory { } public createFulltextOperation( - entityOrRel: EntityAdapter | RelationshipAdapter, + entity: ConcreteEntityAdapter, resolveTree: ResolveTree, context: Neo4jGraphQLTranslationContext ): FulltextOperation { - const entity = entityOrRel instanceof RelationshipAdapter ? entityOrRel.target : entityOrRel; - const relationship = entityOrRel instanceof RelationshipAdapter ? entityOrRel : undefined; - let resolveTreeWhere: Record = isObject(resolveTree.args.where) ? resolveTree.args.where : {}; let sortOptions: Record = (resolveTree.args.options as Record) || {}; let fieldsByTypeName = resolveTree.fieldsByTypeName; let resolverArgs = resolveTree.args; - if (isConcreteEntity(entity)) { - const fulltextOptions = this.getFulltextOptions(context); - let scoreField: FulltextScoreField | undefined; - let scoreFilter: FulltextScoreFilter | undefined; + const fulltextOptions = this.getFulltextOptions(context); + let scoreField: FulltextScoreField | undefined; + let scoreFilter: FulltextScoreFilter | undefined; - // Compatibility of top level operations - const fulltextOperationDeprecatedFields = - resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.result]; + // Compatibility of top level operations + const fulltextOperationDeprecatedFields = + resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.result]; - if (fulltextOperationDeprecatedFields) { - const scoreWhere = resolveTreeWhere.score; - resolveTreeWhere = resolveTreeWhere[entity.singular] || {}; + if (fulltextOperationDeprecatedFields) { + const scoreWhere = resolveTreeWhere.score; + resolveTreeWhere = resolveTreeWhere[entity.singular] || {}; - const scoreRawField = fulltextOperationDeprecatedFields.score; + const scoreRawField = fulltextOperationDeprecatedFields.score; - const nestedResolveTree: Record = fulltextOperationDeprecatedFields[entity.singular] || {}; - resolverArgs = { ...(nestedResolveTree?.args || {}), ...resolveTree.args }; + const nestedResolveTree: Record = fulltextOperationDeprecatedFields[entity.singular] || {}; + resolverArgs = { ...(nestedResolveTree?.args || {}), ...resolveTree.args }; - sortOptions = { - limit: sortOptions.limit, - offset: sortOptions.offset, - sort: filterTruthy((sortOptions.sort || []).map((field) => field[entity.singular] || field)), - }; - fieldsByTypeName = nestedResolveTree.fieldsByTypeName || {}; - if (scoreRawField) { - scoreField = this.createFulltextScoreField(scoreRawField, fulltextOptions.score); - } - if (scoreWhere) { - scoreFilter = new FulltextScoreFilter({ - scoreVariable: fulltextOptions.score, - min: scoreWhere.min, - max: scoreWhere.max, - }); - } + sortOptions = { + limit: sortOptions.limit, + offset: sortOptions.offset, + sort: filterTruthy((sortOptions.sort || []).map((field) => field[entity.singular] || field)), + }; + fieldsByTypeName = nestedResolveTree.fieldsByTypeName || {}; + if (scoreRawField) { + scoreField = this.createFulltextScoreField(scoreRawField, fulltextOptions.score); + } + if (scoreWhere) { + scoreFilter = new FulltextScoreFilter({ + scoreVariable: fulltextOptions.score, + min: scoreWhere.min, + max: scoreWhere.max, + }); } + } - checkEntityAuthentication({ - entity: entity.entity, - targetOperations: ["READ"], - context, - }); + checkEntityAuthentication({ + entity: entity.entity, + targetOperations: ["READ"], + context, + }); - const operation = new FulltextOperation({ - target: entity, - relationship, - directed: Boolean(resolverArgs.directed ?? true), - fulltext: fulltextOptions, - scoreField, - scoreVariable: fulltextOptions.score, - }); + const operation = new FulltextOperation({ + target: entity, + directed: Boolean(resolverArgs.directed ?? true), + fulltext: fulltextOptions, + scoreField, + scoreVariable: fulltextOptions.score, + }); - if (scoreFilter) { - operation.addFilters(scoreFilter); - } + if (scoreFilter) { + operation.addFilters(scoreFilter); + } - this.hydrateOperation({ - operation, - entity, - fieldsByTypeName: fieldsByTypeName, - context, - whereArgs: resolveTreeWhere, - }); + this.hydrateOperation({ + operation, + entity, + fieldsByTypeName: fieldsByTypeName, + context, + whereArgs: resolveTreeWhere, + }); - // Override sort to support score - const sortOptions2 = this.getOptions(entity, sortOptions); + // Override sort to support score + const sortOptions2 = this.getOptions(entity, sortOptions); - if (sortOptions2) { - const sort = this.sortAndPaginationFactory.createSortFields( - sortOptions2, - entity, - fulltextOptions.score - ); - operation.addSort(...sort); + if (sortOptions2) { + const sort = this.sortAndPaginationFactory.createSortFields(sortOptions2, entity, fulltextOptions.score); + operation.addSort(...sort); - const pagination = this.sortAndPaginationFactory.createPagination(sortOptions2); - if (pagination) { - operation.addPagination(pagination); - } + const pagination = this.sortAndPaginationFactory.createPagination(sortOptions2); + if (pagination) { + operation.addPagination(pagination); } - - return operation; - } else { - throw new Error("Fulltext nor supported on interfaces"); } + + return operation; } public createReadOperation( From bd91318502fa45e876f5eafcb2f22bc9e5140aa1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 22:35:37 +0000 Subject: [PATCH 24/72] chore(deps): update dependency @types/jest to v29.5.10 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 438a49de91c..918a4ab8c92 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -39,7 +39,7 @@ "@types/body-parser": "1.19.5", "@types/cors": "2.8.17", "@types/debug": "4.1.12", - "@types/jest": "29.5.9", + "@types/jest": "29.5.10", "@types/node": "20.9.3", "camelcase": "6.3.0", "graphql-ws": "5.14.2", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 5f2ca6ecd25..a3b23337d2a 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -54,7 +54,7 @@ "@faker-js/faker": "8.3.1", "@types/deep-equal": "1.0.4", "@types/is-uuid": "1.0.2", - "@types/jest": "29.5.9", + "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", "@types/node": "20.9.3", "@types/pluralize": "0.0.33", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index fec6ee0923c..344b1187865 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -37,7 +37,7 @@ "author": "Neo4j Inc.", "devDependencies": { "@neo4j/graphql": "^4.0.0", - "@types/jest": "29.5.9", + "@types/jest": "29.5.10", "@types/node": "20.9.3", "@types/pluralize": "0.0.33", "jest": "29.7.0", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 7b2138e8650..554429b22de 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -46,7 +46,7 @@ "neo4j-driver": "^5.8.0" }, "devDependencies": { - "@types/jest": "29.5.9", + "@types/jest": "29.5.10", "@types/node": "20.9.3", "camelcase": "6.3.0", "graphql-tag": "2.12.6", diff --git a/yarn.lock b/yarn.lock index 04266db6b76..09635081192 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3533,7 +3533,7 @@ __metadata: "@types/body-parser": 1.19.5 "@types/cors": 2.8.17 "@types/debug": 4.1.12 - "@types/jest": 29.5.9 + "@types/jest": 29.5.10 "@types/node": 20.9.3 amqplib: 0.10.3 body-parser: ^1.20.2 @@ -3562,7 +3562,7 @@ __metadata: "@graphql-codegen/typescript": ^4.0.0 "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 - "@types/jest": 29.5.9 + "@types/jest": 29.5.10 "@types/node": 20.9.3 camelcase: 6.3.0 graphql-tag: 2.12.6 @@ -3668,7 +3668,7 @@ __metadata: "@neo4j/cypher-builder": ^1.7.1 "@types/deep-equal": 1.0.4 "@types/is-uuid": 1.0.2 - "@types/jest": 29.5.9 + "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 "@types/node": 20.9.3 "@types/pluralize": 0.0.33 @@ -3727,7 +3727,7 @@ __metadata: resolution: "@neo4j/introspector@workspace:packages/introspector" dependencies: "@neo4j/graphql": ^4.0.0 - "@types/jest": 29.5.9 + "@types/jest": 29.5.10 "@types/node": 20.9.3 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 @@ -7474,13 +7474,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:29.5.9": - version: 29.5.9 - resolution: "@types/jest@npm:29.5.9" +"@types/jest@npm:29.5.10": + version: 29.5.10 + resolution: "@types/jest@npm:29.5.10" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 02245cff5f5b5ef46cc8c28acc516674aa167f2398bc4042752db7c763555928c5d62d56b3510eb8a2e5e25eea921f34866ef1c5515edfe16699b4a1e99ed4e1 + checksum: ef385905787db528de9b6beb2688865c0bb276e64256ed60b9a1a6ffc0b75737456cb5e27e952a3241c5845b6a1da487470010dd30f3ca59c8581624c564a823 languageName: node linkType: hard From 56b9b20f73ecbcff60c4e1f33cdf2eb63f436714 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 22:44:28 +0000 Subject: [PATCH 25/72] chore(deps): update dependency @types/node to v20.9.4 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 918a4ab8c92..24fd066e42e 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.9.3", + "@types/node": "20.9.4", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index a3b23337d2a..b061dbbb8d1 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.9.3", + "@types/node": "20.9.4", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 344b1187865..0bd03c12e9e 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.9.3", + "@types/node": "20.9.4", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 554429b22de..911ae191b36 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.9.3", + "@types/node": "20.9.4", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index 09635081192..a7a8a31b3a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3534,7 +3534,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.9.3 + "@types/node": 20.9.4 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3563,7 +3563,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.9.3 + "@types/node": 20.9.4 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3670,7 +3670,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.9.3 + "@types/node": 20.9.4 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3728,7 +3728,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.9.3 + "@types/node": 20.9.4 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7635,12 +7635,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.9.3": - version: 20.9.3 - resolution: "@types/node@npm:20.9.3" +"@types/node@npm:20.9.4": + version: 20.9.4 + resolution: "@types/node@npm:20.9.4" dependencies: undici-types: ~5.26.4 - checksum: 0cfbfd2a8bd18acc75aa4d7685c7dcf56344f48addd4041d306dc194f3132f8014d56fd49fcb26bcdf400b883f9527e5e2beaf52dfce029cef15c69b8ed2e72a + checksum: 619144cfee8235f692009e4268a8b80ef4ec496670273ab1cef04e4a053b471f391af6701e65f2f91a107256933d902b6caec079d551b109e981b0b706624815 languageName: node linkType: hard From 23ddb8458493ce3e8a633378121d75d61ef162c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:05:54 +0000 Subject: [PATCH 26/72] fix(deps): update dependency @graphql-tools/schema to v10.0.2 --- packages/graphql/package.json | 2 +- yarn.lock | 63 +++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index b061dbbb8d1..19e9adaff1d 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -89,7 +89,7 @@ "@apollo/subgraph": "^2.2.3", "@graphql-tools/merge": "^9.0.0", "@graphql-tools/resolvers-composition": "^7.0.0", - "@graphql-tools/schema": "10.0.0", + "@graphql-tools/schema": "10.0.2", "@graphql-tools/utils": "^10.0.0", "@neo4j/cypher-builder": "^1.7.1", "camelcase": "^6.3.0", diff --git a/yarn.lock b/yarn.lock index a7a8a31b3a7..4e8d2f37650 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2576,6 +2576,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:^9.0.1": + version: 9.0.1 + resolution: "@graphql-tools/merge@npm:9.0.1" + dependencies: + "@graphql-tools/utils": ^10.0.10 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: f078628838f57dcd2988b46ec27ce4786daef6e7fdd07c012acec2fe52139f4a905a101883eb0fa7094d1ace6d1b10e6a8d40c03778496b50e85093b36316e4e + languageName: node + linkType: hard + "@graphql-tools/optimize@npm:^2.0.0": version: 2.0.0 resolution: "@graphql-tools/optimize@npm:2.0.0" @@ -2614,17 +2626,17 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/schema@npm:10.0.0, @graphql-tools/schema@npm:^10.0.0": - version: 10.0.0 - resolution: "@graphql-tools/schema@npm:10.0.0" +"@graphql-tools/schema@npm:10.0.2": + version: 10.0.2 + resolution: "@graphql-tools/schema@npm:10.0.2" dependencies: - "@graphql-tools/merge": ^9.0.0 - "@graphql-tools/utils": ^10.0.0 + "@graphql-tools/merge": ^9.0.1 + "@graphql-tools/utils": ^10.0.10 tslib: ^2.4.0 value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 550e9a4528584a4d108892f1553fb5b2590e63e88b9a9d3c1ad80b01c974ca9947adb9d1448a6969230d90c15dc96e8e84d62f32ef0fde804c389b43ac5bd739 + checksum: fe977b1aee05b0a88cf6bb029f17d828d8707f784e1d42d446984b6ba649d78e16e3295c549ee352c09bbe88ad87c23bbe04b946c096b6815156c5be80d79a3f languageName: node linkType: hard @@ -2642,6 +2654,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/schema@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/schema@npm:10.0.0" + dependencies: + "@graphql-tools/merge": ^9.0.0 + "@graphql-tools/utils": ^10.0.0 + tslib: ^2.4.0 + value-or-promise: ^1.0.12 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 550e9a4528584a4d108892f1553fb5b2590e63e88b9a9d3c1ad80b01c974ca9947adb9d1448a6969230d90c15dc96e8e84d62f32ef0fde804c389b43ac5bd739 + languageName: node + linkType: hard + "@graphql-tools/schema@npm:^9.0.0, @graphql-tools/schema@npm:^9.0.18": version: 9.0.19 resolution: "@graphql-tools/schema@npm:9.0.19" @@ -2680,6 +2706,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/utils@npm:^10.0.10": + version: 10.0.10 + resolution: "@graphql-tools/utils@npm:10.0.10" + dependencies: + "@graphql-typed-document-node/core": ^3.1.1 + cross-inspect: 1.0.0 + dset: ^3.1.2 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 2b23dcf131901eec196b6b2d2a6c57c66b4b2c21eb1a1e22b9bd6e6cb53b9cf9ca630cca633398ae4a6e4aed781ed6fdaf2d93e990836c15ffec44ac1de50aa1 + languageName: node + linkType: hard + "@graphql-tools/utils@npm:^9.2.1": version: 9.2.1 resolution: "@graphql-tools/utils@npm:9.2.1" @@ -3663,7 +3703,7 @@ __metadata: "@faker-js/faker": 8.3.1 "@graphql-tools/merge": ^9.0.0 "@graphql-tools/resolvers-composition": ^7.0.0 - "@graphql-tools/schema": 10.0.0 + "@graphql-tools/schema": 10.0.2 "@graphql-tools/utils": ^10.0.0 "@neo4j/cypher-builder": ^1.7.1 "@types/deep-equal": 1.0.4 @@ -11372,6 +11412,15 @@ __metadata: languageName: node linkType: hard +"cross-inspect@npm:1.0.0": + version: 1.0.0 + resolution: "cross-inspect@npm:1.0.0" + dependencies: + tslib: ^2.4.0 + checksum: 975c81799549627027254eb70f1c349cefb14435d580bea6f351f510c839dcb1a9288983407bac2ad317e6eff29cf1e99299606da21f404562bfa64cec502239 + languageName: node + linkType: hard + "cross-spawn@npm:^5.1.0": version: 5.1.0 resolution: "cross-spawn@npm:5.1.0" From e23866f91e5e6fbdb9a6fc441e3d2b5800dac805 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:15:19 +0000 Subject: [PATCH 27/72] chore(deps): update dependency @types/react-dom to v18.2.17 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index bcd6b539f0d..bedccb9c02d 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -77,7 +77,7 @@ "@types/lodash.debounce": "4.0.9", "@types/markdown-it": "13.0.7", "@types/prettier": "2.7.3", - "@types/react-dom": "18.2.16", + "@types/react-dom": "18.2.17", "@types/webpack": "5.28.5", "autoprefixer": "10.4.16", "compression-webpack-plugin": "10.0.0", diff --git a/yarn.lock b/yarn.lock index 4e8d2f37650..76a39984594 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3644,7 +3644,7 @@ __metadata: "@types/lodash.debounce": 4.0.9 "@types/markdown-it": 13.0.7 "@types/prettier": 2.7.3 - "@types/react-dom": 18.2.16 + "@types/react-dom": 18.2.17 "@types/webpack": 5.28.5 autoprefixer: 10.4.16 classnames: 2.3.2 @@ -7754,12 +7754,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:18.2.16": - version: 18.2.16 - resolution: "@types/react-dom@npm:18.2.16" +"@types/react-dom@npm:18.2.17": + version: 18.2.17 + resolution: "@types/react-dom@npm:18.2.17" dependencies: "@types/react": "*" - checksum: 0d79f6e121155003840414e3cb1f5d70eef3a7f03450f7837c806b319b9e737880e368def615e2bf49159ed4041899e77b1002902e1285dd656783d2a836eb73 + checksum: 7a4e704ed4be6e0c3ccd8a22ff69386fe548304bf4db090513f42e059ff4c65f7a427790320051524d6578a2e4c9667bb7a80a4c989b72361c019fbe851d9385 languageName: node linkType: hard From 079cefac361b8e414bc63ba39a1af6358a40d188 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 03:32:08 +0000 Subject: [PATCH 28/72] chore(deps): update srvaroa/labeler action to v1.8.0 --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 95c3bfd9816..80d205f614b 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -13,6 +13,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: srvaroa/labeler@acf01918af3d0369c12611117826c1cf718b82ce # v1.7.2 + - uses: srvaroa/labeler@74404350883f8b689b026d8747622bd12d3f070a # v1.8.0 env: GITHUB_TOKEN: ${{ secrets.NEO4J_TEAM_GRAPHQL_PERSONAL_ACCESS_TOKEN }} From 4a2631ec7adf009223001f0f4894ae5eaf58decd Mon Sep 17 00:00:00 2001 From: angrykoala Date: Thu, 23 Nov 2023 11:46:02 +0000 Subject: [PATCH 29/72] Refactors nested readOperation into using EntitySelection --- .../queryAST/ast/operations/ReadOperation.ts | 55 ++++++++++++++----- .../queryAST/ast/selection/EntitySelection.ts | 17 ++++++ .../queryAST/ast/selection/NodeSelection.ts | 32 +++++++++++ .../ast/selection/RelationshipSelection.ts | 42 ++++++++++++++ .../queryAST/factory/OperationFactory.ts | 21 +++++++ .../graphql/tests/tck/issues/4077.test.ts | 2 +- 6 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts create mode 100644 packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts create mode 100644 packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index 782e7623d05..d548c06c970 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -22,7 +22,7 @@ import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/mode import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { filterTruthy } from "../../../../utils/utils"; import { hasTarget } from "../../utils/context-has-target"; -import { createNodeFromEntity, createRelationshipFromEntity } from "../../utils/create-node-from-entity"; +import { createNodeFromEntity } from "../../utils/create-node-from-entity"; import { wrapSubqueriesInCypherCalls } from "../../utils/wrap-subquery-in-calls"; import type { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; @@ -31,6 +31,9 @@ import { CypherAttributeField } from "../fields/attribute-fields/CypherAttribute import type { Filter } from "../filters/Filter"; import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters"; import type { Pagination } from "../pagination/Pagination"; +import type { EntitySelection, SelectionClause } from "../selection/EntitySelection"; +import { NodeSelection } from "../selection/NodeSelection"; +import { RelationshipSelection } from "../selection/RelationshipSelection"; import { CypherPropertySort } from "../sort/CypherPropertySort"; import type { Sort } from "../sort/Sort"; import type { OperationTranspileResult } from "./operations"; @@ -51,19 +54,38 @@ export class ReadOperation extends Operation { public nodeAlias: string | undefined; // This is just to maintain naming with the old way (this), remove after refactor + private selection: EntitySelection; + constructor({ target, relationship, directed, + selection, }: { target: ConcreteEntityAdapter; relationship?: RelationshipAdapter; directed?: boolean; + selection?: EntitySelection; }) { super(); this.target = target; this.directed = directed ?? true; this.relationship = relationship; + + if (!selection) { + if (relationship) { + this.selection = new RelationshipSelection({ + target: relationship, + directed, + }); + } else { + this.selection = new NodeSelection({ + target, + }); + } + } else { + this.selection = selection; + } } public setFields(fields: Field[]) { @@ -102,25 +124,24 @@ export class ReadOperation extends Operation { //TODO: dupe from transpile if (!hasTarget(context)) throw new Error("No parent node found!"); - const relVar = createRelationshipFromEntity(entity); - const targetNode = createNodeFromEntity(entity.target, context.neo4jGraphQLContext); - const relDirection = entity.getCypherDirection(this.directed); - const pattern = new Cypher.Pattern(context.target) - .withoutLabels() - .related(relVar) - .withDirection(relDirection) - .to(targetNode); - - const nestedContext = context.push({ target: targetNode, relationship: relVar }); + // eslint-disable-next-line prefer-const + let { selection: matchClause, nestedContext } = this.selection.apply(context); const filterPredicates = this.getPredicates(nestedContext); const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext).map((sq) => - new Cypher.Call(sq).innerWith(targetNode) + new Cypher.Call(sq).innerWith(nestedContext.target) ); const authFiltersPredicate = this.getAuthFilterPredicate(nestedContext); - const { preSelection, selectionClause: matchClause } = this.getSelectionClauses(nestedContext, pattern); + let extraMatches: SelectionClause[] = this.getChildren().flatMap((f) => { + return f.getSelection(nestedContext); + }); + + if (extraMatches.length > 0) { + extraMatches = [matchClause, ...extraMatches]; + matchClause = new Cypher.With("*"); + } const wherePredicate = Cypher.and(filterPredicates, ...authFiltersPredicate); let withWhere: Cypher.With | undefined; @@ -146,11 +167,11 @@ export class ReadOperation extends Operation { const cypherFieldSubqueries = this.getCypherFieldsSubqueries(nestedContext); const subqueries = Cypher.concat(...this.getFieldsSubqueries(nestedContext), ...cypherFieldSubqueries); - const sortSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.sortFields, [targetNode]); + const sortSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.sortFields, [nestedContext.target]); const ret = this.getProjectionClause(nestedContext, context.returnVariable, entity.isList); const clause = Cypher.concat( - ...preSelection, + ...extraMatches, matchClause, ...authFilterSubqueries, filterSubqueryWith, @@ -219,6 +240,9 @@ export class ReadOperation extends Operation { if (this.relationship) { return this.transpileNestedRelationship(this.relationship, context); } + + // const { selection: matchClause, nestedContext } = this.selection.apply(context); + const isCreateSelection = context.env.topLevelOperationName === "CREATE"; const node = createNodeFromEntity(this.target, context.neo4jGraphQLContext, this.nodeAlias); const filterSubqueries = wrapSubqueriesInCypherCalls(context, this.filters, [node]); @@ -307,6 +331,7 @@ export class ReadOperation extends Operation { public getChildren(): QueryASTNode[] { return filterTruthy([ + this.selection, ...this.filters, ...this.authFilters, ...this.fields, diff --git a/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts new file mode 100644 index 00000000000..fc2e79a0088 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts @@ -0,0 +1,17 @@ +import type Cypher from "@neo4j/cypher-builder"; +import type { QueryASTContext } from "../QueryASTContext"; +import { QueryASTNode } from "../QueryASTNode"; + +export type SelectionClause = Cypher.Match | Cypher.With | Cypher.Yield; + +export abstract class EntitySelection extends QueryASTNode { + public getChildren(): QueryASTNode[] { + return []; + } + + /** Apply selection over the given context, returns the updated context and the selection clause */ + public abstract apply(context: QueryASTContext): { + nestedContext: QueryASTContext; + selection: SelectionClause; + }; +} diff --git a/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts new file mode 100644 index 00000000000..a54eadae0e6 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts @@ -0,0 +1,32 @@ +import Cypher from "@neo4j/cypher-builder"; +import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; +import { createNodeFromEntity } from "../../utils/create-node-from-entity"; +import { QueryASTContext } from "../QueryASTContext"; +import { EntitySelection, type SelectionClause } from "./EntitySelection"; + +export class NodeSelection extends EntitySelection { + private target: ConcreteEntityAdapter; + private alias: string | undefined; + + constructor({ target, alias }: { target: ConcreteEntityAdapter; alias?: string }) { + super(); + this.target = target; + this.alias = alias; + } + + public apply(context: QueryASTContext): { + nestedContext: QueryASTContext; + selection: SelectionClause; + } { + const node = createNodeFromEntity(this.target, context.neo4jGraphQLContext, this.alias); + + return { + selection: new Cypher.Match(node), + nestedContext: new QueryASTContext({ + target: node, + neo4jGraphQLContext: context.neo4jGraphQLContext, + returnVariable: context.returnVariable, + }), + }; + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts new file mode 100644 index 00000000000..a1448a0446e --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts @@ -0,0 +1,42 @@ +import Cypher from "@neo4j/cypher-builder"; +import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; +import { hasTarget } from "../../utils/context-has-target"; +import { createNodeFromEntity, createRelationshipFromEntity } from "../../utils/create-node-from-entity"; +import type { QueryASTContext } from "../QueryASTContext"; +import { EntitySelection, type SelectionClause } from "./EntitySelection"; + +export class RelationshipSelection extends EntitySelection { + private target: RelationshipAdapter; + private alias: string | undefined; + private directed: boolean; + + constructor({ target, alias, directed }: { target: RelationshipAdapter; alias?: string; directed?: boolean }) { + super(); + this.target = target; + this.alias = alias; + this.directed = directed ?? true; + } + + public apply(context: QueryASTContext): { + nestedContext: QueryASTContext; + selection: SelectionClause; + } { + if (!hasTarget(context)) throw new Error("No parent node over a nested relationship match!"); + const relVar = createRelationshipFromEntity(this.target); + const targetNode = createNodeFromEntity(this.target.target, context.neo4jGraphQLContext, this.alias); + const relDirection = this.target.getCypherDirection(this.directed); + + const pattern = new Cypher.Pattern(context.target) + .withoutLabels() + .related(relVar) + .withDirection(relDirection) + .to(targetNode); + + // NOTE: Direction not passed (can we remove it from context?) + const nestedContext = context.push({ target: targetNode, relationship: relVar }); + return { + nestedContext: nestedContext, + selection: new Cypher.Match(pattern), + }; + } +} diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 2855073b4a4..bb41702fefe 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -48,6 +48,9 @@ import { CompositeConnectionReadOperation } from "../ast/operations/composite/Co import { CompositeReadOperation } from "../ast/operations/composite/CompositeReadOperation"; import { CompositeReadPartial } from "../ast/operations/composite/CompositeReadPartial"; import type { Operation } from "../ast/operations/operations"; +import type { EntitySelection } from "../ast/selection/EntitySelection"; +import { NodeSelection } from "../ast/selection/NodeSelection"; +import { RelationshipSelection } from "../ast/selection/RelationshipSelection"; import { getConcreteEntitiesInOnArgumentOfWhere } from "../utils/get-concrete-entities-in-on-argument-of-where"; import { getConcreteWhere } from "../utils/get-concrete-where"; import { isConcreteEntity } from "../utils/is-concrete-entity"; @@ -242,10 +245,28 @@ export class OperationsFactory { targetOperations: ["READ"], context, }); + + let selection: EntitySelection; + if (relationship) { + selection = new RelationshipSelection({ + target: relationship, + directed: Boolean(resolveTree.args?.directed ?? true), + }); + } else { + selection = new NodeSelection({ + target: entity, + }); + } + + // if(entityOrRel instanceof EntityAdapter){ + + // } + const operation = new ReadOperation({ target: entity, relationship, directed: Boolean(resolveTree.args?.directed ?? true), + selection, }); return this.hydrateReadOperation({ diff --git a/packages/graphql/tests/tck/issues/4077.test.ts b/packages/graphql/tests/tck/issues/4077.test.ts index 19874cbff4c..8a66f00d1c9 100644 --- a/packages/graphql/tests/tck/issues/4077.test.ts +++ b/packages/graphql/tests/tck/issues/4077.test.ts @@ -148,7 +148,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { `); }); - test("wrap authenticated subquery on nested read operation", async () => { + test.only("wrap authenticated subquery on nested read operation", async () => { const query = gql` query { videos { From 27dfd8e3f260b3bfef9ead79856f1ee25d5ba7e2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:22:39 +0000 Subject: [PATCH 30/72] chore(deps): update github/codeql-action digest to 407ffaf --- .github/workflows/reusable-codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-codeql-analysis.yml b/.github/workflows/reusable-codeql-analysis.yml index 51d71640199..72717506d99 100644 --- a/.github/workflows/reusable-codeql-analysis.yml +++ b/.github/workflows/reusable-codeql-analysis.yml @@ -14,9 +14,9 @@ jobs: with: node-version: lts/* - name: Initialize CodeQL - uses: github/codeql-action/init@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2 + uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2 with: config-file: ./.github/codeql/codeql-config.yml languages: javascript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2 + uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2 From 0ee6e84230b2795df8acc56d06e809fea8b81e1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:31:06 +0000 Subject: [PATCH 31/72] chore(deps): update node.js to 445acd9 --- packages/apollo-federation-subgraph-compatibility/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-federation-subgraph-compatibility/Dockerfile b/packages/apollo-federation-subgraph-compatibility/Dockerfile index 98791e74806..811732484d7 100644 --- a/packages/apollo-federation-subgraph-compatibility/Dockerfile +++ b/packages/apollo-federation-subgraph-compatibility/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts@sha256:146bbe4eaee99ae885be2a0a767f63a4b96032141d70d80e590f10e0d7ebabcb +FROM node:lts@sha256:445acd9b2ef7e9de665424053bf95652e0b8995ef36500557d48faf29300170a WORKDIR /app From d8ea4a5b760889912e09f41e9d97bde36ad43cbf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:19:16 +0000 Subject: [PATCH 32/72] chore(deps): update node.js to v20.10.0 --- Dockerfile | 2 +- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index ebef9c1d9c7..14f0cd34a35 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.9.0-buster-slim@sha256:050917322f944bd3d42d5d654cb1f4865fe2680c9981e9197fcadea770bb6003 +FROM node:20.10.0-buster-slim@sha256:b46831a79b7bd8d8d38b2bd50273b7611f0e168c840a97a1137a0cf243850086 WORKDIR /app diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 24fd066e42e..5746b0bbd33 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.9.4", + "@types/node": "20.9.5", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 19e9adaff1d..eaca969b98c 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.9.4", + "@types/node": "20.9.5", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 0bd03c12e9e..2a892b325e1 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.9.4", + "@types/node": "20.9.5", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 911ae191b36..4584eab9d23 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.9.4", + "@types/node": "20.9.5", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index 76a39984594..2e317206df9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3574,7 +3574,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.9.4 + "@types/node": 20.9.5 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3603,7 +3603,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.9.4 + "@types/node": 20.9.5 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3710,7 +3710,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.9.4 + "@types/node": 20.9.5 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3768,7 +3768,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.9.4 + "@types/node": 20.9.5 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7675,12 +7675,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.9.4": - version: 20.9.4 - resolution: "@types/node@npm:20.9.4" +"@types/node@npm:20.9.5": + version: 20.9.5 + resolution: "@types/node@npm:20.9.5" dependencies: undici-types: ~5.26.4 - checksum: 619144cfee8235f692009e4268a8b80ef4ec496670273ab1cef04e4a053b471f391af6701e65f2f91a107256933d902b6caec079d551b109e981b0b706624815 + checksum: 050ebd958e31ad21143f93614a25b15a8de5d0004338359409f76a661f734c4549698d852f21176c62f5661dacb572a090669cffef93cf6361c959e4d27d8d95 languageName: node linkType: hard From 5a04fa70045cecd46652bf28188540d0208330c6 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 10:50:33 +0000 Subject: [PATCH 33/72] Use selection on top level transpile read --- .../src/translate/queryAST/ast/QueryAST.ts | 1 - .../ast/operations/CreateOperation.ts | 11 ++-- .../queryAST/ast/operations/ReadOperation.ts | 56 +++++++++++++------ .../queryAST/ast/selection/NodeSelection.ts | 2 + .../queryAST/factory/OperationFactory.ts | 24 +++++++- .../queryAST/factory/QueryASTFactory.ts | 2 +- .../graphql/src/translate/translate-create.ts | 19 ++++--- .../graphql/src/translate/unwind-create.ts | 17 ++++-- .../relationship_properties/create.test.ts | 6 +- .../tests/tck/directives/plural.test.ts | 4 +- packages/graphql/tests/tck/simple.test.ts | 4 +- 11 files changed, 97 insertions(+), 49 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/ast/QueryAST.ts b/packages/graphql/src/translate/queryAST/ast/QueryAST.ts index ab3e7216e44..24fd7d6a6cb 100644 --- a/packages/graphql/src/translate/queryAST/ast/QueryAST.ts +++ b/packages/graphql/src/translate/queryAST/ast/QueryAST.ts @@ -36,7 +36,6 @@ export class QueryAST { public build(neo4jGraphQLContext: Neo4jGraphQLTranslationContext, varName?: string): Cypher.Clause { const context = this.buildQueryASTContext(neo4jGraphQLContext, varName); - return Cypher.concat(...this.transpile(context).clauses); } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts index 39c052537dd..65ab70cf0fd 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts @@ -17,14 +17,14 @@ * limitations under the License. */ -import { filterTruthy } from "../../../../utils/utils"; import Cypher from "@neo4j/cypher-builder"; -import type { OperationTranspileResult } from "./operations"; -import { Operation } from "./operations"; -import type { QueryASTNode } from "../QueryASTNode"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import type { ReadOperation } from "./ReadOperation"; +import { filterTruthy } from "../../../../utils/utils"; import type { QueryASTContext } from "../QueryASTContext"; +import type { QueryASTNode } from "../QueryASTNode"; +import type { ReadOperation } from "./ReadOperation"; +import type { OperationTranspileResult } from "./operations"; +import { Operation } from "./operations"; /** * This is currently just a dummy tree node, @@ -50,6 +50,7 @@ export class CreateOperation extends Operation { } public transpile(context: QueryASTContext): OperationTranspileResult { + console.log("Transpile create"); if (!context.target) throw new Error("No parent node found!"); context.env.topLevelOperationName = "CREATE"; // TODO: implement the actual create / unwind create diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index d548c06c970..532573d046f 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -22,7 +22,6 @@ import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/mode import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { filterTruthy } from "../../../../utils/utils"; import { hasTarget } from "../../utils/context-has-target"; -import { createNodeFromEntity } from "../../utils/create-node-from-entity"; import { wrapSubqueriesInCypherCalls } from "../../utils/wrap-subquery-in-calls"; import type { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; @@ -121,12 +120,12 @@ export class ReadOperation extends Operation { context: QueryASTContext ): OperationTranspileResult { const isCreateSelection = context.env.topLevelOperationName === "CREATE"; - //TODO: dupe from transpile if (!hasTarget(context)) throw new Error("No parent node found!"); // eslint-disable-next-line prefer-const let { selection: matchClause, nestedContext } = this.selection.apply(context); + const filterPredicates = this.getPredicates(nestedContext); const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext).map((sq) => @@ -241,24 +240,46 @@ export class ReadOperation extends Operation { return this.transpileNestedRelationship(this.relationship, context); } - // const { selection: matchClause, nestedContext } = this.selection.apply(context); + let { selection: matchClause, nestedContext } = this.selection.apply(context); + const isCreateSelection = nestedContext.env.topLevelOperationName === "CREATE"; + if (isCreateSelection) { + if (!context.hasTarget()) { + throw new Error("Invalid target for create operation"); + } + // Match is not applied on creation (last concat ignores the top level match) so we revert the context apply + nestedContext = context; + } + + // const node = createNodeFromEntity(this.target, nestedContext.neo4jGraphQLContext, this.nodeAlias); + const preWith: Cypher.With | undefined = + isCreateSelection && context.target ? new Cypher.With([context.target, nestedContext.target]) : undefined; - const isCreateSelection = context.env.topLevelOperationName === "CREATE"; - const node = createNodeFromEntity(this.target, context.neo4jGraphQLContext, this.nodeAlias); - const filterSubqueries = wrapSubqueriesInCypherCalls(context, this.filters, [node]); - const filterPredicates = this.getPredicates(context); + const filterSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.filters, [nestedContext.target]); + const filterPredicates = this.getPredicates(nestedContext); - const authFilterSubqueries = this.getAuthFilterSubqueries(context).map((sq) => - new Cypher.Call(sq).innerWith(node) + const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext).map((sq) => + new Cypher.Call(sq).innerWith(nestedContext.target) ); - const fieldSubqueries = this.getFieldsSubqueries(context); - const cypherFieldSubqueries = this.getCypherFieldsSubqueries(context); - const sortSubqueries = wrapSubqueriesInCypherCalls(context, this.sortFields, [node]); + const fieldSubqueries = this.getFieldsSubqueries(nestedContext); + const cypherFieldSubqueries = this.getCypherFieldsSubqueries(nestedContext); + const sortSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.sortFields, [nestedContext.target]); const subqueries = Cypher.concat(...fieldSubqueries); - const authFiltersPredicate = this.getAuthFilterPredicate(context); - const ret: Cypher.Return = this.getReturnStatement(context, context.returnVariable); - const { preSelection, selectionClause: matchClause } = this.getSelectionClauses(context, node); + const authFiltersPredicate = this.getAuthFilterPredicate(nestedContext); + const ret: Cypher.Return = this.getReturnStatement( + isCreateSelection ? context : nestedContext, + nestedContext.returnVariable + ); + + let extraMatches: SelectionClause[] = this.getChildren().flatMap((f) => { + return f.getSelection(nestedContext); + }); + + if (extraMatches.length > 0) { + extraMatches = [matchClause, ...extraMatches]; + matchClause = new Cypher.With("*"); + } + // const { preSelection, selectionClause: _matchClause2 } = this.getSelectionClauses(nestedContext, node); let filterSubqueryWith: Cypher.With | undefined; let filterSubqueriesClause: Cypher.Clause | undefined = undefined; @@ -285,7 +306,7 @@ export class ReadOperation extends Operation { let sortClause: Cypher.With | undefined; if (this.sortFields.length > 0 || this.pagination) { sortClause = new Cypher.With("*"); - this.addSortToClause(context, node, sortClause); + this.addSortToClause(nestedContext, nestedContext.target, sortClause); } const sortBlock = Cypher.concat(...sortSubqueries, sortClause); @@ -300,7 +321,8 @@ export class ReadOperation extends Operation { clause = Cypher.concat(filterSubqueriesClause, filterSubqueryWith, sortAndLimitBlock, subqueries, ret); } else { clause = Cypher.concat( - ...preSelection, + preWith, + ...extraMatches, matchClause, ...authFilterSubqueries, filterSubqueriesClause, diff --git a/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts index a54eadae0e6..fdd51bdcd46 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts @@ -26,6 +26,8 @@ export class NodeSelection extends EntitySelection { target: node, neo4jGraphQLContext: context.neo4jGraphQLContext, returnVariable: context.returnVariable, + env: context.env, + shouldCollect: context.shouldCollect, }), }; } diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index bb41702fefe..3aa87f1c4c4 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -83,7 +83,8 @@ export class OperationsFactory { public createTopLevelOperation( entity: EntityAdapter | RelationshipAdapter, resolveTree: ResolveTree, - context: Neo4jGraphQLTranslationContext + context: Neo4jGraphQLTranslationContext, + varName?: string ): Operation { if (isConcreteEntity(entity)) { // Handles deprecated top level fulltext @@ -110,7 +111,7 @@ export class OperationsFactory { if (context.resolveTree.args.fulltext || context.resolveTree.args.phrase) { op = this.createFulltextOperation(entity, resolveTree, context); } else { - op = this.createReadOperation(entity, resolveTree, context) as ReadOperation; + op = this.createReadOperation(entity, resolveTree, context, varName) as ReadOperation; } op.nodeAlias = TOP_LEVEL_NODE_NAME; @@ -233,7 +234,8 @@ export class OperationsFactory { public createReadOperation( entityOrRel: EntityAdapter | RelationshipAdapter, resolveTree: ResolveTree, - context: Neo4jGraphQLTranslationContext + context: Neo4jGraphQLTranslationContext, + varName?: string ): ReadOperation | CompositeReadOperation { const entity = entityOrRel instanceof RelationshipAdapter ? entityOrRel.target : entityOrRel; const relationship = entityOrRel instanceof RelationshipAdapter ? entityOrRel : undefined; @@ -255,6 +257,7 @@ export class OperationsFactory { } else { selection = new NodeSelection({ target: entity, + alias: varName, }); } @@ -279,10 +282,25 @@ export class OperationsFactory { } else { const concreteEntities = getConcreteEntitiesInOnArgumentOfWhere(entity, resolveTreeWhere); const concreteReadOperations = concreteEntities.map((concreteEntity: ConcreteEntityAdapter) => { + // Duplicate from normal read + let selection: EntitySelection; + if (relationship) { + selection = new RelationshipSelection({ + target: relationship, + directed: Boolean(resolveTree.args?.directed ?? true), + }); + } else { + selection = new NodeSelection({ + target: concreteEntity, + alias: varName, + }); + } + const readPartial = new CompositeReadPartial({ target: concreteEntity, relationship, directed: Boolean(resolveTree.args?.directed ?? true), + selection, }); const whereArgs = getConcreteWhere(entity, concreteEntity, resolveTreeWhere); diff --git a/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts b/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts index 456ea373239..003c041723b 100644 --- a/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/QueryASTFactory.ts @@ -53,7 +53,7 @@ export class QueryASTFactory { entityAdapter: EntityAdapter, context: Neo4jGraphQLTranslationContext ): QueryAST { - const operation = this.operationsFactory.createTopLevelOperation(entityAdapter, resolveTree, context); + const operation = this.operationsFactory.createTopLevelOperation(entityAdapter, resolveTree, context, "this"); return new QueryAST(operation); } } diff --git a/packages/graphql/src/translate/translate-create.ts b/packages/graphql/src/translate/translate-create.ts index 5944f0cec37..af668751851 100644 --- a/packages/graphql/src/translate/translate-create.ts +++ b/packages/graphql/src/translate/translate-create.ts @@ -17,20 +17,20 @@ * limitations under the License. */ -import type { Node } from "../classes"; +import Cypher from "@neo4j/cypher-builder"; import Debug from "debug"; -import createCreateAndParams from "./create-create-and-params"; +import type { Node } from "../classes"; +import { CallbackBucket } from "../classes/CallbackBucket"; import { DEBUG_TRANSLATE, META_CYPHER_VARIABLE } from "../constants"; +import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; +import { compileCypherIfExists } from "../utils/compile-cypher"; import { filterTruthy } from "../utils/utils"; -import { CallbackBucket } from "../classes/CallbackBucket"; -import Cypher from "@neo4j/cypher-builder"; -import unwindCreate from "./unwind-create"; import { UnsupportedUnwindOptimization } from "./batch-create/types"; -import { compileCypherIfExists } from "../utils/compile-cypher"; -import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { getAuthorizationStatements } from "./utils/get-authorization-statements"; -import { QueryASTEnv, QueryASTContext } from "./queryAST/ast/QueryASTContext"; +import createCreateAndParams from "./create-create-and-params"; +import { QueryASTContext, QueryASTEnv } from "./queryAST/ast/QueryASTContext"; import { QueryASTFactory } from "./queryAST/factory/QueryASTFactory"; +import unwindCreate from "./unwind-create"; +import { getAuthorizationStatements } from "./utils/get-authorization-statements"; const debug = Debug(DEBUG_TRANSLATE); @@ -126,6 +126,7 @@ export default async function translateCreate({ ); const queryASTEnv = new QueryASTEnv(); const projectedVariables: Cypher.Node[] = []; + console.log(queryAST); /** * Currently, the create projections are resolved separately for each input, * the following block reuses the same ReadOperation for each of the variable names generated during the create operations. diff --git a/packages/graphql/src/translate/unwind-create.ts b/packages/graphql/src/translate/unwind-create.ts index 31b64b2260b..f1770a4eb89 100644 --- a/packages/graphql/src/translate/unwind-create.ts +++ b/packages/graphql/src/translate/unwind-create.ts @@ -17,16 +17,20 @@ * limitations under the License. */ +import Cypher from "@neo4j/cypher-builder"; +import Debug from "debug"; import type { Node } from "../classes"; +import { CallbackBucket } from "../classes/CallbackBucket"; +import { DEBUG_TRANSLATE } from "../constants"; +import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; +import { getTreeDescriptor, mergeTreeDescriptors, parseCreate } from "./batch-create/parser"; import type { GraphQLCreateInput } from "./batch-create/types"; import { UnsupportedUnwindOptimization } from "./batch-create/types"; -import { mergeTreeDescriptors, getTreeDescriptor, parseCreate } from "./batch-create/parser"; import { UnwindCreateVisitor } from "./batch-create/unwind-create-visitors/UnwindCreateVisitor"; -import { CallbackBucket } from "../classes/CallbackBucket"; -import Cypher from "@neo4j/cypher-builder"; -import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { QueryASTFactory } from "./queryAST/factory/QueryASTFactory"; import { QueryASTContext, QueryASTEnv } from "./queryAST/ast/QueryASTContext"; +import { QueryASTFactory } from "./queryAST/factory/QueryASTFactory"; + +const debug = Debug(DEBUG_TRANSLATE); export default async function unwindCreate({ context, @@ -61,6 +65,7 @@ export default async function unwindCreate({ concreteEntityAdapter, context ); + debug(queryAST.print()); const queryASTEnv = new QueryASTEnv(); const queryASTContext = new QueryASTContext({ target: rootNodeVariable, @@ -70,7 +75,7 @@ export default async function unwindCreate({ shouldCollect: true, }); const clauses = queryAST.transpile(queryASTContext).clauses; - + console.log("Clauses", ...clauses); const projectionCypher = clauses.length ? Cypher.concat(...clauses) : new Cypher.Return(new Cypher.Literal("Query cannot conclude with CALL")); diff --git a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts index 5dcd054497d..f51fca6ee0d 100644 --- a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts +++ b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, translateQuery, formatParams } from "../../utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; describe("Relationship Properties Create Cypher", () => { let typeDefs: DocumentNode; @@ -48,7 +48,7 @@ describe("Relationship Properties Create Cypher", () => { }); }); - test("Create movie with a relationship that has properties", async () => { + test.only("Create movie with a relationship that has properties", async () => { const query = gql` mutation { createMovies( diff --git a/packages/graphql/tests/tck/directives/plural.test.ts b/packages/graphql/tests/tck/directives/plural.test.ts index 95bb0bbfaf9..e7fd9779b5a 100644 --- a/packages/graphql/tests/tck/directives/plural.test.ts +++ b/packages/graphql/tests/tck/directives/plural.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery, formatParams } from "../utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("Plural directive", () => { let typeDefs: DocumentNode; diff --git a/packages/graphql/tests/tck/simple.test.ts b/packages/graphql/tests/tck/simple.test.ts index a832134c4af..5626f70885b 100644 --- a/packages/graphql/tests/tck/simple.test.ts +++ b/packages/graphql/tests/tck/simple.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../src"; -import { formatCypher, translateQuery, formatParams } from "./utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "./utils/tck-test-utils"; describe("Simple Cypher tests", () => { let typeDefs: DocumentNode; From df1f79e797f3ffdaf63d28b0a7053a6a97707817 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 11:37:47 +0000 Subject: [PATCH 34/72] Entity selection on fulltext --- .../ast/operations/FulltextOperation.ts | 61 +------- .../queryAST/ast/operations/ReadOperation.ts | 2 +- .../ast/selection/FulltextSelection.ts | 60 ++++++++ .../queryAST/factory/OperationFactory.ts | 9 +- .../graphql/tests/tck/fulltext/auth.test.ts | 138 +++++++++--------- .../graphql/tests/tck/fulltext/match.test.ts | 12 +- .../tests/tck/fulltext/node-labels.test.ts | 12 +- .../graphql/tests/tck/fulltext/score.test.ts | 42 +++--- 8 files changed, 175 insertions(+), 161 deletions(-) create mode 100644 packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts diff --git a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts index 526f1ce9820..681edb2fce8 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts @@ -17,14 +17,14 @@ * limitations under the License. */ -import Cypher from "@neo4j/cypher-builder"; +import type Cypher from "@neo4j/cypher-builder"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import { mapLabelsWithContext } from "../../../../schema-model/utils/map-labels-with-context"; import { filterTruthy } from "../../../../utils/utils"; import type { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; import type { FulltextScoreField } from "../fields/FulltextScoreField"; +import type { EntitySelection } from "../selection/EntitySelection"; import { ReadOperation } from "./ReadOperation"; export type FulltextOptions = { @@ -34,86 +34,35 @@ export type FulltextOptions = { }; export class FulltextOperation extends ReadOperation { - private fulltext: FulltextOptions; - private scoreField: FulltextScoreField | undefined; - private scoreVariable: Cypher.Variable; constructor({ target, relationship, directed, - fulltext, scoreField, - scoreVariable, + selection, }: { target: ConcreteEntityAdapter; relationship?: RelationshipAdapter; directed?: boolean; - fulltext: FulltextOptions; scoreField: FulltextScoreField | undefined; - scoreVariable: Cypher.Variable; + selection: EntitySelection; }) { super({ target, directed, relationship, + selection, }); - this.fulltext = fulltext; this.scoreField = scoreField; - this.scoreVariable = scoreVariable; } public getChildren(): QueryASTNode[] { return filterTruthy([...super.getChildren(), this.scoreField]); } - protected getSelectionClauses( - context: QueryASTContext, - node: Cypher.Node | Cypher.Pattern - ): { - preSelection: Array; - selectionClause: Cypher.Yield | Cypher.With; - } { - if (!this.nodeAlias) { - throw new Error("Node alias missing on top level fulltext"); - } - - if (node instanceof Cypher.Pattern) { - throw new Error("Nested not supported in aggregations"); - } - - const phraseParam = new Cypher.Param(this.fulltext.phrase); - const indexName = new Cypher.Literal(this.fulltext.index); - - let fulltextClause: Cypher.Yield | Cypher.With = Cypher.db.index.fulltext - .queryNodes(indexName, phraseParam) - .yield(["node", node], ["score", this.scoreVariable]); - - const expectedLabels = mapLabelsWithContext(this.target.getLabels(), context.neo4jGraphQLContext); - - const whereOperators = expectedLabels.map((label) => { - return Cypher.in(new Cypher.Param(label), Cypher.labels(node)); - }); - - fulltextClause.where(Cypher.and(...whereOperators)); - - let extraMatches: Array = this.getChildren().flatMap((f) => { - return f.getSelection(context); - }); - - if (extraMatches.length > 0) { - extraMatches = [fulltextClause, ...extraMatches]; - fulltextClause = new Cypher.With("*"); - } - - return { - preSelection: extraMatches, - selectionClause: fulltextClause, - }; - } - protected getReturnStatement(context: QueryASTContext, returnVariable: Cypher.Variable): Cypher.Return { const returnClause = super.getReturnStatement(context, returnVariable); diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index 532573d046f..f0610a36e04 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -64,7 +64,7 @@ export class ReadOperation extends Operation { target: ConcreteEntityAdapter; relationship?: RelationshipAdapter; directed?: boolean; - selection?: EntitySelection; + selection: EntitySelection; }) { super(); this.target = target; diff --git a/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts new file mode 100644 index 00000000000..cd518e98349 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts @@ -0,0 +1,60 @@ +import Cypher from "@neo4j/cypher-builder"; +import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; +import { mapLabelsWithContext } from "../../../../schema-model/utils/map-labels-with-context"; +import { QueryASTContext } from "../QueryASTContext"; +import type { FulltextOptions } from "../operations/FulltextOperation"; +import { EntitySelection, type SelectionClause } from "./EntitySelection"; + +export class FulltextSelection extends EntitySelection { + private target: ConcreteEntityAdapter; + private fulltext: FulltextOptions; + + private scoreVariable: Cypher.Variable; + + constructor({ + target, + fulltext, + scoreVariable, + }: { + target: ConcreteEntityAdapter; + fulltext: FulltextOptions; + scoreVariable: Cypher.Variable; + }) { + super(); + this.target = target; + this.fulltext = fulltext; + this.scoreVariable = scoreVariable; + } + + public apply(context: QueryASTContext): { + nestedContext: QueryASTContext; + selection: SelectionClause; + } { + const node = new Cypher.Node(); + const phraseParam = new Cypher.Param(this.fulltext.phrase); + const indexName = new Cypher.Literal(this.fulltext.index); + + const fulltextClause: Cypher.Yield | Cypher.With = Cypher.db.index.fulltext + .queryNodes(indexName, phraseParam) + .yield(["node", node], ["score", this.scoreVariable]); + + const expectedLabels = mapLabelsWithContext(this.target.getLabels(), context.neo4jGraphQLContext); + + const whereOperators = expectedLabels.map((label) => { + return Cypher.in(new Cypher.Param(label), Cypher.labels(node)); + }); + + fulltextClause.where(Cypher.and(...whereOperators)); + + return { + selection: fulltextClause, + nestedContext: new QueryASTContext({ + target: node, + neo4jGraphQLContext: context.neo4jGraphQLContext, + returnVariable: context.returnVariable, + env: context.env, + shouldCollect: context.shouldCollect, + }), + }; + } +} diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 3aa87f1c4c4..d23eec7e7d9 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -49,6 +49,7 @@ import { CompositeReadOperation } from "../ast/operations/composite/CompositeRea import { CompositeReadPartial } from "../ast/operations/composite/CompositeReadPartial"; import type { Operation } from "../ast/operations/operations"; import type { EntitySelection } from "../ast/selection/EntitySelection"; +import { FulltextSelection } from "../ast/selection/FulltextSelection"; import { NodeSelection } from "../ast/selection/NodeSelection"; import { RelationshipSelection } from "../ast/selection/RelationshipSelection"; import { getConcreteEntitiesInOnArgumentOfWhere } from "../utils/get-concrete-entities-in-on-argument-of-where"; @@ -195,12 +196,16 @@ export class OperationsFactory { context, }); + const selection = new FulltextSelection({ + target: entity, + fulltext: fulltextOptions, + scoreVariable: fulltextOptions.score, + }); const operation = new FulltextOperation({ target: entity, directed: Boolean(resolverArgs.directed ?? true), - fulltext: fulltextOptions, scoreField, - scoreVariable: fulltextOptions.score, + selection, }); if (scoreFilter) { diff --git a/packages/graphql/tests/tck/fulltext/auth.test.ts b/packages/graphql/tests/tck/fulltext/auth.test.ts index e5c842d6750..2a76ee0ea17 100644 --- a/packages/graphql/tests/tck/fulltext/auth.test.ts +++ b/packages/graphql/tests/tck/fulltext/auth.test.ts @@ -75,11 +75,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0) - RETURN this { .title } AS this" + WHERE ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -131,11 +131,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -189,11 +189,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this1:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -249,11 +249,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this2:DIRECTED]-(this1:Person) WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this3:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -312,11 +312,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this2:DIRECTED]-(this1:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this3:DIRECTED]-(this2:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -376,11 +376,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this2:Person) WHERE ($param3 IS NOT NULL AND this1.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($param3 IS NOT NULL AND this2.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -437,11 +437,11 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this2:Person) WHERE NOT ($param3 IS NOT NULL AND this1.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this2:DIRECTED]-(this3:Person) WHERE NOT ($param3 IS NOT NULL AND this2.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -492,14 +492,14 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[:DIRECTED]-(this1:Person) - WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) }) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -551,14 +551,14 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[:DIRECTED]-(this1:Person) - WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -612,17 +612,17 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[:DIRECTED]-(this1:Person) - WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) } AND NOT (EXISTS { - MATCH (this)<-[:DIRECTED]-(this1:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -678,14 +678,14 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -744,17 +744,17 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) } AND NOT (EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -814,14 +814,14 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE ($param3 IS NOT NULL AND this1.year = $param3) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($param3 IS NOT NULL AND this2.year = $param3) }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` @@ -878,17 +878,17 @@ describe("Cypher -> fulltext -> Auth", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE ($param3 IS NOT NULL AND this1.year = $param3) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($param3 IS NOT NULL AND this2.year = $param3) } AND NOT (EXISTS { - MATCH (this)<-[this1:DIRECTED]-(this2:Person) - WHERE NOT ($param3 IS NOT NULL AND this1.year = $param3) + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE NOT ($param3 IS NOT NULL AND this2.year = $param3) }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this { .title } AS this" + RETURN this0 { .title } AS this" `); expect(result.params).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/fulltext/match.test.ts b/packages/graphql/tests/tck/fulltext/match.test.ts index 9bef2efe409..10050ac53a8 100644 --- a/packages/graphql/tests/tck/fulltext/match.test.ts +++ b/packages/graphql/tests/tck/fulltext/match.test.ts @@ -50,9 +50,9 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) - RETURN this { .title } AS this" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) + RETURN this0 { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -78,9 +78,9 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE ($param1 IN labels(this) AND this.title = $param2) - RETURN this { .title } AS this" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND this0.title = $param2) + RETURN this0 { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/fulltext/node-labels.test.ts b/packages/graphql/tests/tck/fulltext/node-labels.test.ts index 4aa8abf72db..75fe3842a78 100644 --- a/packages/graphql/tests/tck/fulltext/node-labels.test.ts +++ b/packages/graphql/tests/tck/fulltext/node-labels.test.ts @@ -47,9 +47,9 @@ describe("Cypher -> fulltext -> Additional Labels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE ($param1 IN labels(this) AND $param2 IN labels(this)) - RETURN this { .title } AS this" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND $param2 IN labels(this0)) + RETURN this0 { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -93,9 +93,9 @@ describe("Cypher -> fulltext -> Additional Labels", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE ($param1 IN labels(this) AND $param2 IN labels(this)) - RETURN this { .title } AS this" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND $param2 IN labels(this0)) + RETURN this0 { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/fulltext/score.test.ts b/packages/graphql/tests/tck/fulltext/score.test.ts index a8ad7932254..b92f00598d7 100644 --- a/packages/graphql/tests/tck/fulltext/score.test.ts +++ b/packages/graphql/tests/tck/fulltext/score.test.ts @@ -55,9 +55,9 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) - RETURN this { .title, .released } AS movie, var0 AS score" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) + RETURN this0 { .title, .released } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -84,9 +84,9 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE ($param1 IN labels(this) AND this.released > $param2) - RETURN this { .title, .released } AS movie, var0 AS score" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND this0.released > $param2) + RETURN this0 { .title, .released } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -116,9 +116,9 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE ($param1 IN labels(this) AND var0 >= $param2) - RETURN this { .title } AS movie, var0 AS score" + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND var1 >= $param2) + RETURN this0 { .title } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -145,11 +145,11 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - ORDER BY this.title DESC - RETURN this { .title } AS movie, var0 AS score" + ORDER BY this0.title DESC + RETURN this0 { .title } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -175,11 +175,11 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - ORDER BY var0 ASC - RETURN this { .title } AS movie, var0 AS score" + ORDER BY var1 ASC + RETURN this0 { .title } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -208,11 +208,11 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this, score AS var0 - WHERE $param1 IN labels(this) + "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) WITH * - ORDER BY var0 ASC, this.title DESC - RETURN this { .title } AS movie, var0 AS score" + ORDER BY var1 ASC, this0.title DESC + RETURN this0 { .title } AS movie, var1 AS score" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` From ef3433af2f2666227b41bc60624dbfc44ab9085c Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 13:22:55 +0000 Subject: [PATCH 35/72] Update composite read partial to use selection --- .../ast/operations/CreateOperation.ts | 1 - .../queryAST/ast/operations/ReadOperation.ts | 19 +------ .../composite/CompositeReadPartial.ts | 53 ++++++++++--------- .../ast/selection/RelationshipSelection.ts | 28 +++++++--- .../queryAST/factory/OperationFactory.ts | 5 +- .../graphql/src/translate/unwind-create.ts | 1 - .../tck/experimental/union-top-level.test.ts | 4 +- 7 files changed, 58 insertions(+), 53 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts index 65ab70cf0fd..d3f00ad0ac2 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/CreateOperation.ts @@ -50,7 +50,6 @@ export class CreateOperation extends Operation { } public transpile(context: QueryASTContext): OperationTranspileResult { - console.log("Transpile create"); if (!context.target) throw new Error("No parent node found!"); context.env.topLevelOperationName = "CREATE"; // TODO: implement the actual create / unwind create diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index f0610a36e04..9709fd597bd 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -31,8 +31,6 @@ import type { Filter } from "../filters/Filter"; import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters"; import type { Pagination } from "../pagination/Pagination"; import type { EntitySelection, SelectionClause } from "../selection/EntitySelection"; -import { NodeSelection } from "../selection/NodeSelection"; -import { RelationshipSelection } from "../selection/RelationshipSelection"; import { CypherPropertySort } from "../sort/CypherPropertySort"; import type { Sort } from "../sort/Sort"; import type { OperationTranspileResult } from "./operations"; @@ -53,7 +51,7 @@ export class ReadOperation extends Operation { public nodeAlias: string | undefined; // This is just to maintain naming with the old way (this), remove after refactor - private selection: EntitySelection; + protected selection: EntitySelection; constructor({ target, @@ -71,20 +69,7 @@ export class ReadOperation extends Operation { this.directed = directed ?? true; this.relationship = relationship; - if (!selection) { - if (relationship) { - this.selection = new RelationshipSelection({ - target: relationship, - directed, - }); - } else { - this.selection = new NodeSelection({ - target, - }); - } - } else { - this.selection = selection; - } + this.selection = selection; } public setFields(fields: Field[]) { diff --git a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeReadPartial.ts b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeReadPartial.ts index 96e022b8b92..2b43ae83aea 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeReadPartial.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeReadPartial.ts @@ -20,8 +20,8 @@ import Cypher from "@neo4j/cypher-builder"; import type { RelationshipAdapter } from "../../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { hasTarget } from "../../../utils/context-has-target"; -import { createNodeFromEntity, createRelationshipFromEntity } from "../../../utils/create-node-from-entity"; -import { QueryASTContext } from "../../QueryASTContext"; +import type { QueryASTContext } from "../../QueryASTContext"; +import type { SelectionClause } from "../../selection/EntitySelection"; import { ReadOperation } from "../ReadOperation"; import type { OperationTranspileResult } from "../operations"; @@ -39,19 +39,19 @@ export class CompositeReadPartial extends ReadOperation { context: QueryASTContext ): OperationTranspileResult { if (!hasTarget(context)) throw new Error("No parent node found!"); - const parentNode = context.target; - const relVar = createRelationshipFromEntity(entity); - const targetNode = createNodeFromEntity(this.target, context.neo4jGraphQLContext); - const relDirection = entity.getCypherDirection(this.directed); - - const pattern = new Cypher.Pattern(parentNode) - .withoutLabels() - .related(relVar) - .withDirection(relDirection) - .to(targetNode); - - const nestedContext = context.push({ target: targetNode, relationship: relVar }); - const { preSelection, selectionClause: matchClause } = this.getSelectionClauses(nestedContext, pattern); + + // eslint-disable-next-line prefer-const + let { selection: matchClause, nestedContext } = this.selection.apply(context); + + let extraMatches: SelectionClause[] = this.getChildren().flatMap((f) => { + return f.getSelection(nestedContext); + }); + + if (extraMatches.length > 0) { + extraMatches = [matchClause, ...extraMatches]; + matchClause = new Cypher.With("*"); + } + const filterPredicates = this.getPredicates(nestedContext); const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext); const authFiltersPredicate = this.getAuthFilterPredicate(nestedContext); @@ -66,12 +66,12 @@ export class CompositeReadPartial extends ReadOperation { const subqueries = Cypher.concat(...this.getFieldsSubqueries(nestedContext), ...cypherFieldSubqueries); const sortSubqueries = this.sortFields .flatMap((sq) => sq.getSubqueries(nestedContext)) - .map((sq) => new Cypher.Call(sq).innerWith(targetNode)); + .map((sq) => new Cypher.Call(sq).innerWith(nestedContext.target)); const ret = this.getProjectionClause(nestedContext, context.returnVariable); const clause = Cypher.concat( - ...preSelection, + ...extraMatches, matchClause, ...authFilterSubqueries, subqueries, @@ -87,13 +87,18 @@ export class CompositeReadPartial extends ReadOperation { // dupe from transpileNestedCompositeRelationship private transpileTopLevelCompositeEntity(context: QueryASTContext): OperationTranspileResult { - const targetNode = createNodeFromEntity(this.target, context.neo4jGraphQLContext); - const nestedContext = new QueryASTContext({ - target: targetNode, - env: context.env, - neo4jGraphQLContext: context.neo4jGraphQLContext, + // eslint-disable-next-line prefer-const + let { selection: matchClause, nestedContext } = this.selection.apply(context); + + let extraMatches: SelectionClause[] = this.getChildren().flatMap((f) => { + return f.getSelection(nestedContext); }); - const { preSelection, selectionClause: matchClause } = this.getSelectionClauses(nestedContext, targetNode); + + if (extraMatches.length > 0) { + extraMatches = [matchClause, ...extraMatches]; + matchClause = new Cypher.With("*"); + } + const filterPredicates = this.getPredicates(nestedContext); const authFilterSubqueries = this.getAuthFilterSubqueries(nestedContext); const authFiltersPredicate = this.getAuthFilterPredicate(nestedContext); @@ -105,7 +110,7 @@ export class CompositeReadPartial extends ReadOperation { const subqueries = Cypher.concat(...this.getFieldsSubqueries(nestedContext)); const ret = this.getProjectionClause(nestedContext, context.returnVariable); - const clause = Cypher.concat(...preSelection, matchClause, ...authFilterSubqueries, subqueries, ret); + const clause = Cypher.concat(...extraMatches, matchClause, ...authFilterSubqueries, subqueries, ret); return { clauses: [clause], diff --git a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts index a1448a0446e..2b939dd109c 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts @@ -1,4 +1,5 @@ import Cypher from "@neo4j/cypher-builder"; +import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { hasTarget } from "../../utils/context-has-target"; import { createNodeFromEntity, createRelationshipFromEntity } from "../../utils/create-node-from-entity"; @@ -6,15 +7,28 @@ import type { QueryASTContext } from "../QueryASTContext"; import { EntitySelection, type SelectionClause } from "./EntitySelection"; export class RelationshipSelection extends EntitySelection { - private target: RelationshipAdapter; + private relationship: RelationshipAdapter; + // Overrides relationship target for composite entities + private targetOverride: ConcreteEntityAdapter | undefined; private alias: string | undefined; private directed: boolean; - constructor({ target, alias, directed }: { target: RelationshipAdapter; alias?: string; directed?: boolean }) { + constructor({ + relationship, + alias, + directed, + targetOverride, + }: { + relationship: RelationshipAdapter; + alias?: string; + directed?: boolean; + targetOverride?: ConcreteEntityAdapter; + }) { super(); - this.target = target; + this.relationship = relationship; this.alias = alias; this.directed = directed ?? true; + this.targetOverride = targetOverride; } public apply(context: QueryASTContext): { @@ -22,9 +36,11 @@ export class RelationshipSelection extends EntitySelection { selection: SelectionClause; } { if (!hasTarget(context)) throw new Error("No parent node over a nested relationship match!"); - const relVar = createRelationshipFromEntity(this.target); - const targetNode = createNodeFromEntity(this.target.target, context.neo4jGraphQLContext, this.alias); - const relDirection = this.target.getCypherDirection(this.directed); + const relVar = createRelationshipFromEntity(this.relationship); + + const relationshipTarget = this.targetOverride ?? this.relationship.target; + const targetNode = createNodeFromEntity(relationshipTarget, context.neo4jGraphQLContext, this.alias); + const relDirection = this.relationship.getCypherDirection(this.directed); const pattern = new Cypher.Pattern(context.target) .withoutLabels() diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index d23eec7e7d9..b723a30bb0b 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -256,7 +256,7 @@ export class OperationsFactory { let selection: EntitySelection; if (relationship) { selection = new RelationshipSelection({ - target: relationship, + relationship, directed: Boolean(resolveTree.args?.directed ?? true), }); } else { @@ -291,8 +291,9 @@ export class OperationsFactory { let selection: EntitySelection; if (relationship) { selection = new RelationshipSelection({ - target: relationship, + relationship, directed: Boolean(resolveTree.args?.directed ?? true), + targetOverride: concreteEntity, }); } else { selection = new NodeSelection({ diff --git a/packages/graphql/src/translate/unwind-create.ts b/packages/graphql/src/translate/unwind-create.ts index f1770a4eb89..7127ab4892b 100644 --- a/packages/graphql/src/translate/unwind-create.ts +++ b/packages/graphql/src/translate/unwind-create.ts @@ -75,7 +75,6 @@ export default async function unwindCreate({ shouldCollect: true, }); const clauses = queryAST.transpile(queryASTContext).clauses; - console.log("Clauses", ...clauses); const projectionCypher = clauses.length ? Cypher.concat(...clauses) : new Cypher.Return(new Cypher.Literal("Query cannot conclude with CALL")); diff --git a/packages/graphql/tests/tck/experimental/union-top-level.test.ts b/packages/graphql/tests/tck/experimental/union-top-level.test.ts index 857684ffff7..2bd74aa1393 100644 --- a/packages/graphql/tests/tck/experimental/union-top-level.test.ts +++ b/packages/graphql/tests/tck/experimental/union-top-level.test.ts @@ -17,11 +17,11 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery, formatParams } from "../utils/tck-test-utils"; import { createBearerToken } from "../../utils/create-bearer-token"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("Union top level operations", () => { const secret = "secret"; From 7342f2a2d78a595e90fd1fea48639bbc01969f38 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 17:14:50 +0000 Subject: [PATCH 36/72] Use selection on aggregations --- .../ast/operations/AggregationOperation.ts | 33 ++++++++++++------- .../queryAST/factory/OperationFactory.ts | 8 +++++ .../field-level-aggregations-edge.test.ts | 6 ++-- .../field-level-aggregations.test.ts | 18 +++++----- .../tests/tck/aggregations/many.test.ts | 6 ++-- .../graphql/tests/tck/issues/1933.test.ts | 12 +++---- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts index 06586620063..867ffbccfaa 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts @@ -29,6 +29,7 @@ import type { AggregationField } from "../fields/aggregation-fields/AggregationF import type { Filter } from "../filters/Filter"; import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters"; import type { Pagination } from "../pagination/Pagination"; +import type { EntitySelection } from "../selection/EntitySelection"; import type { Sort } from "../sort/Sort"; import type { OperationTranspileResult } from "./operations"; import { Operation } from "./operations"; @@ -36,6 +37,7 @@ import { Operation } from "./operations"; // TODO: somewhat dupe of readOperation export class AggregationOperation extends Operation { public readonly entity: ConcreteEntityAdapter | RelationshipAdapter; // TODO: normal entities + private selection: EntitySelection; protected directed: boolean; public fields: AggregationField[] = []; // Aggregation fields @@ -55,13 +57,16 @@ export class AggregationOperation extends Operation { constructor({ entity, directed = true, + selection, }: { entity: ConcreteEntityAdapter | RelationshipAdapter; directed?: boolean; + selection: EntitySelection; }) { super(); this.entity = entity; this.directed = directed; + this.selection = selection; } public setFields(fields: AggregationField[]) { @@ -92,6 +97,7 @@ export class AggregationOperation extends Operation { ...this.filters, ...this.sortFields, ...this.authFilters, + this.selection, this.pagination, ]); } @@ -200,14 +206,14 @@ export class AggregationOperation extends Operation { const fieldSubqueries = this.fields.map((f) => { const returnVariable = new Cypher.Variable(); this.aggregationProjectionMap.set(f.getProjectionField(returnVariable)); - return this.createSubquery(f, pattern, operationContext.target, returnVariable, operationContext); + return this.createSubquery(f, pattern, returnVariable, context); }); const nodeMap = new Cypher.Map(); const nodeFieldSubqueries = this.nodeFields.map((f) => { const returnVariable = new Cypher.Variable(); nodeMap.set(f.getProjectionField(returnVariable)); - return this.createSubquery(f, pattern, operationContext.target, returnVariable, operationContext); + return this.createSubquery(f, pattern, returnVariable, context); }); if (nodeMap.size > 0) { @@ -216,12 +222,11 @@ export class AggregationOperation extends Operation { let edgeFieldSubqueries: Cypher.Clause[] = []; if (operationContext.relationship) { - const relVar = operationContext.relationship; const edgeMap = new Cypher.Map(); edgeFieldSubqueries = this.edgeFields.map((f) => { const returnVariable = new Cypher.Variable(); edgeMap.set(f.getProjectionField(returnVariable)); - return this.createSubquery(f, pattern, relVar, returnVariable, operationContext); + return this.createSubquery(f, pattern, returnVariable, context, "edge"); }); if (edgeMap.size > 0) { this.aggregationProjectionMap.set("edge", edgeMap); @@ -234,18 +239,21 @@ export class AggregationOperation extends Operation { private createSubquery( field: AggregationField, pattern: Cypher.Pattern, - target: Cypher.Variable, returnVariable: Cypher.Variable, - context: QueryASTContext + context: QueryASTContext, + target: "edge" | "node" = "node" ): Cypher.Clause { - const matchClause = new Cypher.Match(pattern); + const { selection: matchClause, nestedContext } = this.selection.apply(context); let extraSelectionWith: Cypher.With | undefined = undefined; - const nestedSubqueries = wrapSubqueriesInCypherCalls(context, this.getChildren(), [target]); - const filterPredicates = this.getPredicates(context); + const nestedSubqueries = wrapSubqueriesInCypherCalls(nestedContext, this.getChildren(), [nestedContext.target]); + const targetVar = target === "edge" ? nestedContext.relationship : nestedContext.target; + if (!targetVar) throw new Error("Edge not define in aggregations"); + + const filterPredicates = this.getPredicates(nestedContext); const selectionClauses = this.getChildren().flatMap((c) => { - return c.getSelection(context); + return c.getSelection(nestedContext); }); if (selectionClauses.length > 0 || nestedSubqueries.length > 0) { extraSelectionWith = new Cypher.With("*"); @@ -258,12 +266,13 @@ export class AggregationOperation extends Operation { matchClause.where(filterPredicates); } } - const ret = this.getFieldProjectionClause(target, returnVariable, field); + + const ret = this.getFieldProjectionClause(targetVar, returnVariable, field); let sortClause: Cypher.With | undefined; if (this.sortFields.length > 0 || this.pagination) { sortClause = new Cypher.With("*"); - this.addSortToClause(context, target, sortClause); + this.addSortToClause(nestedContext, targetVar, sortClause); } return Cypher.concat( diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index b723a30bb0b..945fcdf5bd1 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -356,6 +356,10 @@ export class OperationsFactory { const operation = new AggregationOperation({ entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), + selection: new RelationshipSelection({ + relationship: entityOrRel, + directed: Boolean(resolveTree.args?.directed ?? true), + }), }); return this.hydrateAggregationOperation({ @@ -400,6 +404,10 @@ export class OperationsFactory { const operation = new AggregationOperation({ entity, directed: Boolean(resolveTree.args?.directed ?? true), + selection: new NodeSelection({ + target: entity, + alias: "this", + }), }); //TODO: use a hydrate method here const rawProjectionFields = { diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts index d3f5df051a6..72a629090f4 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, translateQuery, formatParams } from "../../utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; describe("Field Level Aggregations", () => { let typeDefs: DocumentNode; @@ -49,7 +49,7 @@ describe("Field Level Aggregations", () => { }); }); - test("Edge Int Aggregations", async () => { + test.only("Edge Int Aggregations", async () => { const query = gql` query { movies { diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts index 22ed931d3b4..dc19a889fd7 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts @@ -101,13 +101,13 @@ describe("Field Level Aggregations", () => { } CALL { WITH this - MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) - WITH this1 - ORDER BY size(this1.name) DESC - WITH collect(this1.name) AS list - RETURN { longest: head(list), shortest: last(list) } AS var3 + MATCH (this)<-[this3:ACTED_IN]-(this4:Actor) + WITH this4 + ORDER BY size(this4.name) DESC + WITH collect(this4.name) AS list + RETURN { longest: head(list), shortest: last(list) } AS var5 } - RETURN this { actorsAggregate: { count: var2, node: { name: var3 } } } AS this" + RETURN this { actorsAggregate: { count: var2, node: { name: var5 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -247,10 +247,10 @@ describe("Field Level Aggregations", () => { } CALL { WITH this - MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) - RETURN { min: min(this1.age), max: max(this1.age), average: avg(this1.age), sum: sum(this1.age) } AS var3 + MATCH (this)<-[this3:ACTED_IN]-(this4:Actor) + RETURN { min: min(this4.age), max: max(this4.age), average: avg(this4.age), sum: sum(this4.age) } AS var5 } - RETURN this { actorsAggregate: { node: { name: var2, age: var3 } } } AS this" + RETURN this { actorsAggregate: { node: { name: var2, age: var5 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/aggregations/many.test.ts b/packages/graphql/tests/tck/aggregations/many.test.ts index fdecca9e2f4..e1affab85db 100644 --- a/packages/graphql/tests/tck/aggregations/many.test.ts +++ b/packages/graphql/tests/tck/aggregations/many.test.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import type { DocumentNode } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, translateQuery, formatParams } from "../utils/tck-test-utils"; +import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("Cypher Aggregations Many", () => { let typeDefs: DocumentNode; @@ -41,7 +41,7 @@ describe("Cypher Aggregations Many", () => { }); }); - test("Min", async () => { + test.only("Min", async () => { const query = gql` { moviesAggregate { diff --git a/packages/graphql/tests/tck/issues/1933.test.ts b/packages/graphql/tests/tck/issues/1933.test.ts index f6427b805b7..fd4a2611139 100644 --- a/packages/graphql/tests/tck/issues/1933.test.ts +++ b/packages/graphql/tests/tck/issues/1933.test.ts @@ -94,10 +94,10 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } CALL { WITH this - MATCH (this)-[this3:PARTICIPATES]->(this4:Project) - RETURN { min: min(this3.allocation), max: max(this3.allocation), average: avg(this3.allocation), sum: sum(this3.allocation) } AS var6 + MATCH (this)-[this6:PARTICIPATES]->(this7:Project) + RETURN { min: min(this6.allocation), max: max(this6.allocation), average: avg(this6.allocation), sum: sum(this6.allocation) } AS var8 } - RETURN this { .employeeId, .firstName, .lastName, projectsAggregate: { count: var5, edge: { allocation: var6 } } } AS this" + RETURN this { .employeeId, .firstName, .lastName, projectsAggregate: { count: var5, edge: { allocation: var8 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -147,10 +147,10 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } CALL { WITH this - MATCH (this)-[this4:PARTICIPATES]->(this5:Project) - RETURN { min: min(this4.allocation), max: max(this4.allocation), average: avg(this4.allocation), sum: sum(this4.allocation) } AS var7 + MATCH (this)-[this7:PARTICIPATES]->(this8:Project) + RETURN { min: min(this7.allocation), max: max(this7.allocation), average: avg(this7.allocation), sum: sum(this7.allocation) } AS var9 } - RETURN this { .employeeId, .firstName, .lastName, projectsAggregate: { count: var6, edge: { allocation: var7 } } } AS this" + RETURN this { .employeeId, .firstName, .lastName, projectsAggregate: { count: var6, edge: { allocation: var9 } } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` From 2cecb73513cedfa8bdfe9a64d08d3b6b64e5a61e Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 17:31:51 +0000 Subject: [PATCH 37/72] Add fulltext to aggregations --- .../queryAST/factory/OperationFactory.ts | 31 +- .../src/translate/translate-aggregate.ts | 285 +++++++++--------- .../tests/tck/fulltext/aggregate.test.ts | 9 +- 3 files changed, 169 insertions(+), 156 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 945fcdf5bd1..334fa3e6119 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -336,6 +336,7 @@ export class OperationsFactory { resolveTree: ResolveTree, context: Neo4jGraphQLTranslationContext ): AggregationOperation | CompositeAggregationOperation { + console.log("createAggregationOperation"); let entity: ConcreteEntityAdapter | InterfaceEntityAdapter; if (entityOrRel instanceof RelationshipAdapter) { entity = entityOrRel.target as ConcreteEntityAdapter; @@ -353,13 +354,15 @@ export class OperationsFactory { context, }); + const selection = new RelationshipSelection({ + relationship: entityOrRel, + directed: Boolean(resolveTree.args?.directed ?? true), + }); + const operation = new AggregationOperation({ entity: entityOrRel, directed: Boolean(resolveTree.args?.directed ?? true), - selection: new RelationshipSelection({ - relationship: entityOrRel, - directed: Boolean(resolveTree.args?.directed ?? true), - }), + selection, }); return this.hydrateAggregationOperation({ @@ -401,13 +404,25 @@ export class OperationsFactory { } } else { if (isConcreteEntity(entity)) { + let selection: EntitySelection; + if (context.resolveTree.args.fulltext || context.resolveTree.args.phrase) { + const fulltextOptions = this.getFulltextOptions(context); + selection = new FulltextSelection({ + target: entity, + fulltext: fulltextOptions, + scoreVariable: fulltextOptions.score, + }); + } else { + selection = new NodeSelection({ + target: entity, + alias: "this", + }); + } + const operation = new AggregationOperation({ entity, directed: Boolean(resolveTree.args?.directed ?? true), - selection: new NodeSelection({ - target: entity, - alias: "this", - }), + selection, }); //TODO: use a hydrate method here const rawProjectionFields = { diff --git a/packages/graphql/src/translate/translate-aggregate.ts b/packages/graphql/src/translate/translate-aggregate.ts index 700e5ece779..ba648549800 100644 --- a/packages/graphql/src/translate/translate-aggregate.ts +++ b/packages/graphql/src/translate/translate-aggregate.ts @@ -17,18 +17,13 @@ * limitations under the License. */ -import Cypher from "@neo4j/cypher-builder"; +import type Cypher from "@neo4j/cypher-builder"; import Debug from "debug"; import type { Node } from "../classes"; import { DEBUG_TRANSLATE } from "../constants"; import type { EntityAdapter } from "../schema-model/entity/EntityAdapter"; -import type { BaseField, GraphQLWhereArg, PrimitiveField, TemporalField } from "../types"; -import { createAuthorizationBeforePredicateField } from "./authorization/create-authorization-before-predicate"; import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { compileCypher } from "../utils/compile-cypher"; -import { createDatetimeElement } from "./projection/elements/create-datetime-element"; import { QueryASTFactory } from "./queryAST/factory/QueryASTFactory"; -import { translateTopLevelMatch } from "./translate-top-level-match"; const debug = Debug(DEBUG_TRANSLATE); @@ -60,145 +55,145 @@ function translateAggregate({ entityAdapter: EntityAdapter; }): Cypher.CypherResult { // TODO: Move fulltext to new translation layer to remove the deprecated translation - if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { - return translateQuery({ context, entityAdapter }); - } - - if (!node) { - throw new Error("Translating Read: Node cannot be on aggregation."); - } - - const { fieldsByTypeName } = context.resolveTree; - const varName = "this"; - let cypherParams: Record = {}; - const cypherStrs: Cypher.Clause[] = []; - const matchNode = new Cypher.NamedNode(varName, { labels: node.getLabels(context) }); - const where = context.resolveTree.args.where as GraphQLWhereArg | undefined; - const topLevelMatch = translateTopLevelMatch({ matchNode, node, context, operation: "AGGREGATE", where }); - cypherStrs.push(new Cypher.RawCypher(topLevelMatch.cypher)); - cypherParams = { ...cypherParams, ...topLevelMatch.params }; - - const selections = fieldsByTypeName[node.aggregateTypeNames.selection] || {}; - const projections: Cypher.Map = new Cypher.Map(); - - // Do auth first so we can throw out before aggregating - Object.entries(selections).forEach((selection) => { - const authField = node.authableFields.find((x) => x.fieldName === selection[0]); - if (authField) { - const authorizationPredicateReturn = createAuthorizationBeforePredicateField({ - context, - nodes: [ - { - variable: new Cypher.NamedNode(varName), - node, - fieldName: authField.fieldName, - }, - ], - // This operation needs to be READ because this will actually return values, unlike the top-level AGGREGATE - operations: ["READ"], - }); - if (authorizationPredicateReturn) { - const { predicate, preComputedSubqueries } = authorizationPredicateReturn; - if (predicate) { - if (preComputedSubqueries && !preComputedSubqueries.empty) { - cypherStrs.push(preComputedSubqueries); - } - cypherStrs.push(new Cypher.With("*").where(predicate)); - } - } - } - }); - - Object.entries(selections).forEach((selection) => { - if (selection[1].name === "count") { - projections.set(`${selection[1].alias || selection[1].name}`, new Cypher.RawCypher(`count(${varName})`)); - } - - const primitiveField = node.primitiveFields.find((x) => x.fieldName === selection[1].name); - const temporalField = node.temporalFields.find((x) => x.fieldName === selection[1].name); - const field: BaseField = (primitiveField as PrimitiveField) || (temporalField as TemporalField); - let isDateTime = false; - const isString = primitiveField && primitiveField.typeMeta.name === "String"; - - if (!primitiveField && temporalField && temporalField.typeMeta.name === "DateTime") { - isDateTime = true; - } - - if (field) { - const thisProjections: Cypher.Expr[] = []; - const aggregateFields = - selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNullable`] || - selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNonNullable`] || - {}; - - Object.entries(aggregateFields).forEach((entry) => { - // "min" | "max" | "average" | "sum" | "shortest" | "longest" - let operator = entry[1].name; - - if (operator === "average") { - operator = "avg"; - } - - if (operator === "shortest") { - operator = "min"; - } - - if (operator === "longest") { - operator = "max"; - } - - const fieldName = field.dbPropertyName || field.fieldName; - - if (isDateTime) { - thisProjections.push( - createDatetimeElement({ - resolveTree: entry[1], - field: field as TemporalField, - variable: new Cypher.NamedVariable(varName), - valueOverride: `${operator}(this.${fieldName})`, - }) - ); - - return; - } - - if (isString) { - const lessOrGreaterThan = entry[1].name === "shortest" ? "<" : ">"; - - const reduce = ` - reduce(aggVar = collect(this.${fieldName})[0], current IN collect(this.${fieldName}) | - CASE - WHEN size(current) ${lessOrGreaterThan} size(aggVar) THEN current - ELSE aggVar - END - ) - `; - - thisProjections.push(new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${reduce}`)); - - return; - } - - thisProjections.push( - new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${operator}(this.${fieldName})`) - ); - }); - - projections.set( - `${selection[1].alias || selection[1].name}`, - Cypher.count(new Cypher.NamedVariable(varName)) - ); - projections.set( - `${selection[1].alias || selection[1].name}`, - new Cypher.RawCypher((env) => `{ ${thisProjections.map((p) => compileCypher(p, env)).join(", ")} }`) - ); - } - }); - - const retSt = new Cypher.Return(projections); - cypherStrs.push(retSt); - - return Cypher.concat(...cypherStrs).build(undefined, cypherParams); + // if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { + return translateQuery({ context, entityAdapter }); + // } + + // if (!node) { + // throw new Error("Translating Read: Node cannot be on aggregation."); + // } + + // const { fieldsByTypeName } = context.resolveTree; + // const varName = "this"; + // let cypherParams: Record = {}; + // const cypherStrs: Cypher.Clause[] = []; + // const matchNode = new Cypher.NamedNode(varName, { labels: node.getLabels(context) }); + // const where = context.resolveTree.args.where as GraphQLWhereArg | undefined; + // const topLevelMatch = translateTopLevelMatch({ matchNode, node, context, operation: "AGGREGATE", where }); + // cypherStrs.push(new Cypher.RawCypher(topLevelMatch.cypher)); + // cypherParams = { ...cypherParams, ...topLevelMatch.params }; + + // const selections = fieldsByTypeName[node.aggregateTypeNames.selection] || {}; + // const projections: Cypher.Map = new Cypher.Map(); + + // // Do auth first so we can throw out before aggregating + // Object.entries(selections).forEach((selection) => { + // const authField = node.authableFields.find((x) => x.fieldName === selection[0]); + // if (authField) { + // const authorizationPredicateReturn = createAuthorizationBeforePredicateField({ + // context, + // nodes: [ + // { + // variable: new Cypher.NamedNode(varName), + // node, + // fieldName: authField.fieldName, + // }, + // ], + // // This operation needs to be READ because this will actually return values, unlike the top-level AGGREGATE + // operations: ["READ"], + // }); + // if (authorizationPredicateReturn) { + // const { predicate, preComputedSubqueries } = authorizationPredicateReturn; + // if (predicate) { + // if (preComputedSubqueries && !preComputedSubqueries.empty) { + // cypherStrs.push(preComputedSubqueries); + // } + // cypherStrs.push(new Cypher.With("*").where(predicate)); + // } + // } + // } + // }); + + // Object.entries(selections).forEach((selection) => { + // if (selection[1].name === "count") { + // projections.set(`${selection[1].alias || selection[1].name}`, new Cypher.RawCypher(`count(${varName})`)); + // } + + // const primitiveField = node.primitiveFields.find((x) => x.fieldName === selection[1].name); + // const temporalField = node.temporalFields.find((x) => x.fieldName === selection[1].name); + // const field: BaseField = (primitiveField as PrimitiveField) || (temporalField as TemporalField); + // let isDateTime = false; + // const isString = primitiveField && primitiveField.typeMeta.name === "String"; + + // if (!primitiveField && temporalField && temporalField.typeMeta.name === "DateTime") { + // isDateTime = true; + // } + + // if (field) { + // const thisProjections: Cypher.Expr[] = []; + // const aggregateFields = + // selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNullable`] || + // selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNonNullable`] || + // {}; + + // Object.entries(aggregateFields).forEach((entry) => { + // // "min" | "max" | "average" | "sum" | "shortest" | "longest" + // let operator = entry[1].name; + + // if (operator === "average") { + // operator = "avg"; + // } + + // if (operator === "shortest") { + // operator = "min"; + // } + + // if (operator === "longest") { + // operator = "max"; + // } + + // const fieldName = field.dbPropertyName || field.fieldName; + + // if (isDateTime) { + // thisProjections.push( + // createDatetimeElement({ + // resolveTree: entry[1], + // field: field as TemporalField, + // variable: new Cypher.NamedVariable(varName), + // valueOverride: `${operator}(this.${fieldName})`, + // }) + // ); + + // return; + // } + + // if (isString) { + // const lessOrGreaterThan = entry[1].name === "shortest" ? "<" : ">"; + + // const reduce = ` + // reduce(aggVar = collect(this.${fieldName})[0], current IN collect(this.${fieldName}) | + // CASE + // WHEN size(current) ${lessOrGreaterThan} size(aggVar) THEN current + // ELSE aggVar + // END + // ) + // `; + + // thisProjections.push(new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${reduce}`)); + + // return; + // } + + // thisProjections.push( + // new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${operator}(this.${fieldName})`) + // ); + // }); + + // projections.set( + // `${selection[1].alias || selection[1].name}`, + // Cypher.count(new Cypher.NamedVariable(varName)) + // ); + // projections.set( + // `${selection[1].alias || selection[1].name}`, + // new Cypher.RawCypher((env) => `{ ${thisProjections.map((p) => compileCypher(p, env)).join(", ")} }`) + // ); + // } + // }); + + // const retSt = new Cypher.Return(projections); + // cypherStrs.push(retSt); + + // return Cypher.concat(...cypherStrs).build(undefined, cypherParams); } export default translateAggregate; diff --git a/packages/graphql/tests/tck/fulltext/aggregate.test.ts b/packages/graphql/tests/tck/fulltext/aggregate.test.ts index 531cc5d2a7b..52cd317823e 100644 --- a/packages/graphql/tests/tck/fulltext/aggregate.test.ts +++ b/packages/graphql/tests/tck/fulltext/aggregate.test.ts @@ -50,9 +50,12 @@ describe("Cypher -> fulltext -> Aggregate", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this - WHERE $param1 IN labels(this) - RETURN { count: count(this) }" + "CALL { + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) + RETURN count(this0) AS var2 + } + RETURN { count: var2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` From b519668f0e8bc4997ac8a2f1c573ad2128c404a1 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Fri, 24 Nov 2023 17:36:57 +0000 Subject: [PATCH 38/72] Remove old aggregations code --- .../src/translate/translate-aggregate.ts | 138 ------------------ .../field-level-aggregations-edge.test.ts | 2 +- .../tests/tck/aggregations/many.test.ts | 2 +- .../relationship_properties/create.test.ts | 2 +- .../graphql/tests/tck/issues/4077.test.ts | 2 +- 5 files changed, 4 insertions(+), 142 deletions(-) diff --git a/packages/graphql/src/translate/translate-aggregate.ts b/packages/graphql/src/translate/translate-aggregate.ts index ba648549800..de879f03377 100644 --- a/packages/graphql/src/translate/translate-aggregate.ts +++ b/packages/graphql/src/translate/translate-aggregate.ts @@ -55,145 +55,7 @@ function translateAggregate({ entityAdapter: EntityAdapter; }): Cypher.CypherResult { // TODO: Move fulltext to new translation layer to remove the deprecated translation - // if (!context.resolveTree.args.fulltext && !context.resolveTree.args.phrase) { return translateQuery({ context, entityAdapter }); - // } - - // if (!node) { - // throw new Error("Translating Read: Node cannot be on aggregation."); - // } - - // const { fieldsByTypeName } = context.resolveTree; - // const varName = "this"; - // let cypherParams: Record = {}; - // const cypherStrs: Cypher.Clause[] = []; - // const matchNode = new Cypher.NamedNode(varName, { labels: node.getLabels(context) }); - // const where = context.resolveTree.args.where as GraphQLWhereArg | undefined; - // const topLevelMatch = translateTopLevelMatch({ matchNode, node, context, operation: "AGGREGATE", where }); - // cypherStrs.push(new Cypher.RawCypher(topLevelMatch.cypher)); - // cypherParams = { ...cypherParams, ...topLevelMatch.params }; - - // const selections = fieldsByTypeName[node.aggregateTypeNames.selection] || {}; - // const projections: Cypher.Map = new Cypher.Map(); - - // // Do auth first so we can throw out before aggregating - // Object.entries(selections).forEach((selection) => { - // const authField = node.authableFields.find((x) => x.fieldName === selection[0]); - // if (authField) { - // const authorizationPredicateReturn = createAuthorizationBeforePredicateField({ - // context, - // nodes: [ - // { - // variable: new Cypher.NamedNode(varName), - // node, - // fieldName: authField.fieldName, - // }, - // ], - // // This operation needs to be READ because this will actually return values, unlike the top-level AGGREGATE - // operations: ["READ"], - // }); - // if (authorizationPredicateReturn) { - // const { predicate, preComputedSubqueries } = authorizationPredicateReturn; - // if (predicate) { - // if (preComputedSubqueries && !preComputedSubqueries.empty) { - // cypherStrs.push(preComputedSubqueries); - // } - // cypherStrs.push(new Cypher.With("*").where(predicate)); - // } - // } - // } - // }); - - // Object.entries(selections).forEach((selection) => { - // if (selection[1].name === "count") { - // projections.set(`${selection[1].alias || selection[1].name}`, new Cypher.RawCypher(`count(${varName})`)); - // } - - // const primitiveField = node.primitiveFields.find((x) => x.fieldName === selection[1].name); - // const temporalField = node.temporalFields.find((x) => x.fieldName === selection[1].name); - // const field: BaseField = (primitiveField as PrimitiveField) || (temporalField as TemporalField); - // let isDateTime = false; - // const isString = primitiveField && primitiveField.typeMeta.name === "String"; - - // if (!primitiveField && temporalField && temporalField.typeMeta.name === "DateTime") { - // isDateTime = true; - // } - - // if (field) { - // const thisProjections: Cypher.Expr[] = []; - // const aggregateFields = - // selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNullable`] || - // selection[1].fieldsByTypeName[`${field.typeMeta.name}AggregateSelectionNonNullable`] || - // {}; - - // Object.entries(aggregateFields).forEach((entry) => { - // // "min" | "max" | "average" | "sum" | "shortest" | "longest" - // let operator = entry[1].name; - - // if (operator === "average") { - // operator = "avg"; - // } - - // if (operator === "shortest") { - // operator = "min"; - // } - - // if (operator === "longest") { - // operator = "max"; - // } - - // const fieldName = field.dbPropertyName || field.fieldName; - - // if (isDateTime) { - // thisProjections.push( - // createDatetimeElement({ - // resolveTree: entry[1], - // field: field as TemporalField, - // variable: new Cypher.NamedVariable(varName), - // valueOverride: `${operator}(this.${fieldName})`, - // }) - // ); - - // return; - // } - - // if (isString) { - // const lessOrGreaterThan = entry[1].name === "shortest" ? "<" : ">"; - - // const reduce = ` - // reduce(aggVar = collect(this.${fieldName})[0], current IN collect(this.${fieldName}) | - // CASE - // WHEN size(current) ${lessOrGreaterThan} size(aggVar) THEN current - // ELSE aggVar - // END - // ) - // `; - - // thisProjections.push(new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${reduce}`)); - - // return; - // } - - // thisProjections.push( - // new Cypher.RawCypher(`${entry[1].alias || entry[1].name}: ${operator}(this.${fieldName})`) - // ); - // }); - - // projections.set( - // `${selection[1].alias || selection[1].name}`, - // Cypher.count(new Cypher.NamedVariable(varName)) - // ); - // projections.set( - // `${selection[1].alias || selection[1].name}`, - // new Cypher.RawCypher((env) => `{ ${thisProjections.map((p) => compileCypher(p, env)).join(", ")} }`) - // ); - // } - // }); - - // const retSt = new Cypher.Return(projections); - // cypherStrs.push(retSt); - - // return Cypher.concat(...cypherStrs).build(undefined, cypherParams); } export default translateAggregate; diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts index 72a629090f4..05d084b1b77 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts @@ -49,7 +49,7 @@ describe("Field Level Aggregations", () => { }); }); - test.only("Edge Int Aggregations", async () => { + test("Edge Int Aggregations", async () => { const query = gql` query { movies { diff --git a/packages/graphql/tests/tck/aggregations/many.test.ts b/packages/graphql/tests/tck/aggregations/many.test.ts index e1affab85db..85b1428a853 100644 --- a/packages/graphql/tests/tck/aggregations/many.test.ts +++ b/packages/graphql/tests/tck/aggregations/many.test.ts @@ -41,7 +41,7 @@ describe("Cypher Aggregations Many", () => { }); }); - test.only("Min", async () => { + test("Min", async () => { const query = gql` { moviesAggregate { diff --git a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts index f51fca6ee0d..a7b01afb791 100644 --- a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts +++ b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts @@ -48,7 +48,7 @@ describe("Relationship Properties Create Cypher", () => { }); }); - test.only("Create movie with a relationship that has properties", async () => { + test("Create movie with a relationship that has properties", async () => { const query = gql` mutation { createMovies( diff --git a/packages/graphql/tests/tck/issues/4077.test.ts b/packages/graphql/tests/tck/issues/4077.test.ts index 8a66f00d1c9..19874cbff4c 100644 --- a/packages/graphql/tests/tck/issues/4077.test.ts +++ b/packages/graphql/tests/tck/issues/4077.test.ts @@ -148,7 +148,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { `); }); - test.only("wrap authenticated subquery on nested read operation", async () => { + test("wrap authenticated subquery on nested read operation", async () => { const query = gql` query { videos { From f5bf6576f014befac0164af9a12f1d7ad8aa59a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 25 Nov 2023 02:02:58 +0000 Subject: [PATCH 39/72] chore(deps): update dependency @types/node to v20.10.0 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 5746b0bbd33..2d0a7de5e2f 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.9.5", + "@types/node": "20.10.0", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index eaca969b98c..b67ae6e4b8a 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.9.5", + "@types/node": "20.10.0", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 2a892b325e1..0d4b0862bc3 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.9.5", + "@types/node": "20.10.0", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 4584eab9d23..30147ceff51 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.9.5", + "@types/node": "20.10.0", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index 2e317206df9..151d45ddb2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3574,7 +3574,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.9.5 + "@types/node": 20.10.0 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3603,7 +3603,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.9.5 + "@types/node": 20.10.0 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3710,7 +3710,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.9.5 + "@types/node": 20.10.0 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3768,7 +3768,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.9.5 + "@types/node": 20.10.0 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7675,12 +7675,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.9.5": - version: 20.9.5 - resolution: "@types/node@npm:20.9.5" +"@types/node@npm:20.10.0": + version: 20.10.0 + resolution: "@types/node@npm:20.10.0" dependencies: undici-types: ~5.26.4 - checksum: 050ebd958e31ad21143f93614a25b15a8de5d0004338359409f76a661f734c4549698d852f21176c62f5661dacb572a090669cffef93cf6361c959e4d27d8d95 + checksum: face395140d6f2f1755b91fdd3b697cf56aeb9e2514529ce88d56e56f261ad65be7269d863520a9406d73c338699ea68b418e8677584de0c1efeed09539b6f97 languageName: node linkType: hard From e30ebd97ffbcad5826269b9f025e05e0ee1e20af Mon Sep 17 00:00:00 2001 From: angrykoala Date: Mon, 27 Nov 2023 12:35:36 +0000 Subject: [PATCH 40/72] Add license headers --- .../queryAST/ast/selection/EntitySelection.ts | 19 +++++++++++++++++++ .../ast/selection/FulltextSelection.ts | 19 +++++++++++++++++++ .../queryAST/ast/selection/NodeSelection.ts | 19 +++++++++++++++++++ .../ast/selection/RelationshipSelection.ts | 19 +++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts index fc2e79a0088..df06591b7ec 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/EntitySelection.ts @@ -1,3 +1,22 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import type Cypher from "@neo4j/cypher-builder"; import type { QueryASTContext } from "../QueryASTContext"; import { QueryASTNode } from "../QueryASTNode"; diff --git a/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts index cd518e98349..42f8de86865 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts @@ -1,3 +1,22 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import Cypher from "@neo4j/cypher-builder"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { mapLabelsWithContext } from "../../../../schema-model/utils/map-labels-with-context"; diff --git a/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts index fdd51bdcd46..23c3eb5c866 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/NodeSelection.ts @@ -1,3 +1,22 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import Cypher from "@neo4j/cypher-builder"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { createNodeFromEntity } from "../../utils/create-node-from-entity"; diff --git a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts index 2b939dd109c..991a80044d2 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts @@ -1,3 +1,22 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import Cypher from "@neo4j/cypher-builder"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; From decbd2484c68280759ba3cf0724a468f5e89f3ab Mon Sep 17 00:00:00 2001 From: angrykoala Date: Mon, 27 Nov 2023 12:38:56 +0000 Subject: [PATCH 41/72] Remove commented code --- .../src/translate/queryAST/factory/OperationFactory.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index 334fa3e6119..9e6a5abb308 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -266,10 +266,6 @@ export class OperationsFactory { }); } - // if(entityOrRel instanceof EntityAdapter){ - - // } - const operation = new ReadOperation({ target: entity, relationship, From 900083d9a01e0179f7aef0d22c6391fede34ebb5 Mon Sep 17 00:00:00 2001 From: angrykoala Date: Mon, 27 Nov 2023 16:31:31 +0000 Subject: [PATCH 42/72] Apply suggestions from code review Co-authored-by: MacondoExpress --- .../src/translate/queryAST/ast/operations/ReadOperation.ts | 2 -- packages/graphql/src/translate/translate-create.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index 9709fd597bd..e862c760dd3 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -235,7 +235,6 @@ export class ReadOperation extends Operation { nestedContext = context; } - // const node = createNodeFromEntity(this.target, nestedContext.neo4jGraphQLContext, this.nodeAlias); const preWith: Cypher.With | undefined = isCreateSelection && context.target ? new Cypher.With([context.target, nestedContext.target]) : undefined; @@ -264,7 +263,6 @@ export class ReadOperation extends Operation { extraMatches = [matchClause, ...extraMatches]; matchClause = new Cypher.With("*"); } - // const { preSelection, selectionClause: _matchClause2 } = this.getSelectionClauses(nestedContext, node); let filterSubqueryWith: Cypher.With | undefined; let filterSubqueriesClause: Cypher.Clause | undefined = undefined; diff --git a/packages/graphql/src/translate/translate-create.ts b/packages/graphql/src/translate/translate-create.ts index af668751851..51ec0d2b5e3 100644 --- a/packages/graphql/src/translate/translate-create.ts +++ b/packages/graphql/src/translate/translate-create.ts @@ -126,7 +126,6 @@ export default async function translateCreate({ ); const queryASTEnv = new QueryASTEnv(); const projectedVariables: Cypher.Node[] = []; - console.log(queryAST); /** * Currently, the create projections are resolved separately for each input, * the following block reuses the same ReadOperation for each of the variable names generated during the create operations. From b2de523d9e8469171b2a8cdcca333c2732c6a2da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:13:46 +0000 Subject: [PATCH 43/72] fix(deps): update codemirror --- packages/graphql-toolbox/package.json | 6 +++--- yarn.lock | 30 +++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index bedccb9c02d..36b72e5257f 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -42,10 +42,10 @@ }, "author": "Neo4j", "dependencies": { - "@codemirror/autocomplete": "6.11.0", - "@codemirror/commands": "6.3.0", + "@codemirror/autocomplete": "6.11.1", + "@codemirror/commands": "6.3.1", "@codemirror/lang-javascript": "6.2.1", - "@codemirror/language": "6.9.2", + "@codemirror/language": "6.9.3", "@dnd-kit/core": "6.1.0", "@dnd-kit/modifiers": "7.0.0", "@dnd-kit/sortable": "8.0.0", diff --git a/yarn.lock b/yarn.lock index 151d45ddb2d..2fa1e0c24bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1702,9 +1702,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/autocomplete@npm:6.11.0": - version: 6.11.0 - resolution: "@codemirror/autocomplete@npm:6.11.0" +"@codemirror/autocomplete@npm:6.11.1": + version: 6.11.1 + resolution: "@codemirror/autocomplete@npm:6.11.1" dependencies: "@codemirror/language": ^6.0.0 "@codemirror/state": ^6.0.0 @@ -1715,7 +1715,7 @@ __metadata: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.0.0 - checksum: f80ac2c49b3736bdcce8d16776c09bfa3fe85a3ce486bfb96aa07157aff9e7afb3f96575a1d83cee96a97439c0ef0039325901afb811a9f37734ea0a1b965c82 + checksum: 69cb77d51dbc4c76a990fb8e562075d6fa11b2aef00fce33d2a98dd701f6a89050b1b464ae8ee1e2cbe1a4210522b1a3c2260cdf5c933a062093acaf98a5eedc languageName: node linkType: hard @@ -1736,15 +1736,15 @@ __metadata: languageName: node linkType: hard -"@codemirror/commands@npm:6.3.0": - version: 6.3.0 - resolution: "@codemirror/commands@npm:6.3.0" +"@codemirror/commands@npm:6.3.1": + version: 6.3.1 + resolution: "@codemirror/commands@npm:6.3.1" dependencies: "@codemirror/language": ^6.0.0 "@codemirror/state": ^6.2.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.1.0 - checksum: d6ade0ba7d4f80c2e44163935783d2f2f35c8b641a4b4f62452c0630211670abe5093786cf5a4af14147102d4284dae660a26f3ae58fd840e838685a81107d11 + checksum: b0798b6cee0f37902ab4df80c9d28bfc999fa97bf9072b2972b83a036193e4dcb5db6da7fa8f71c4df98d629afff14e03153f5166617680cb917a3334417e932 languageName: node linkType: hard @@ -1775,9 +1775,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/language@npm:6.9.2": - version: 6.9.2 - resolution: "@codemirror/language@npm:6.9.2" +"@codemirror/language@npm:6.9.3": + version: 6.9.3 + resolution: "@codemirror/language@npm:6.9.3" dependencies: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 @@ -1785,7 +1785,7 @@ __metadata: "@lezer/highlight": ^1.0.0 "@lezer/lr": ^1.0.0 style-mod: ^4.0.0 - checksum: eee7b861b5591114cac7502cd532d5b923639740081a4cd7e28696c252af8d759b14686aaf6d5eee7e0969ff647b7aaf03a5eea7235fb6d9858ee19433f1c74d + checksum: 774a40bc91c748d418a9a774161a5b083061124e4439bb753072bc657ec4c4784f595161c10c7c3935154b22291bf6dc74c9abe827033db32e217ac3963478f3 languageName: node linkType: hard @@ -3626,10 +3626,10 @@ __metadata: version: 0.0.0-use.local resolution: "@neo4j/graphql-toolbox@workspace:packages/graphql-toolbox" dependencies: - "@codemirror/autocomplete": 6.11.0 - "@codemirror/commands": 6.3.0 + "@codemirror/autocomplete": 6.11.1 + "@codemirror/commands": 6.3.1 "@codemirror/lang-javascript": 6.2.1 - "@codemirror/language": 6.9.2 + "@codemirror/language": 6.9.3 "@dnd-kit/core": 6.1.0 "@dnd-kit/modifiers": 7.0.0 "@dnd-kit/sortable": 8.0.0 From 2955818537dc0515eb531daeee3c3406d9e2e138 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:23:06 +0000 Subject: [PATCH 44/72] fix(deps): update dependency @neo4j-ndl/react to v2.0.13 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index 36b72e5257f..f71b2afb1cb 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -51,7 +51,7 @@ "@dnd-kit/sortable": "8.0.0", "@graphiql/react": "0.20.2", "@neo4j-ndl/base": "2.0.7", - "@neo4j-ndl/react": "2.0.12", + "@neo4j-ndl/react": "2.0.13", "@neo4j/graphql": "4.4.3", "@neo4j/introspector": "2.0.0", "classnames": "2.3.2", diff --git a/yarn.lock b/yarn.lock index 2fa1e0c24bc..dff561ed977 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3525,9 +3525,9 @@ __metadata: languageName: node linkType: hard -"@neo4j-ndl/react@npm:2.0.12": - version: 2.0.12 - resolution: "@neo4j-ndl/react@npm:2.0.12" +"@neo4j-ndl/react@npm:2.0.13": + version: 2.0.13 + resolution: "@neo4j-ndl/react@npm:2.0.13" dependencies: "@floating-ui/react": 0.25.1 "@heroicons/react": 2.0.13 @@ -3552,7 +3552,7 @@ __metadata: peerDependencies: "@heroicons/react": 2.0.13 react: ">=16.8.0" - checksum: f6b987e49253bb5997d8859f2094a02a6c64f67110f3948b3adf3b9c6ae2118cf8cad9fff0f39e9be0ea87cdcddde1c2763b4362d1c4cb2444aea1697e8701ea + checksum: 6082f00eae397d2d362cefb1ca0e3f8c4cf1dda78978732226900adc2cf312a2fa72caa2c8c49020c5efb4e9610ac443ee97e8a5d15aca8a7a37a2ac0dec2b3f languageName: node linkType: hard @@ -3635,7 +3635,7 @@ __metadata: "@dnd-kit/sortable": 8.0.0 "@graphiql/react": 0.20.2 "@neo4j-ndl/base": 2.0.7 - "@neo4j-ndl/react": 2.0.12 + "@neo4j-ndl/react": 2.0.13 "@neo4j/graphql": 4.4.3 "@neo4j/introspector": 2.0.0 "@playwright/test": 1.40.0 From 35d78c046f64116c23564c49bcc615c2a089e3a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 01:06:26 +0000 Subject: [PATCH 45/72] fix(deps): update dependency zustand to v4.4.7 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index f71b2afb1cb..143d0f02ae7 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -68,7 +68,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "thememirror": "2.0.1", - "zustand": "4.4.6" + "zustand": "4.4.7" }, "devDependencies": { "@playwright/test": "1.40.0", diff --git a/yarn.lock b/yarn.lock index dff561ed977..e66c1836662 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3689,7 +3689,7 @@ __metadata: webpack-cli: 5.1.4 webpack-dev-server: 4.15.1 webpack-notifier: 1.15.0 - zustand: 4.4.6 + zustand: 4.4.7 languageName: unknown linkType: soft @@ -27102,9 +27102,9 @@ __metadata: languageName: unknown linkType: soft -"zustand@npm:4.4.6": - version: 4.4.6 - resolution: "zustand@npm:4.4.6" +"zustand@npm:4.4.7": + version: 4.4.7 + resolution: "zustand@npm:4.4.7" dependencies: use-sync-external-store: 1.2.0 peerDependencies: @@ -27118,6 +27118,6 @@ __metadata: optional: true react: optional: true - checksum: da7b00cc6dbe5cf5fc2e3fbca745317da4bbaf53bf4a6909bbd3e335242704df9689027f613461aff07eb5f672d5570bc1a2ef99d0ad7bc868920a3b331613d4 + checksum: 9aeb6cc86162296c1dac504b8906ff952252c129fb3f05cc8926a7f5c9d7fbe098571d5161b3efe3347c0ee1d80197f787b768c7522721864f7323c28766717e languageName: node linkType: hard From 116c4b277d068cbea59c843d5b682f81c157a5b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 01:15:58 +0000 Subject: [PATCH 46/72] chore(deps): update dependency @apollo/gateway to v2.6.1 --- packages/graphql/package.json | 2 +- yarn.lock | 58 +++++++++++++++++------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index b67ae6e4b8a..a455a0f08b6 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -49,7 +49,7 @@ }, "author": "Neo4j Inc.", "devDependencies": { - "@apollo/gateway": "2.5.7", + "@apollo/gateway": "2.6.1", "@apollo/server": "4.9.5", "@faker-js/faker": "8.3.1", "@types/deep-equal": "1.0.4", diff --git a/yarn.lock b/yarn.lock index e66c1836662..05f1990b150 100644 --- a/yarn.lock +++ b/yarn.lock @@ -99,15 +99,15 @@ __metadata: languageName: node linkType: hard -"@apollo/composition@npm:2.5.7": - version: 2.5.7 - resolution: "@apollo/composition@npm:2.5.7" +"@apollo/composition@npm:2.6.1": + version: 2.6.1 + resolution: "@apollo/composition@npm:2.6.1" dependencies: - "@apollo/federation-internals": 2.5.7 - "@apollo/query-graphs": 2.5.7 + "@apollo/federation-internals": 2.6.1 + "@apollo/query-graphs": 2.6.1 peerDependencies: graphql: ^16.5.0 - checksum: e5860b93389b57a934711578863ad3150133176c6014a33c30decc9bcfe086952bc1c6339b5e45b2f0f66a9ade5ffc2ef7ba2ef1641caf8abf043a93a37a88c9 + checksum: 3332842e7b320457bedc7d6e99ee76ca16595e8d531cdec33a3deaf53a06932900edea839ec92d9a8b314caff8a5b4021efa21e14c98a291a5da84ee6b5530ca languageName: node linkType: hard @@ -125,9 +125,9 @@ __metadata: languageName: node linkType: hard -"@apollo/federation-internals@npm:2.5.7": - version: 2.5.7 - resolution: "@apollo/federation-internals@npm:2.5.7" +"@apollo/federation-internals@npm:2.6.1": + version: 2.6.1 + resolution: "@apollo/federation-internals@npm:2.6.1" dependencies: "@types/uuid": ^9.0.0 chalk: ^4.1.0 @@ -135,7 +135,7 @@ __metadata: uuid: ^9.0.0 peerDependencies: graphql: ^16.5.0 - checksum: 5ad7da7f4b99aa577f992c3fa61b9e0b686ad94e767fd56299d2ea5f789879c661d1b454b960020a0c06bc2de9e71b27e611e6d8fa5d9e11a775ec844ffa63e7 + checksum: af7293ef180ba2ec5f6300d37ac4240f3cdd8aa966a7cae0e175e52835274f6c89b110b3ab55b9494b380ea02261e0e997be806cea4e4c7ec3a2a0f7b591f900 languageName: node linkType: hard @@ -168,13 +168,13 @@ __metadata: languageName: node linkType: hard -"@apollo/gateway@npm:2.5.7": - version: 2.5.7 - resolution: "@apollo/gateway@npm:2.5.7" +"@apollo/gateway@npm:2.6.1": + version: 2.6.1 + resolution: "@apollo/gateway@npm:2.6.1" dependencies: - "@apollo/composition": 2.5.7 - "@apollo/federation-internals": 2.5.7 - "@apollo/query-planner": 2.5.7 + "@apollo/composition": 2.6.1 + "@apollo/federation-internals": 2.6.1 + "@apollo/query-planner": 2.6.1 "@apollo/server-gateway-interface": ^1.1.0 "@apollo/usage-reporting-protobuf": ^4.1.0 "@apollo/utils.createhash": ^2.0.0 @@ -192,7 +192,7 @@ __metadata: node-fetch: ^2.6.7 peerDependencies: graphql: ^16.5.0 - checksum: ecedac02bfbecec4d7f493dd2334334feb0afd99456ffe53017c8afb6ff82ccbcf071e17eff718eb4fe62fca3df41047607135547e115c52ba04f1fa5edf208d + checksum: 01bfc84b7344db334efcc98fc18b9c0ea16389b8051449177c4fab1b746e76b9a7d67ddbf9fc08c48f42732c0579ca4a731272aa68b9bdc0ad839b2d74c1f3d3 languageName: node linkType: hard @@ -219,33 +219,33 @@ __metadata: languageName: node linkType: hard -"@apollo/query-graphs@npm:2.5.7": - version: 2.5.7 - resolution: "@apollo/query-graphs@npm:2.5.7" +"@apollo/query-graphs@npm:2.6.1": + version: 2.6.1 + resolution: "@apollo/query-graphs@npm:2.6.1" dependencies: - "@apollo/federation-internals": 2.5.7 + "@apollo/federation-internals": 2.6.1 deep-equal: ^2.0.5 ts-graphviz: ^1.5.4 uuid: ^9.0.0 peerDependencies: graphql: ^16.5.0 - checksum: 5f5bae32613ba408f88eaf210db649b6c11370fd7556aadb33818ee2290b28fe3ae5ce259aac5bcad7015cabf8ea73c6e90b257e9e07ebc4e381b5e00f0d8eee + checksum: 932acd852c10e2dad8e67902e302602ecaeee5fd1bc98fe4c58bec45fadb20b7a5ba7384f33374e54c08c3e935254b3c88275b6bec9186c378fb89aa578a4853 languageName: node linkType: hard -"@apollo/query-planner@npm:2.5.7": - version: 2.5.7 - resolution: "@apollo/query-planner@npm:2.5.7" +"@apollo/query-planner@npm:2.6.1": + version: 2.6.1 + resolution: "@apollo/query-planner@npm:2.6.1" dependencies: - "@apollo/federation-internals": 2.5.7 - "@apollo/query-graphs": 2.5.7 + "@apollo/federation-internals": 2.6.1 + "@apollo/query-graphs": 2.6.1 "@apollo/utils.keyvaluecache": ^2.1.0 chalk: ^4.1.0 deep-equal: ^2.0.5 pretty-format: ^29.0.0 peerDependencies: graphql: ^16.5.0 - checksum: 3c8950f2540f5e1fae58ac25d889683e201867eaf3124dd336c78bb727c06eb650cae5d9fd9a504be97d346c61408998424a156c37c22e00b45d13146ff3014b + checksum: 14c8cf1447241d6b2273be8de12c54e45cb963d49807ac9af4ac906e2edb70e8c962ae283c766700333cef56d14dcfaca8741d60ea2070d994fb1a8f50023f43 languageName: node linkType: hard @@ -3697,7 +3697,7 @@ __metadata: version: 0.0.0-use.local resolution: "@neo4j/graphql@workspace:packages/graphql" dependencies: - "@apollo/gateway": 2.5.7 + "@apollo/gateway": 2.6.1 "@apollo/server": 4.9.5 "@apollo/subgraph": ^2.2.3 "@faker-js/faker": 8.3.1 From a069a1985770e3348716bb56e4b383eb0689541b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 04:44:03 +0000 Subject: [PATCH 47/72] chore(deps): update dependency nock to v13.4.0 --- packages/graphql/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index a455a0f08b6..68935826d2a 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -75,7 +75,7 @@ "koa-router": "12.0.1", "libnpmsearch": "7.0.0", "mock-jwks": "1.0.10", - "nock": "13.3.8", + "nock": "13.4.0", "npm-run-all": "4.1.5", "randomstring": "1.3.0", "rimraf": "5.0.5", diff --git a/yarn.lock b/yarn.lock index 05f1990b150..f282ad792f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3737,7 +3737,7 @@ __metadata: koa-router: 12.0.1 libnpmsearch: 7.0.0 mock-jwks: 1.0.10 - nock: 13.3.8 + nock: 13.4.0 npm-run-all: 4.1.5 pluralize: ^8.0.0 randomstring: 1.3.0 @@ -19920,14 +19920,14 @@ __metadata: languageName: node linkType: hard -"nock@npm:13.3.8": - version: 13.3.8 - resolution: "nock@npm:13.3.8" +"nock@npm:13.4.0": + version: 13.4.0 + resolution: "nock@npm:13.4.0" dependencies: debug: ^4.1.0 json-stringify-safe: ^5.0.1 propagate: ^2.0.0 - checksum: 98f7d9d1c6b4fad560d7f1033705f9a0318e288060c10e36973d1798d6c824fee1f23a9ecbb1118bf70068f58bb04eaa50c5d046f5cf0ceaf4a2dc76fe7a82b2 + checksum: 30c3751854f9c412df5f99e01eeaef25b2583d3cae80b8c46524acb39d8b7fa61043603472ad94a3adc4b7d1e0f3098e6bb06e787734cbfbde2751891115b311 languageName: node linkType: hard From feaf27c7aca0c5188d40b6df491651ccba0dff3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 04:53:13 +0000 Subject: [PATCH 48/72] chore(deps): update dependency @apollo/federation-subgraph-compatibility to v2.1.0 --- .../package.json | 2 +- yarn.lock | 40 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/apollo-federation-subgraph-compatibility/package.json b/packages/apollo-federation-subgraph-compatibility/package.json index 91ff474aa1d..bf247a48ac8 100644 --- a/packages/apollo-federation-subgraph-compatibility/package.json +++ b/packages/apollo-federation-subgraph-compatibility/package.json @@ -16,7 +16,7 @@ "neo4j-driver": "^5.8.0" }, "devDependencies": { - "@apollo/federation-subgraph-compatibility": "2.0.1", + "@apollo/federation-subgraph-compatibility": "2.1.0", "fork-ts-checker-webpack-plugin": "9.0.2", "ts-loader": "9.5.1", "tsconfig-paths-webpack-plugin": "4.1.0", diff --git a/yarn.lock b/yarn.lock index f282ad792f3..4d4ee22c216 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,32 +139,33 @@ __metadata: languageName: node linkType: hard -"@apollo/federation-subgraph-compatibility-tests@npm:2.0.1": - version: 2.0.1 - resolution: "@apollo/federation-subgraph-compatibility-tests@npm:2.0.1" +"@apollo/federation-subgraph-compatibility-tests@npm:2.1.0": + version: 2.1.0 + resolution: "@apollo/federation-subgraph-compatibility-tests@npm:2.1.0" dependencies: - "@apollo/rover": ^0.19.1 + "@apollo/rover": ^0.21.0 debug: ^4.3.4 execa: ^5.1.1 graphql: ^16.8.0 jest: ^29.6.4 make-fetch-happen: ^13.0.0 + mustache: ^4.2.0 pm2: ^5.3.0 ts-jest: ^29.1.1 - checksum: d5783c5e0c029783ea196c11ac6cff5cfbf5d42c167aa7a00c6dd31ac4e87cf28baca744ae82adc370c5505ae1360a4fb975474dbfd141a7bae52b2d86a50e76 + checksum: 10eaa73a2f8726e5a96e2270ed169586e68b1c8c38295602b5f997ed4b4a3d4de32c78bf7d219347b1ef534a3c80e3929026dea0d8723e9ca1f1670d925564c2 languageName: node linkType: hard -"@apollo/federation-subgraph-compatibility@npm:2.0.1": - version: 2.0.1 - resolution: "@apollo/federation-subgraph-compatibility@npm:2.0.1" +"@apollo/federation-subgraph-compatibility@npm:2.1.0": + version: 2.1.0 + resolution: "@apollo/federation-subgraph-compatibility@npm:2.1.0" dependencies: - "@apollo/federation-subgraph-compatibility-tests": 2.0.1 + "@apollo/federation-subgraph-compatibility-tests": 2.1.0 commander: ^11.0.0 debug: ^4.3.4 bin: fedtest: dist/compatibilityTestCommand.js - checksum: 9a6c1d06c6e58745f041bf131efa6704972f0c5935577825321b83565443df38b5830a9d9e265ba2a447bea6a8c57b6f2eeac7163031ea3f1ff7f904a141bf86 + checksum: 7f9bea9f22fd31093bdd1fa930de966128e9436820754b92cb3a25445579b29d91e8bd85968fc85f1a354a77d528746e3a186f771577fd1ee33f736a18ae4f9f languageName: node linkType: hard @@ -249,9 +250,9 @@ __metadata: languageName: node linkType: hard -"@apollo/rover@npm:^0.19.1": - version: 0.19.1 - resolution: "@apollo/rover@npm:0.19.1" +"@apollo/rover@npm:^0.21.0": + version: 0.21.0 + resolution: "@apollo/rover@npm:0.21.0" dependencies: axios-proxy-builder: ^0.1.1 binary-install: ^1.0.6 @@ -259,7 +260,7 @@ __metadata: detect-libc: ^2.0.0 bin: rover: run.js - checksum: 35da5ceaeec832030bf5b425aa6127771b594a64fa70e26d50139fe8000291f76d1781df23ee5cf46b8579c5480f2b5992c90db6122d697331c371c88b30a304 + checksum: e96fae284559d012ddeb823713649a9f0975051cac5a5305c998b288d0eac2b871129d10d455f0f5b7f135387df54e844f33c0dafd6499de90e80e9f7d99ea01 languageName: node linkType: hard @@ -8869,7 +8870,7 @@ __metadata: version: 0.0.0-use.local resolution: "apollo-federation-subgraph-compatibility@workspace:packages/apollo-federation-subgraph-compatibility" dependencies: - "@apollo/federation-subgraph-compatibility": 2.0.1 + "@apollo/federation-subgraph-compatibility": 2.1.0 "@apollo/server": ^4.7.0 "@graphql-tools/wrap": ^10.0.0 "@neo4j/graphql": ^4.4.3 @@ -19535,6 +19536,15 @@ __metadata: languageName: node linkType: hard +"mustache@npm:^4.2.0": + version: 4.2.0 + resolution: "mustache@npm:4.2.0" + bin: + mustache: bin/mustache + checksum: 928fcb63e3aa44a562bfe9b59ba202cccbe40a46da50be6f0dd831b495be1dd7e38ca4657f0ecab2c1a89dc7bccba0885eab7ee7c1b215830da765758c7e0506 + languageName: node + linkType: hard + "mute-stream@npm:0.0.7": version: 0.0.7 resolution: "mute-stream@npm:0.0.7" From 9fda1f1a4b2f44e4aa53c8b3c1117ab3995c0b49 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 22:43:46 +0000 Subject: [PATCH 49/72] chore(deps): update dependency @playwright/test to v1.40.1 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 30 +++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index 143d0f02ae7..d9330064b6d 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -71,7 +71,7 @@ "zustand": "4.4.7" }, "devDependencies": { - "@playwright/test": "1.40.0", + "@playwright/test": "1.40.1", "@tsconfig/create-react-app": "2.0.1", "@types/codemirror": "5.60.15", "@types/lodash.debounce": "4.0.9", diff --git a/yarn.lock b/yarn.lock index 4d4ee22c216..cfc6eada696 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3639,7 +3639,7 @@ __metadata: "@neo4j-ndl/react": 2.0.13 "@neo4j/graphql": 4.4.3 "@neo4j/introspector": 2.0.0 - "@playwright/test": 1.40.0 + "@playwright/test": 1.40.1 "@tsconfig/create-react-app": 2.0.1 "@types/codemirror": 5.60.15 "@types/lodash.debounce": 4.0.9 @@ -4755,14 +4755,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:1.40.0": - version: 1.40.0 - resolution: "@playwright/test@npm:1.40.0" +"@playwright/test@npm:1.40.1": + version: 1.40.1 + resolution: "@playwright/test@npm:1.40.1" dependencies: - playwright: 1.40.0 + playwright: 1.40.1 bin: playwright: cli.js - checksum: 128f05978f9f5a557f0b7924ec134d43cb70c78d74bc3bf7b18576f00e72399100ddf1f4a139e05ea8275407d8e27be0203ac34f514319a2cbeb01eaf0be5be4 + checksum: ae094e6cb809365c0707ee2b184e42d2a2542569ada020d2d44ca5866066941262bd9a67af185f86c2fb0133c9b712ea8cb73e2959a289e4261c5fd17077283c languageName: node linkType: hard @@ -21379,27 +21379,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.40.0": - version: 1.40.0 - resolution: "playwright-core@npm:1.40.0" +"playwright-core@npm:1.40.1": + version: 1.40.1 + resolution: "playwright-core@npm:1.40.1" bin: playwright-core: cli.js - checksum: 57de5c91a4c404b120ed2af8541b21cdedcbc4f27477341157666d356bbee3b3fab8e61d020f0f450708fa2e8f6dc244b9224cb1985d5426e609cebed15af095 + checksum: 84d92fb9b86e3c225b16b6886bf858eb5059b4e60fa1205ff23336e56a06dcb2eac62650992dede72f406c8e70a7b6a5303e511f9b4bc0b85022ede356a01ee0 languageName: node linkType: hard -"playwright@npm:1.40.0": - version: 1.40.0 - resolution: "playwright@npm:1.40.0" +"playwright@npm:1.40.1": + version: 1.40.1 + resolution: "playwright@npm:1.40.1" dependencies: fsevents: 2.3.2 - playwright-core: 1.40.0 + playwright-core: 1.40.1 dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 7ba49e5376a6cfd1d32048dbdb2fd38e09182aa2e4619fdb23d3e6530fa6987f2f3fd34ad1d9d906fb4ec2da69ee7536eeb881982d60750fde809183caa607fc + checksum: 9e36791c1b4a649c104aa365fdd9d049924eeb518c5967c0e921aa38b9b00994aa6ee54784d6c2af194b3b494b6f69772673081ef53c6c4a4b2065af9955c4ba languageName: node linkType: hard From fbf0799d304d7a0d5e63d04f99bb9efe0c40fc34 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 22:53:12 +0000 Subject: [PATCH 50/72] fix(deps): update dependency @codemirror/commands to v6.3.2 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index d9330064b6d..6c9933e7eea 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -43,7 +43,7 @@ "author": "Neo4j", "dependencies": { "@codemirror/autocomplete": "6.11.1", - "@codemirror/commands": "6.3.1", + "@codemirror/commands": "6.3.2", "@codemirror/lang-javascript": "6.2.1", "@codemirror/language": "6.9.3", "@dnd-kit/core": "6.1.0", diff --git a/yarn.lock b/yarn.lock index cfc6eada696..16232ff7fb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1737,15 +1737,15 @@ __metadata: languageName: node linkType: hard -"@codemirror/commands@npm:6.3.1": - version: 6.3.1 - resolution: "@codemirror/commands@npm:6.3.1" +"@codemirror/commands@npm:6.3.2": + version: 6.3.2 + resolution: "@codemirror/commands@npm:6.3.2" dependencies: "@codemirror/language": ^6.0.0 "@codemirror/state": ^6.2.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.1.0 - checksum: b0798b6cee0f37902ab4df80c9d28bfc999fa97bf9072b2972b83a036193e4dcb5db6da7fa8f71c4df98d629afff14e03153f5166617680cb917a3334417e932 + checksum: 683c444d8e6ad889ab5efd0d742b0fa28b78c8cad63276ec60d298b13d4939c8bd7e1d6fd3535645b8d255147de0d3aef46d89a29c19d0af58a7f2914bdcb3ab languageName: node linkType: hard @@ -3628,7 +3628,7 @@ __metadata: resolution: "@neo4j/graphql-toolbox@workspace:packages/graphql-toolbox" dependencies: "@codemirror/autocomplete": 6.11.1 - "@codemirror/commands": 6.3.1 + "@codemirror/commands": 6.3.2 "@codemirror/lang-javascript": 6.2.1 "@codemirror/language": 6.9.3 "@dnd-kit/core": 6.1.0 From 1c87ccbec99d8775478d29bfad8d369c35722242 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 23:07:27 +0000 Subject: [PATCH 51/72] chore(deps): update typescript-eslint monorepo to v6.13.1 --- package.json | 4 +- yarn.lock | 104 +++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 40396dfd86a..9864218fe6d 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ }, "devDependencies": { "@tsconfig/node16": "1.0.4", - "@typescript-eslint/eslint-plugin": "6.12.0", - "@typescript-eslint/parser": "6.12.0", + "@typescript-eslint/eslint-plugin": "6.13.1", + "@typescript-eslint/parser": "6.13.1", "concurrently": "8.2.2", "dotenv": "16.3.1", "eslint": "8.54.0", diff --git a/yarn.lock b/yarn.lock index 16232ff7fb7..8cc68cd355e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7975,15 +7975,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.12.0" +"@typescript-eslint/eslint-plugin@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.13.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.12.0 - "@typescript-eslint/type-utils": 6.12.0 - "@typescript-eslint/utils": 6.12.0 - "@typescript-eslint/visitor-keys": 6.12.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/type-utils": 6.13.1 + "@typescript-eslint/utils": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 @@ -7996,25 +7996,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: a791ebe432a6cac50a15c9e98502b62e874de0c7e35fd320b9bdca21afd4ae88c88cff45ee50a95362da14e98965d946e57b15965f5522f1153568a3fe45db8a + checksum: 568093d76c200a8502047d74f29300110a59b9f2a5cbf995a6cbe419c803a7ec22220e9592a884401d2dde72c79346b4cc0ee393e7b422924ad4a8a2040af3b0 languageName: node linkType: hard -"@typescript-eslint/parser@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/parser@npm:6.12.0" +"@typescript-eslint/parser@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/parser@npm:6.13.1" dependencies: - "@typescript-eslint/scope-manager": 6.12.0 - "@typescript-eslint/types": 6.12.0 - "@typescript-eslint/typescript-estree": 6.12.0 - "@typescript-eslint/visitor-keys": 6.12.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/typescript-estree": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 92923b7ee61f52d6b74f515640fe6bbb6b0a922d20dabeb6b59bc73f3c132bf750a2b706bb40fbe6d233c6ecc1abe905c99aa062280bb78e5724334f5b6c4ac5 + checksum: 58b7fef6f2d02c8f4737f9908a8d335a20bee20dba648233a69f28e7b39237791d2b9fbb818e628dcc053ddf16507b161ace7f1139e093d72365f1270c426de3 languageName: node linkType: hard @@ -8028,22 +8028,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/scope-manager@npm:6.12.0" +"@typescript-eslint/scope-manager@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/scope-manager@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.12.0 - "@typescript-eslint/visitor-keys": 6.12.0 - checksum: 4cc4eb1bcd04ba7b0a1de4284521cde5f3f25f2530f78dfcb3f098396b142fd30a45f615a87dc7a3adddbd131a6255cb12b1df19aacff71a3f766992ddef183f + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 + checksum: 109a213f82719e10f8c6a0168f2e105dc1369c7e0c075c1f30af137030fc866a3a585a77ff78a9a3538afc213061c8aedbb4462a91f26cbd90eefbab8b89ea10 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/type-utils@npm:6.12.0" +"@typescript-eslint/type-utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/type-utils@npm:6.13.1" dependencies: - "@typescript-eslint/typescript-estree": 6.12.0 - "@typescript-eslint/utils": 6.12.0 + "@typescript-eslint/typescript-estree": 6.13.1 + "@typescript-eslint/utils": 6.13.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -8051,7 +8051,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: c345c45f1262eee4b9f6960a59b3aba960643d0004094a3d8fb9682ab79af2fae864695029246dc9e0d4fdb2f3d017a56b7dc034e551d263deba75c2ef048d39 + checksum: e39d28dd2f3b47a26b4f6aa2c7a301bdd769ce9148d734be93441a813c3d1111eba1d655677355bba5519f3d4dbe93e4ff4e46830216b0302df0070bf7a80057 languageName: node linkType: hard @@ -8062,10 +8062,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/types@npm:6.12.0" - checksum: d3b40f9d400f6455ce5ae610651597c9e9ec85d46ca6d3c1025597a76305c557ebc5b88340ec6db0e694c9c79f1299d375b87a1a5b9314b22231dbbb5ce54695 +"@typescript-eslint/types@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/types@npm:6.13.1" + checksum: bb1d52f1646bab9acd3ec874567ffbaaaf7fe4a5f79845bdacbfea46d15698e58d45797da05b08c23f9496a17229b7f2c1363d000fd89ce4e79874fd57ba1d4a languageName: node linkType: hard @@ -8087,12 +8087,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.12.0" +"@typescript-eslint/typescript-estree@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.12.0 - "@typescript-eslint/visitor-keys": 6.12.0 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -8101,24 +8101,24 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 943f7ff2e164d812f6ae0a2d5096836aff00b1fda39937b03f126f266f03f3655794f5fc4643b49b71c312126d9422dfd764744bd1ba41ee6821a5bac1511aa2 + checksum: 09aa0f5cbd60e84df4f58f3d479be352549600b24dbefe75c686ea89252526c52c1c06ce1ae56c0405dd7337002e741c2ba02b71fb1caa3b94a740a70fcc8699 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/utils@npm:6.12.0" +"@typescript-eslint/utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/utils@npm:6.13.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.12.0 - "@typescript-eslint/types": 6.12.0 - "@typescript-eslint/typescript-estree": 6.12.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/typescript-estree": 6.13.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: dad05bd0e4db7a88c2716f9ee83c7c28c30d71e57392e58dc0db66b5f5c4c86b9db14142c6a1a82cf1650da294d31980c56a118015d3a2a645acb8b8a5ebc315 + checksum: 14f64840869c8755af4d287cfc74abc424dc139559e87ca1a8b0e850f4fa56311d99dfb61a43dd4433eae5914be12b4b3390e55de1f236dce6701830d17e31c9 languageName: node linkType: hard @@ -8150,13 +8150,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.12.0" +"@typescript-eslint/visitor-keys@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.12.0 + "@typescript-eslint/types": 6.13.1 eslint-visitor-keys: ^3.4.1 - checksum: 3d8dc74ae748a95fe60b48dbaecca8d9c0c8df344d8034e3843057251fba24f06a3d29dbb9f525c9540b538d8c24221d3cf119ac483e9de38149a978051c72f3 + checksum: d15d362203a2fe995ea62a59d5b44c15c8fb1fb30ff59dd1542a980f75b3b62035303dfb781d83709921613f6ac8cc5bf57b70f6e20d820aec8b7911f07152e9 languageName: node linkType: hard @@ -19854,8 +19854,8 @@ __metadata: "@changesets/changelog-github": 0.4.8 "@changesets/cli": 2.26.2 "@tsconfig/node16": 1.0.4 - "@typescript-eslint/eslint-plugin": 6.12.0 - "@typescript-eslint/parser": 6.12.0 + "@typescript-eslint/eslint-plugin": 6.13.1 + "@typescript-eslint/parser": 6.13.1 concurrently: 8.2.2 dotenv: 16.3.1 eslint: 8.54.0 From cd9c3927a10787a5e3587b1eb93bdd332f4f4074 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 23:16:55 +0000 Subject: [PATCH 52/72] fix(deps): update dependency @changesets/changelog-github to v0.5.0 --- package.json | 2 +- yarn.lock | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 9864218fe6d..eb9afa4456c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "packageManager": "yarn@3.7.0", "dependencies": { - "@changesets/changelog-github": "0.4.8", + "@changesets/changelog-github": "0.5.0", "@changesets/cli": "2.26.2" } } diff --git a/yarn.lock b/yarn.lock index 8cc68cd355e..0f1b73806bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1341,14 +1341,14 @@ __metadata: languageName: node linkType: hard -"@changesets/changelog-github@npm:0.4.8": - version: 0.4.8 - resolution: "@changesets/changelog-github@npm:0.4.8" +"@changesets/changelog-github@npm:0.5.0": + version: 0.5.0 + resolution: "@changesets/changelog-github@npm:0.5.0" dependencies: - "@changesets/get-github-info": ^0.5.2 - "@changesets/types": ^5.2.1 + "@changesets/get-github-info": ^0.6.0 + "@changesets/types": ^6.0.0 dotenv: ^8.1.0 - checksum: 8a357cc08757e0eeca267ee05141f68bef936582abef8b78a5d30d99f5a86e41b7d3debba70992b73b2f57b0fc6201ec1cc3c65116930167ee3197b427b865c5 + checksum: 4ab43d8104693f970d878f2b1657ff67b4d4dcb7452ddf118575153bab74286cdfd125381c2ab92b205bce4b2c653c36552138bf2900f7165ac39a868b7fe22c languageName: node linkType: hard @@ -1432,13 +1432,13 @@ __metadata: languageName: node linkType: hard -"@changesets/get-github-info@npm:^0.5.2": - version: 0.5.2 - resolution: "@changesets/get-github-info@npm:0.5.2" +"@changesets/get-github-info@npm:^0.6.0": + version: 0.6.0 + resolution: "@changesets/get-github-info@npm:0.6.0" dependencies: dataloader: ^1.4.0 node-fetch: ^2.5.0 - checksum: 067e07eeaecdbedbd1c715513c4aa6206a941bd1d3af292d067792808c6fa6644caad2b35fba614a44892559c031c234df8028f8d2abd4cb2682d48080ef5df3 + checksum: 753173bda536aa79cb0502f59ce13889b23ae8463d04893d43ff22966818060837d9db4052b6cbfbd95dfb242fbfd38890a38c56832948e83bf358a47812b708 languageName: node linkType: hard @@ -1541,6 +1541,13 @@ __metadata: languageName: node linkType: hard +"@changesets/types@npm:^6.0.0": + version: 6.0.0 + resolution: "@changesets/types@npm:6.0.0" + checksum: d528b5d712f62c26ea422c7d34ccf6eac57a353c0733d96716db3c796ecd9bba5d496d48b37d5d46b784dc45b69c06ce3345fa3515df981bb68456cad68e6465 + languageName: node + linkType: hard + "@changesets/write@npm:^0.2.3": version: 0.2.3 resolution: "@changesets/write@npm:0.2.3" @@ -19851,7 +19858,7 @@ __metadata: version: 0.0.0-use.local resolution: "neo4j-graphql@workspace:." dependencies: - "@changesets/changelog-github": 0.4.8 + "@changesets/changelog-github": 0.5.0 "@changesets/cli": 2.26.2 "@tsconfig/node16": 1.0.4 "@typescript-eslint/eslint-plugin": 6.13.1 From 9c7b66ae43eb460e932590cd8bda03d163571dca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 04:21:35 +0000 Subject: [PATCH 53/72] fix(deps): update dependency @changesets/cli to v2.27.1 --- package.json | 2 +- yarn.lock | 243 +++++++++++++++++++++++---------------------------- 2 files changed, 112 insertions(+), 133 deletions(-) diff --git a/package.json b/package.json index eb9afa4456c..d04215ab294 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,6 @@ "packageManager": "yarn@3.7.0", "dependencies": { "@changesets/changelog-github": "0.5.0", - "@changesets/cli": "2.26.2" + "@changesets/cli": "2.27.1" } } diff --git a/yarn.lock b/yarn.lock index 0f1b73806bb..49c061067fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1297,15 +1297,15 @@ __metadata: languageName: node linkType: hard -"@changesets/apply-release-plan@npm:^6.1.4": - version: 6.1.4 - resolution: "@changesets/apply-release-plan@npm:6.1.4" +"@changesets/apply-release-plan@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/apply-release-plan@npm:7.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/config": ^2.3.1 - "@changesets/get-version-range-type": ^0.3.2 - "@changesets/git": ^2.0.0 - "@changesets/types": ^5.2.1 + "@changesets/config": ^3.0.0 + "@changesets/get-version-range-type": ^0.4.0 + "@changesets/git": ^3.0.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 detect-indent: ^6.0.0 fs-extra: ^7.0.1 @@ -1314,30 +1314,30 @@ __metadata: prettier: ^2.7.1 resolve-from: ^5.0.0 semver: ^7.5.3 - checksum: d386aee70c5483c97d964c6fa1191878005b7050d34b2e1e4a1ad66d9ad44f8f20d1c884e01e770b954bd2d4364f935510e53ae896212669f67e5c37b2a610c7 + checksum: ad83f89a3d46cd5249fa960cb0324114532bd5f25e74466d181afd6661273824859d038a12ba587a5e044f9169810e4a6febbb61e23c3819b3b28c00176a8bdf languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^5.2.4": - version: 5.2.4 - resolution: "@changesets/assemble-release-plan@npm:5.2.4" +"@changesets/assemble-release-plan@npm:^6.0.0": + version: 6.0.0 + resolution: "@changesets/assemble-release-plan@npm:6.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 semver: ^7.5.3 - checksum: 32f443a0afec3d5a4afc68c8de32e8ff88531ea24976b50583b1d6870d71cec2729f27952af82854eb54e2ad0a619872d211d654c596ee0eb42c83ab54ad15ae + checksum: 0e6d25f25e0e3cc0e92aa8c43f5f496bae9464e2523be4ff81e31b6c9971b63bb1264821a2483c48d451d89d60af1acebe727e7f8c392ed48188a3ff26d0950e languageName: node linkType: hard -"@changesets/changelog-git@npm:^0.1.14": - version: 0.1.14 - resolution: "@changesets/changelog-git@npm:0.1.14" +"@changesets/changelog-git@npm:^0.2.0": + version: 0.2.0 + resolution: "@changesets/changelog-git@npm:0.2.0" dependencies: - "@changesets/types": ^5.2.1 - checksum: 60b45bb899e66cec669ab3884d5d18550cd30bf5a8b06f335eb72aa6c9e018dd3e0187e4df61c91a22076153e346b735b792f0e9c6186e6245b1b7aec2fc42d4 + "@changesets/types": ^6.0.0 + checksum: 132660f7fdabbdda00ac803cc822d6427a1a38a17a5f414e87ad32f6dc4cbef5280a147ecdc087a28dc06c8bd0762f8d6e7132d01b8a4142b59fbe1bc2177034 languageName: node linkType: hard @@ -1352,34 +1352,33 @@ __metadata: languageName: node linkType: hard -"@changesets/cli@npm:2.26.2": - version: 2.26.2 - resolution: "@changesets/cli@npm:2.26.2" +"@changesets/cli@npm:2.27.1": + version: 2.27.1 + resolution: "@changesets/cli@npm:2.27.1" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/apply-release-plan": ^6.1.4 - "@changesets/assemble-release-plan": ^5.2.4 - "@changesets/changelog-git": ^0.1.14 - "@changesets/config": ^2.3.1 - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/get-release-plan": ^3.0.17 - "@changesets/git": ^2.0.0 - "@changesets/logger": ^0.0.5 - "@changesets/pre": ^1.0.14 - "@changesets/read": ^0.5.9 - "@changesets/types": ^5.2.1 - "@changesets/write": ^0.2.3 + "@changesets/apply-release-plan": ^7.0.0 + "@changesets/assemble-release-plan": ^6.0.0 + "@changesets/changelog-git": ^0.2.0 + "@changesets/config": ^3.0.0 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/get-release-plan": ^4.0.0 + "@changesets/git": ^3.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/pre": ^2.0.0 + "@changesets/read": ^0.6.0 + "@changesets/types": ^6.0.0 + "@changesets/write": ^0.3.0 "@manypkg/get-packages": ^1.1.3 - "@types/is-ci": ^3.0.0 "@types/semver": ^7.5.0 ansi-colors: ^4.1.3 chalk: ^2.1.0 + ci-info: ^3.7.0 enquirer: ^2.3.0 external-editor: ^3.1.0 fs-extra: ^7.0.1 human-id: ^1.0.2 - is-ci: ^3.0.1 meow: ^6.0.0 outdent: ^0.5.0 p-limit: ^2.2.0 @@ -1391,44 +1390,44 @@ __metadata: tty-table: ^4.1.5 bin: changeset: bin.js - checksum: fc7b5bf319b19abed7a8d33a9fbd9ce49108af61c9c51920f609a49cb0c557f0b998711250d0cac149d0bed8a522f3109c4d8b0dda65b96ff2f823d16ca2f972 + checksum: 0d030dec7e0ef28626082a257d57f46cdf65edb65a95f5a3511a9d298ca052388d8ab7f9a714943864eddc59148c4afb0b802a9c75b5bea45aade4c0dc7a5fa6 languageName: node linkType: hard -"@changesets/config@npm:^2.3.1": - version: 2.3.1 - resolution: "@changesets/config@npm:2.3.1" +"@changesets/config@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/config@npm:3.0.0" dependencies: - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/logger": ^0.0.5 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 micromatch: ^4.0.2 - checksum: 8af58e3add4751ac8ce2c01f026ac8843b8d1c07c9a3df6518496eaef67f56458a84cad310763c588f7eccbf6831afbf280df7e05e78b294027b6b847be3d0cc + checksum: 31a8c37e38768cf3676d24b7d371009dd1d691f221ecf086b79f0d96dc8e95aa408cda3659eb867a14615ea38a1c2be448bf0655c7570539af57c930ca784051 languageName: node linkType: hard -"@changesets/errors@npm:^0.1.4": - version: 0.1.4 - resolution: "@changesets/errors@npm:0.1.4" +"@changesets/errors@npm:^0.2.0": + version: 0.2.0 + resolution: "@changesets/errors@npm:0.2.0" dependencies: extendable-error: ^0.1.5 - checksum: 10734f1379715bf5a70b566dd42b50a75964d76f382bb67332776614454deda6d04a43dd7e727cd7cba56d7f2f7c95a07c7c0a19dd5d64fb1980b28322840733 + checksum: 4b79373f92287af4f723e8dbbccaf0299aa8735fc043243d0ad587f04a7614615ea50180be575d4438b9f00aa82d1cf85e902b77a55bdd3e0a8dd97e77b18c60 languageName: node linkType: hard -"@changesets/get-dependents-graph@npm:^1.3.6": - version: 1.3.6 - resolution: "@changesets/get-dependents-graph@npm:1.3.6" +"@changesets/get-dependents-graph@npm:^2.0.0": + version: 2.0.0 + resolution: "@changesets/get-dependents-graph@npm:2.0.0" dependencies: - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 chalk: ^2.1.0 fs-extra: ^7.0.1 semver: ^7.5.3 - checksum: d2cbbc5041063b939899502d1b264a0d9edb655acefd7f6197883229156bb7cfd1ace642ae4a1f7f7b432f2c51429f5dc9851ff5a9ed47f1c0159916e66627a9 + checksum: 6690d3ed36e8a636bc2a985d209bd72ee1100601ccf00850ca1fbe8500af839a3f4e5bd2167858cf11383aa76360f853e481533157060ad882fb56319db3090a languageName: node linkType: hard @@ -1442,88 +1441,88 @@ __metadata: languageName: node linkType: hard -"@changesets/get-release-plan@npm:^3.0.17": - version: 3.0.17 - resolution: "@changesets/get-release-plan@npm:3.0.17" +"@changesets/get-release-plan@npm:^4.0.0": + version: 4.0.0 + resolution: "@changesets/get-release-plan@npm:4.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/assemble-release-plan": ^5.2.4 - "@changesets/config": ^2.3.1 - "@changesets/pre": ^1.0.14 - "@changesets/read": ^0.5.9 - "@changesets/types": ^5.2.1 + "@changesets/assemble-release-plan": ^6.0.0 + "@changesets/config": ^3.0.0 + "@changesets/pre": ^2.0.0 + "@changesets/read": ^0.6.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 - checksum: 8a0e3794d0f1e6220d173dbec96352ad69b585d013c3183888ca598dfdfcaa8a5ac3f7f36d5c511575cdc3559c2ad6f8cecfaa16ba9c24380899a81daa7af924 + checksum: 57672c1e94f95de8ac65aac969275e0cb225f02aa86b2cef69329fff6e36ba5fde04eadeb6af36f4d8ac41a8fd329028b4df4c23c15c10fd13e026c77463d576 languageName: node linkType: hard -"@changesets/get-version-range-type@npm:^0.3.2": - version: 0.3.2 - resolution: "@changesets/get-version-range-type@npm:0.3.2" - checksum: b7ee7127c472a3886906ca6db336ac11233a5e75abc882084bfb4794e79a8936e3faceec3c04bf61c26453cd7f74278d9bf22aea4cdca8c1cd992591925b3c9b +"@changesets/get-version-range-type@npm:^0.4.0": + version: 0.4.0 + resolution: "@changesets/get-version-range-type@npm:0.4.0" + checksum: 2e8c511e658e193f48de7f09522649c4cf072932f0cbe0f252a7f2703d7775b0b90b632254526338795d0658e340be9dff3879cfc8eba4534b8cd6071efff8c9 languageName: node linkType: hard -"@changesets/git@npm:^2.0.0": - version: 2.0.0 - resolution: "@changesets/git@npm:2.0.0" +"@changesets/git@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/git@npm:3.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 is-subdir: ^1.1.1 micromatch: ^4.0.2 spawndamnit: ^2.0.0 - checksum: 3820b7b689bbe8dfb93222c766bee214e68a45f07b2b5c8056891f9ffe6f1e369c0f84388246a9eea5317b496ae80ffd1508319190f79c359f060ebf8ccb7b13 + checksum: a8fa66d77302b50d5e604aca898ee813247537d23a05004637ecee4aa1579d6a2859283c099bdcf3e2b232258c93ff81dd57aa867858788e457df40118c64c2b languageName: node linkType: hard -"@changesets/logger@npm:^0.0.5": - version: 0.0.5 - resolution: "@changesets/logger@npm:0.0.5" +"@changesets/logger@npm:^0.1.0": + version: 0.1.0 + resolution: "@changesets/logger@npm:0.1.0" dependencies: chalk: ^2.1.0 - checksum: bfec3cd9122b00c0ec25e96730f771ffd662ef3906d571bad1e4e9993f9d54d357d3eaf074b3dfaa4e23af759ce68efa2a97d8b845b0d8c951df5d21c6dfdff5 + checksum: d8ef1b7caf3d2c15a9e7743b7a9462e0c2e61c76d9a5bbed5eff805afa8226117505309c6e9095001136b4f6d9ae0aba61377e53af8aa0809f1febd1b5f787f1 languageName: node linkType: hard -"@changesets/parse@npm:^0.3.16": - version: 0.3.16 - resolution: "@changesets/parse@npm:0.3.16" +"@changesets/parse@npm:^0.4.0": + version: 0.4.0 + resolution: "@changesets/parse@npm:0.4.0" dependencies: - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 js-yaml: ^3.13.1 - checksum: 475f808ac8d33ec90af3914d55af1da8eeb9336d6cab7dd9e5be74af844f0ec04f4a67d5237a1d3284a468e0c9198e2be01d0e5870a1b28e63bc240f5f1ffea9 + checksum: 3dd970b244479746233ebd357cfff3816cf9f344ebf2cf0c7c55ce8579adfd3f506978e86ad61222dc3acf1548a2105ffdd8b3e940b3f82b225741315cee2bf0 languageName: node linkType: hard -"@changesets/pre@npm:^1.0.14": - version: 1.0.14 - resolution: "@changesets/pre@npm:1.0.14" +"@changesets/pre@npm:^2.0.0": + version: 2.0.0 + resolution: "@changesets/pre@npm:2.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 - checksum: 6b849bd6f916476a5b5664bc4286020bee506985c82f723a757fa4e681b0b7129db81751f16072ac55a980ffd83a4b234d6b8d0f8b6bc889aa0c0fd5377431e8 + checksum: 6a01086405f4e4ce63abb8f222de39b69a5762c9c8c8f19c0d3c72f7798248d7a152937028f1be24be1f8a4a5e47e4cb23c54bc36f979539b24a728c893caf4e languageName: node linkType: hard -"@changesets/read@npm:^0.5.9": - version: 0.5.9 - resolution: "@changesets/read@npm:0.5.9" +"@changesets/read@npm:^0.6.0": + version: 0.6.0 + resolution: "@changesets/read@npm:0.6.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/git": ^2.0.0 - "@changesets/logger": ^0.0.5 - "@changesets/parse": ^0.3.16 - "@changesets/types": ^5.2.1 + "@changesets/git": ^3.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/parse": ^0.4.0 + "@changesets/types": ^6.0.0 chalk: ^2.1.0 fs-extra: ^7.0.1 p-filter: ^2.1.0 - checksum: 0875a80829186de2da55bc0347601cc31b269d54fb6967a5093abacbbd9f949e352907b8340b61348a304228fdade670ded151327f16eea3424b5b4b2bb9888c + checksum: 3da6428124b4983f6ccbdae324c73044cd6a84269bfdbaff545331042e3d6845c647613b5d8f4ffdd48bad5b791623eca2be1b507652ea47b77e136cd2e26c70 languageName: node linkType: hard @@ -1534,13 +1533,6 @@ __metadata: languageName: node linkType: hard -"@changesets/types@npm:^5.2.1": - version: 5.2.1 - resolution: "@changesets/types@npm:5.2.1" - checksum: 527dc1aa41b040fe35bcd55f7d07bec710320b179b000c429723e25b87aac18be487daf5047d4fecf2781aad78f73abff111e76e411b652f7a2e812a464c69f2 - languageName: node - linkType: hard - "@changesets/types@npm:^6.0.0": version: 6.0.0 resolution: "@changesets/types@npm:6.0.0" @@ -1548,16 +1540,16 @@ __metadata: languageName: node linkType: hard -"@changesets/write@npm:^0.2.3": - version: 0.2.3 - resolution: "@changesets/write@npm:0.2.3" +"@changesets/write@npm:^0.3.0": + version: 0.3.0 + resolution: "@changesets/write@npm:0.3.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 fs-extra: ^7.0.1 human-id: ^1.0.2 prettier: ^2.7.1 - checksum: 40ad8069f9adc565b78a5f25992e31b41a12e551d94c29e1b4def49ce98871a1e358feda6536be8b363a6dba18b1226a22ecfc60fdd7bc1e74bfcf46b07f91be + checksum: 37588eb3ef2af15b3ea09d46864c994780619d20b791ea5b654801a035a3a12540c7f953e6e4f36731678615edc6d1c32f8fe174d599d3e6ce2d68263865788b languageName: node linkType: hard @@ -7481,15 +7473,6 @@ __metadata: languageName: node linkType: hard -"@types/is-ci@npm:^3.0.0": - version: 3.0.1 - resolution: "@types/is-ci@npm:3.0.1" - dependencies: - ci-info: ^3.1.0 - checksum: c5cce9ffcd2528ebc731570855d23f99e2589d094e20ac5c3d87c2e53a456c2e7002851bd3fec4e3c20cdd8a5b090d8a90194e108192d9494c4d130ff9b65bbb - languageName: node - linkType: hard - "@types/is-uuid@npm:1.0.2": version: 1.0.2 resolution: "@types/is-uuid@npm:1.0.2" @@ -10487,13 +10470,20 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0": +"ci-info@npm:^3.2.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard +"ci-info@npm:^3.7.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 6b19dc9b2966d1f8c2041a838217299718f15d6c4b63ae36e4674edd2bee48f780e94761286a56aa59eb305a85fbea4ddffb7630ec063e7ec7e7e5ad42549a87 + languageName: node + linkType: hard + "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -16273,17 +16263,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^3.0.1": - version: 3.0.1 - resolution: "is-ci@npm:3.0.1" - dependencies: - ci-info: ^3.2.0 - bin: - is-ci: bin.js - checksum: 192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e - languageName: node - linkType: hard - "is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.9.0": version: 2.13.0 resolution: "is-core-module@npm:2.13.0" @@ -19859,7 +19838,7 @@ __metadata: resolution: "neo4j-graphql@workspace:." dependencies: "@changesets/changelog-github": 0.5.0 - "@changesets/cli": 2.26.2 + "@changesets/cli": 2.27.1 "@tsconfig/node16": 1.0.4 "@typescript-eslint/eslint-plugin": 6.13.1 "@typescript-eslint/parser": 6.13.1 From 03a2496223d6e2dae5d97b89645b7e62be2fa96e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 04:31:25 +0000 Subject: [PATCH 54/72] fix(deps): update dependency neo4j-driver to v5.15.0 --- package.json | 2 +- .../package.json | 2 +- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 36 +++++++++---------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index d04215ab294..76895899292 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "husky": "8.0.3", "jest": "29.7.0", "lint-staged": "15.1.0", - "neo4j-driver": "5.14.0", + "neo4j-driver": "5.15.0", "npm-run-all": "4.1.5", "prettier": "2.8.8", "set-tz": "0.2.0", diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 2d0a7de5e2f..cd714b1c3d9 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -44,7 +44,7 @@ "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", - "neo4j-driver": "5.14.0", + "neo4j-driver": "5.15.0", "pluralize": "8.0.0", "randomstring": "1.3.0", "supertest": "6.3.3", diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index 6c9933e7eea..2ad634816c2 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -62,7 +62,7 @@ "graphql": "16.8.1", "graphql-query-complexity": "0.12.0", "markdown-it": "13.0.2", - "neo4j-driver": "5.14.0", + "neo4j-driver": "5.15.0", "prettier": "3.0.0", "process": "0.11.10", "react": "18.2.0", diff --git a/yarn.lock b/yarn.lock index 49c061067fc..fac195cacf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3581,7 +3581,7 @@ __metadata: cors: ^2.8.5 graphql-ws: 5.14.2 jest: 29.7.0 - neo4j-driver: 5.14.0 + neo4j-driver: 5.15.0 pluralize: 8.0.0 randomstring: 1.3.0 supertest: 6.3.3 @@ -3666,7 +3666,7 @@ __metadata: jest: 29.7.0 jest-environment-jsdom: 29.7.0 markdown-it: 13.0.2 - neo4j-driver: 5.14.0 + neo4j-driver: 5.15.0 node-polyfill-webpack-plugin: 2.0.1 parse5: 7.1.2 postcss: 8.4.31 @@ -19786,14 +19786,14 @@ __metadata: languageName: node linkType: hard -"neo4j-driver-bolt-connection@npm:5.14.0": - version: 5.14.0 - resolution: "neo4j-driver-bolt-connection@npm:5.14.0" +"neo4j-driver-bolt-connection@npm:5.15.0": + version: 5.15.0 + resolution: "neo4j-driver-bolt-connection@npm:5.15.0" dependencies: buffer: ^6.0.3 - neo4j-driver-core: 5.14.0 + neo4j-driver-core: 5.15.0 string_decoder: ^1.3.0 - checksum: 1f7fe0331ad6db1fedc7b9c0863f97579c65475a3baf701a33c3e311a1556924048fc9f63635070c418c278142f5890d78f8867015cd8ce6d19e4e89bfc3a721 + checksum: 5e36d6eab0130df8d15e71ad85ca36697292e32ea4195ec7f33a547675942f3148a30c5aa99fc93e830d4b3fb771d50d05f98a12f1134bd7f06d2c09a3ca8d36 languageName: node linkType: hard @@ -19804,21 +19804,21 @@ __metadata: languageName: node linkType: hard -"neo4j-driver-core@npm:5.14.0": - version: 5.14.0 - resolution: "neo4j-driver-core@npm:5.14.0" - checksum: bc676f698f817d12e34b2936ade6993c626e0e345c86a729895232e129f50f7c2358ed36aec282bb256afed3ee0d1902a3a953d30d0aeaed338d93af25214612 +"neo4j-driver-core@npm:5.15.0": + version: 5.15.0 + resolution: "neo4j-driver-core@npm:5.15.0" + checksum: c13ba8cf0a68ee5de7d3855aa1e5b8e724c8f735ab5514e8dd8cfaee71d808faf38b9511666d223005a10340a1929a7d90074c7596e7f25daa47bb77fcbd0a96 languageName: node linkType: hard -"neo4j-driver@npm:5.14.0": - version: 5.14.0 - resolution: "neo4j-driver@npm:5.14.0" +"neo4j-driver@npm:5.15.0": + version: 5.15.0 + resolution: "neo4j-driver@npm:5.15.0" dependencies: - neo4j-driver-bolt-connection: 5.14.0 - neo4j-driver-core: 5.14.0 + neo4j-driver-bolt-connection: 5.15.0 + neo4j-driver-core: 5.15.0 rxjs: ^7.8.1 - checksum: 6982004944901327b8f7cccc0939c935d33f1c7a63c1783c64f5af2ff5f1fe45cbb702abd0a507c93831232eafae39236affada4e8916f99318d10dfcfe4e35d + checksum: 9e80d00719c1ec24bddfd9f221690db992e4102cb511f7809ae6f77f3fbc555b6420f205b24a6132cd91784356323c69eefc3731ee40c919faaaf791b03eafbb languageName: node linkType: hard @@ -19858,7 +19858,7 @@ __metadata: husky: 8.0.3 jest: 29.7.0 lint-staged: 15.1.0 - neo4j-driver: 5.14.0 + neo4j-driver: 5.15.0 npm-run-all: 4.1.5 prettier: 2.8.8 set-tz: 0.2.0 From 2eb1ac2a5469cb229b700fb4cab56609a7731b97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 22:14:37 +0000 Subject: [PATCH 55/72] chore(deps): update rabbitmq:3.12-management docker digest to 7c7bf7a --- examples/neo-place/docker-compose.yml | 2 +- examples/subscriptions/apollo_rabbitmq/docker-compose.yml | 2 +- packages/graphql-amqp-subscriptions-engine/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/neo-place/docker-compose.yml b/examples/neo-place/docker-compose.yml index ddd872b502c..c3132014371 100644 --- a/examples/neo-place/docker-compose.yml +++ b/examples/neo-place/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c + image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 ports: - "5672:5672" - "15672:15672" diff --git a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml index ddd872b502c..c3132014371 100644 --- a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml +++ b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c + image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 ports: - "5672:5672" - "15672:15672" diff --git a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml index 8edb8919a82..1f74c17ffaf 100644 --- a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml +++ b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.5' # This is just for local testing services: rabbitmq: - image: rabbitmq:3.12-management@sha256:fe80978eb1d442d2fd48cc389f033f4b51c3ce923ba5db2b8c47f79683acd85c + image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 ports: - "5672:5672" - "15672:15672" From bcb3f7b6cf3badaaaf4b5bc55e5da4b4a59e2b78 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 22:23:31 +0000 Subject: [PATCH 56/72] chore(deps): update rabbitmq docker digest to d548e59 --- .../workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml index eead7ce859e..21ac71a6c74 100644 --- a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml +++ b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml @@ -26,7 +26,7 @@ jobs: ports: - 7687:7687 rabbitmq: - image: rabbitmq@sha256:ff6380f9596a3875bcaa03836c2c39b0ade0e34d2148bca8b5bf407d23f908d5 + image: rabbitmq@sha256:d548e59469ae8f20c801be59e1a9e40bddeec2f496f3d1c37b15c7728700ccc4 env: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest From 04bc6ccdef85b55909bc64271727b97ecee13f16 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:50:43 +0000 Subject: [PATCH 57/72] chore(deps): update babel monorepo to v7.23.5 --- packages/package-tests/babel/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/package-tests/babel/package.json b/packages/package-tests/babel/package.json index 3063034af9b..1817a88c2f9 100644 --- a/packages/package-tests/babel/package.json +++ b/packages/package-tests/babel/package.json @@ -13,8 +13,8 @@ "neo4j-driver": "^5.8.0" }, "devDependencies": { - "@babel/core": "7.23.3", + "@babel/core": "7.23.5", "@babel/node": "7.22.19", - "@babel/preset-env": "7.23.3" + "@babel/preset-env": "7.23.5" } } From 170c475153be1aa6d62e394234915741d820f11a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:59:59 +0000 Subject: [PATCH 58/72] chore(deps): update dependency @types/node to v20.10.1 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index cd714b1c3d9..c53ac61c558 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.10.0", + "@types/node": "20.10.1", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 68935826d2a..dd0ea831be3 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.10.0", + "@types/node": "20.10.1", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 0d4b0862bc3..0c24c1ba2fd 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.10.0", + "@types/node": "20.10.1", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 30147ceff51..c1dd6c0e07f 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.10.0", + "@types/node": "20.10.1", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index fac195cacf9..5db4dab9db9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3574,7 +3574,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.10.0 + "@types/node": 20.10.1 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3603,7 +3603,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.10.0 + "@types/node": 20.10.1 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3710,7 +3710,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.10.0 + "@types/node": 20.10.1 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3768,7 +3768,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.10.0 + "@types/node": 20.10.1 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7666,12 +7666,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.10.0": - version: 20.10.0 - resolution: "@types/node@npm:20.10.0" +"@types/node@npm:20.10.1": + version: 20.10.1 + resolution: "@types/node@npm:20.10.1" dependencies: undici-types: ~5.26.4 - checksum: face395140d6f2f1755b91fdd3b697cf56aeb9e2514529ce88d56e56f261ad65be7269d863520a9406d73c338699ea68b418e8677584de0c1efeed09539b6f97 + checksum: 9dfdcd2496ce535dba0ae496985d6e991a8a5d70a180db3a94c947a2123d99318a95dce4aa2a192f7e57c3afa3fdb44d6fd63e18efd49568950d6c239dadcc39 languageName: node linkType: hard From 8fff0da4fe8ef20c330e0e2f32b0baecc896adc4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 22:03:12 +0000 Subject: [PATCH 59/72] fix(deps): update dependency @neo4j-ndl/react to v2.0.14 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index 2ad634816c2..c8ff3c3c598 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -51,7 +51,7 @@ "@dnd-kit/sortable": "8.0.0", "@graphiql/react": "0.20.2", "@neo4j-ndl/base": "2.0.7", - "@neo4j-ndl/react": "2.0.13", + "@neo4j-ndl/react": "2.0.14", "@neo4j/graphql": "4.4.3", "@neo4j/introspector": "2.0.0", "classnames": "2.3.2", diff --git a/yarn.lock b/yarn.lock index 5db4dab9db9..a1e7f2439da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3525,9 +3525,9 @@ __metadata: languageName: node linkType: hard -"@neo4j-ndl/react@npm:2.0.13": - version: 2.0.13 - resolution: "@neo4j-ndl/react@npm:2.0.13" +"@neo4j-ndl/react@npm:2.0.14": + version: 2.0.14 + resolution: "@neo4j-ndl/react@npm:2.0.14" dependencies: "@floating-ui/react": 0.25.1 "@heroicons/react": 2.0.13 @@ -3552,7 +3552,7 @@ __metadata: peerDependencies: "@heroicons/react": 2.0.13 react: ">=16.8.0" - checksum: 6082f00eae397d2d362cefb1ca0e3f8c4cf1dda78978732226900adc2cf312a2fa72caa2c8c49020c5efb4e9610ac443ee97e8a5d15aca8a7a37a2ac0dec2b3f + checksum: be5ab15da9f8fe4b57310caf01972e15de83dc42f60abb9f731410385429db116cd388d1bb934cd6bac64995c168a0f12251ca81f38b080f34e34e18c80fea63 languageName: node linkType: hard @@ -3635,7 +3635,7 @@ __metadata: "@dnd-kit/sortable": 8.0.0 "@graphiql/react": 0.20.2 "@neo4j-ndl/base": 2.0.7 - "@neo4j-ndl/react": 2.0.13 + "@neo4j-ndl/react": 2.0.14 "@neo4j/graphql": 4.4.3 "@neo4j/introspector": 2.0.0 "@playwright/test": 1.40.1 From 65c5242605e0c5f7f3daf67751ae6c84e648ef8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:26:59 +0000 Subject: [PATCH 60/72] chore(deps): update dependency @types/node to v20.10.2 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index c53ac61c558..54c6146c970 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.10.1", + "@types/node": "20.10.2", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index dd0ea831be3..616556b5079 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.10.1", + "@types/node": "20.10.2", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 0c24c1ba2fd..77fa3b18b08 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.10.1", + "@types/node": "20.10.2", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index c1dd6c0e07f..5eb1c03d8c0 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.10.1", + "@types/node": "20.10.2", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index a1e7f2439da..fbdf0c10c97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3574,7 +3574,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.10.1 + "@types/node": 20.10.2 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3603,7 +3603,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.10.1 + "@types/node": 20.10.2 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3710,7 +3710,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.10.1 + "@types/node": 20.10.2 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3768,7 +3768,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.10.1 + "@types/node": 20.10.2 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7666,12 +7666,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.10.1": - version: 20.10.1 - resolution: "@types/node@npm:20.10.1" +"@types/node@npm:20.10.2": + version: 20.10.2 + resolution: "@types/node@npm:20.10.2" dependencies: undici-types: ~5.26.4 - checksum: 9dfdcd2496ce535dba0ae496985d6e991a8a5d70a180db3a94c947a2123d99318a95dce4aa2a192f7e57c3afa3fdb44d6fd63e18efd49568950d6c239dadcc39 + checksum: c0c84e8270cdf7a47a18c0230c0321537cc59506adb0e3cba51949b6f1ad4879f2e2ec3a29161f2f5321ebb6415460712d9f0a25ac5c02be0f5435464fe77c23 languageName: node linkType: hard From 8b9d4c83db8ef0a8cec85c7ee239ea4e5b3684a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:36:09 +0000 Subject: [PATCH 61/72] chore(deps): update dependency eslint to v8.55.0 --- package.json | 2 +- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 76895899292..b8ec3b36dfc 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@typescript-eslint/parser": "6.13.1", "concurrently": "8.2.2", "dotenv": "16.3.1", - "eslint": "8.54.0", + "eslint": "8.55.0", "eslint-config-prettier": "9.0.0", "eslint-formatter-summary": "1.1.0", "eslint-import-resolver-typescript": "3.6.1", diff --git a/yarn.lock b/yarn.lock index fbdf0c10c97..8ccf85e0bdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2112,9 +2112,9 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.3": - version: 2.1.3 - resolution: "@eslint/eslintrc@npm:2.1.3" +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -2125,14 +2125,14 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: 5c6c3878192fe0ddffa9aff08b4e2f3bcc8f1c10d6449b7295a5f58b662019896deabfc19890455ffd7e60a5bd28d25d0eaefb2f78b2d230aae3879af92b89e5 + checksum: 10957c7592b20ca0089262d8c2a8accbad14b4f6507e35416c32ee6b4dbf9cad67dfb77096bbd405405e9ada2b107f3797fe94362e1c55e0b09d6e90dd149127 languageName: node linkType: hard -"@eslint/js@npm:8.54.0": - version: 8.54.0 - resolution: "@eslint/js@npm:8.54.0" - checksum: 6d88a6f711ef0133566b5340e3178a178fbb297585766460f195d0a9db85688f1e5cf8559fd5748aeb3131e2096c66595b323d8edab22df015acda68f1ebde92 +"@eslint/js@npm:8.55.0": + version: 8.55.0 + resolution: "@eslint/js@npm:8.55.0" + checksum: fa33ef619f0646ed15649b0c2e313e4d9ccee8425884bdbfc78020d6b6b64c0c42fa9d83061d0e6158e1d4274f03f0f9008786540e2efab8fcdc48082259908c languageName: node linkType: hard @@ -13380,14 +13380,14 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.54.0": - version: 8.54.0 - resolution: "eslint@npm:8.54.0" +"eslint@npm:8.55.0": + version: 8.55.0 + resolution: "eslint@npm:8.55.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 - "@eslint/eslintrc": ^2.1.3 - "@eslint/js": 8.54.0 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": 8.55.0 "@humanwhocodes/config-array": ^0.11.13 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 @@ -13424,7 +13424,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 7e876e9da2a18a017271cf3733d05a3dfbbe469272d75753408c6ea5b1646c71c6bb18cb91e10ca930144c32c1ce3701e222f1ae6784a3975a69f8f8aa68e49f + checksum: 83f82a604559dc1faae79d28fdf3dfc9e592ca221052e2ea516e1b379b37e77e4597705a16880e2f5ece4f79087c1dd13fd7f6e9746f794a401175519db18b41 languageName: node linkType: hard @@ -19844,7 +19844,7 @@ __metadata: "@typescript-eslint/parser": 6.13.1 concurrently: 8.2.2 dotenv: 16.3.1 - eslint: 8.54.0 + eslint: 8.55.0 eslint-config-prettier: 9.0.0 eslint-formatter-summary: 1.1.0 eslint-import-resolver-typescript: 3.6.1 From 9b115357c8e44b5826d78ec0cc7f1bb6430b7c6c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 05:12:31 +0000 Subject: [PATCH 62/72] chore(deps): update dependency postcss to v8.4.32 --- packages/graphql-toolbox/package.json | 2 +- yarn.lock | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index c8ff3c3c598..fd184d99a56 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -93,7 +93,7 @@ "jest-environment-jsdom": "29.7.0", "node-polyfill-webpack-plugin": "2.0.1", "parse5": "7.1.2", - "postcss": "8.4.31", + "postcss": "8.4.32", "postcss-loader": "7.3.3", "randomstring": "1.3.0", "style-loader": "3.3.3", diff --git a/yarn.lock b/yarn.lock index 8ccf85e0bdc..505893ae6b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3669,7 +3669,7 @@ __metadata: neo4j-driver: 5.15.0 node-polyfill-webpack-plugin: 2.0.1 parse5: 7.1.2 - postcss: 8.4.31 + postcss: 8.4.32 postcss-loader: 7.3.3 prettier: 3.0.0 process: 0.11.10 @@ -19634,6 +19634,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -21648,14 +21657,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.31": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" +"postcss@npm:8.4.32": + version: 8.4.32 + resolution: "postcss@npm:8.4.32" dependencies: - nanoid: ^3.3.6 + nanoid: ^3.3.7 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea + checksum: 220d9d0bf5d65be7ed31006c523bfb11619461d296245c1231831f90150aeb4a31eab9983ac9c5c89759a3ca8b60b3e0d098574964e1691673c3ce5c494305ae languageName: node linkType: hard From 24661d00781b2a466b3cf19e8f2753daaca49bb6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 06:15:22 +0000 Subject: [PATCH 63/72] chore(deps): update ibmjava:8-jre docker digest to 956d16b --- .../graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile b/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile index 09ad14c1c80..c23ed74a407 100644 --- a/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile +++ b/packages/graphql-amqp-subscriptions-engine/qpid-docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ibmjava:8-jre@sha256:6e861dd25acbd010a0072838fb9e2bc78fddb6df4917367b9ec128d409a94d05 +FROM ibmjava:8-jre@sha256:956d16bfd2e8f53ab8a9aa0fe82f9750532f8b87a1cfcf6fc27bb3b24deb3726 WORKDIR /usr/local/qpid RUN apt-get update && apt-get install -y curl \ && curl https://dlcdn.apache.org/qpid/broker-j/8.0.6/binaries/apache-qpid-broker-j-8.0.6-bin.tar.gz \ From 0f6e33185520ed2a38ba985d2e8f88a911989bc7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 10:22:28 +0000 Subject: [PATCH 64/72] chore(deps): update dependency eslint-config-prettier to v9.1.0 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b8ec3b36dfc..e5795361c56 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "concurrently": "8.2.2", "dotenv": "16.3.1", "eslint": "8.55.0", - "eslint-config-prettier": "9.0.0", + "eslint-config-prettier": "9.1.0", "eslint-formatter-summary": "1.1.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-eslint-comments": "3.2.0", diff --git a/yarn.lock b/yarn.lock index 505893ae6b4..4ab2968b0c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13169,14 +13169,14 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:9.0.0": - version: 9.0.0 - resolution: "eslint-config-prettier@npm:9.0.0" +"eslint-config-prettier@npm:9.1.0": + version: 9.1.0 + resolution: "eslint-config-prettier@npm:9.1.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 362e991b6cb343f79362bada2d97c202e5303e6865888918a7445c555fb75e4c078b01278e90be98aa98ae22f8597d8e93d48314bec6824f540f7efcab3ce451 + checksum: 9229b768c879f500ee54ca05925f31b0c0bafff3d9f5521f98ff05127356de78c81deb9365c86a5ec4efa990cb72b74df8612ae15965b14136044c73e1f6a907 languageName: node linkType: hard @@ -19854,7 +19854,7 @@ __metadata: concurrently: 8.2.2 dotenv: 16.3.1 eslint: 8.55.0 - eslint-config-prettier: 9.0.0 + eslint-config-prettier: 9.1.0 eslint-formatter-summary: 1.1.0 eslint-import-resolver-typescript: 3.6.1 eslint-plugin-eslint-comments: 3.2.0 From 3e1b8ffe645b9d2221bdbb5786700c74af4a952a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 10:30:38 +0000 Subject: [PATCH 65/72] chore(deps): update rabbitmq docker digest to 01710e3 --- .../workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml index 21ac71a6c74..81a1c982f30 100644 --- a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml +++ b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml @@ -26,7 +26,7 @@ jobs: ports: - 7687:7687 rabbitmq: - image: rabbitmq@sha256:d548e59469ae8f20c801be59e1a9e40bddeec2f496f3d1c37b15c7728700ccc4 + image: rabbitmq@sha256:01710e30987a79c3273612913ce2823aac6311960a17e63b41c2179eb5ac100c env: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest From 19a62f0ce1819110fa85f18ab5fab03fca3f04d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:48:09 +0000 Subject: [PATCH 66/72] chore(deps): update rabbitmq docker digest to 9902112 --- .../workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml index 81a1c982f30..628420628bd 100644 --- a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml +++ b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml @@ -26,7 +26,7 @@ jobs: ports: - 7687:7687 rabbitmq: - image: rabbitmq@sha256:01710e30987a79c3273612913ce2823aac6311960a17e63b41c2179eb5ac100c + image: rabbitmq@sha256:99021122f160abc607dc477e4b30b56a7b5f8efbe5e4b89a04ee90b0efcc8242 env: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest From 3fe3cc0286f98823a30d75e7f505c794875dc9d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:56:48 +0000 Subject: [PATCH 67/72] chore(deps): update rabbitmq:3.12-management docker digest to 571a1b2 --- examples/neo-place/docker-compose.yml | 2 +- examples/subscriptions/apollo_rabbitmq/docker-compose.yml | 2 +- packages/graphql-amqp-subscriptions-engine/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/neo-place/docker-compose.yml b/examples/neo-place/docker-compose.yml index c3132014371..9053ebb74d9 100644 --- a/examples/neo-place/docker-compose.yml +++ b/examples/neo-place/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 + image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 ports: - "5672:5672" - "15672:15672" diff --git a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml index c3132014371..9053ebb74d9 100644 --- a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml +++ b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 + image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 ports: - "5672:5672" - "15672:15672" diff --git a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml index 1f74c17ffaf..71d690c0fe1 100644 --- a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml +++ b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.5' # This is just for local testing services: rabbitmq: - image: rabbitmq:3.12-management@sha256:7c7bf7a895c65e139a85b7ec030650e085bd5f3c0f9866f56fed12ea6125c561 + image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 ports: - "5672:5672" - "15672:15672" From f55b7efc0cfecd55dbf8b02a61bd918e99641b26 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 19:01:02 +0000 Subject: [PATCH 68/72] chore(deps): update rabbitmq docker digest to b669305 --- .../workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml index 628420628bd..9864bed2518 100644 --- a/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml +++ b/.github/workflows/reusable-subscriptions-plugin-amqp-e2e-test.yml @@ -26,7 +26,7 @@ jobs: ports: - 7687:7687 rabbitmq: - image: rabbitmq@sha256:99021122f160abc607dc477e4b30b56a7b5f8efbe5e4b89a04ee90b0efcc8242 + image: rabbitmq@sha256:b669305108158abf3d7790a7f6f5a56b7de9598a926b6672281a7edf11460a2d env: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest From 46ae508e8a7d1b0486506eacab7c7cd5b3d650dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 21:16:52 +0000 Subject: [PATCH 69/72] chore(deps): update rabbitmq:3.12-management docker digest to f8237ef --- examples/neo-place/docker-compose.yml | 2 +- examples/subscriptions/apollo_rabbitmq/docker-compose.yml | 2 +- packages/graphql-amqp-subscriptions-engine/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/neo-place/docker-compose.yml b/examples/neo-place/docker-compose.yml index 9053ebb74d9..d578a9e8296 100644 --- a/examples/neo-place/docker-compose.yml +++ b/examples/neo-place/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 + image: rabbitmq:3.12-management@sha256:f8237ef62feb8fc61fe949170fa15379c45c784e1b8f1e9a4a0b9fbf7c5c8bf1 ports: - "5672:5672" - "15672:15672" diff --git a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml index 9053ebb74d9..d578a9e8296 100644 --- a/examples/subscriptions/apollo_rabbitmq/docker-compose.yml +++ b/examples/subscriptions/apollo_rabbitmq/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.5' services: rabbitmq: - image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 + image: rabbitmq:3.12-management@sha256:f8237ef62feb8fc61fe949170fa15379c45c784e1b8f1e9a4a0b9fbf7c5c8bf1 ports: - "5672:5672" - "15672:15672" diff --git a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml index 71d690c0fe1..fbbe8d8c20d 100644 --- a/packages/graphql-amqp-subscriptions-engine/docker-compose.yml +++ b/packages/graphql-amqp-subscriptions-engine/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.5' # This is just for local testing services: rabbitmq: - image: rabbitmq:3.12-management@sha256:571a1b29274daaa76e8b35131f2e1338d1819c81a4ba4d282c42bf4b9785c193 + image: rabbitmq:3.12-management@sha256:f8237ef62feb8fc61fe949170fa15379c45c784e1b8f1e9a4a0b9fbf7c5c8bf1 ports: - "5672:5672" - "15672:15672" From 39b4305b294982afd5ee5579a31769838bff3564 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:03:13 +0000 Subject: [PATCH 70/72] chore(deps): update dependency lint-staged to v15.2.0 --- package.json | 2 +- yarn.lock | 150 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 107 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index e5795361c56..d3e3c8f3a74 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "graphql": "16.8.1", "husky": "8.0.3", "jest": "29.7.0", - "lint-staged": "15.1.0", + "lint-staged": "15.2.0", "neo4j-driver": "5.15.0", "npm-run-all": "4.1.5", "prettier": "2.8.8", diff --git a/yarn.lock b/yarn.lock index 4ab2968b0c7..03018abc526 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8714,12 +8714,12 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^5.0.0": - version: 5.0.0 - resolution: "ansi-escapes@npm:5.0.0" +"ansi-escapes@npm:^6.2.0": + version: 6.2.0 + resolution: "ansi-escapes@npm:6.2.0" dependencies: - type-fest: ^1.0.2 - checksum: d4b5eb8207df38367945f5dd2ef41e08c28edc192dc766ef18af6b53736682f49d8bfcfa4e4d6ecbc2e2f97c258fda084fb29a9e43b69170b71090f771afccac + type-fest: ^3.0.0 + checksum: f0bc667d5f1ededc3ea89b73c34f0cba95473525b07e1290ddfd3fc868c94614e95f3549f5c4fd0c05424af7d3fd298101fb3d9a52a597d3782508b340783bd7 languageName: node linkType: hard @@ -8799,7 +8799,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 @@ -10597,13 +10597,13 @@ __metadata: languageName: node linkType: hard -"cli-truncate@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-truncate@npm:3.1.0" +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" dependencies: slice-ansi: ^5.0.0 - string-width: ^5.0.0 - checksum: c3243e41974445691c63f8b405df1d5a24049dc33d324fe448dc572e561a7b772ae982692900b1a5960901cc4fc7def25a629b9c69a4208ee89d12ab3332617a + string-width: ^7.0.0 + checksum: d5149175fd25ca985731bdeec46a55ec237475cf74c1a5e103baea696aceb45e372ac4acbaabf1316f06bd62e348123060f8191ffadfeedebd2a70a2a7fb199d languageName: node linkType: hard @@ -12739,6 +12739,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.3.0 + resolution: "emoji-regex@npm:10.3.0" + checksum: 5da48edfeb9462fb1ae5495cff2d79129974c696853fb0ce952cbf560f29a2756825433bf51cfd5157ec7b9f93f46f31d712e896d63e3d8ac9c3832bdb45ab73 + languageName: node + linkType: hard + "emoji-regex@npm:^7.0.1": version: 7.0.3 resolution: "emoji-regex@npm:7.0.3" @@ -14551,6 +14558,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.2.0 + resolution: "get-east-asian-width@npm:1.2.0" + checksum: ea55f4d4a42c4b00d3d9be3111bc17eb0161f60ed23fc257c1390323bb780a592d7a8bdd550260fd4627dabee9a118cdfa3475ae54edca35ebcd3bdae04179e3 + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": version: 1.2.1 resolution: "get-intrinsic@npm:1.2.1" @@ -16352,6 +16366,15 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^5.0.0": + version: 5.0.0 + resolution: "is-fullwidth-code-point@npm:5.0.0" + dependencies: + get-east-asian-width: ^1.0.0 + checksum: 8dfb2d2831b9e87983c136f5c335cd9d14c1402973e357a8ff057904612ed84b8cba196319fabedf9aefe4639e14fe3afe9d9966d1d006ebeb40fe1fed4babe5 + languageName: node + linkType: hard + "is-generator-fn@npm:^2.0.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" @@ -18079,7 +18102,14 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.1.0, lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0": +"lilconfig@npm:3.0.0": + version: 3.0.0 + resolution: "lilconfig@npm:3.0.0" + checksum: a155f1cd24d324ab20dd6974db9ebcf3fb6f2b60175f7c052d917ff8a746b590bc1ee550f6fc3cb1e8716c8b58304e22fe2193febebc0cf16fa86d85e6f896c5 + languageName: node + linkType: hard + +"lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" checksum: 8549bb352b8192375fed4a74694cd61ad293904eee33f9d4866c2192865c44c4eb35d10782966242634e0cbc1e91fe62b1247f148dc5514918e3a966da7ea117 @@ -18118,23 +18148,23 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:15.1.0": - version: 15.1.0 - resolution: "lint-staged@npm:15.1.0" +"lint-staged@npm:15.2.0": + version: 15.2.0 + resolution: "lint-staged@npm:15.2.0" dependencies: chalk: 5.3.0 commander: 11.1.0 debug: 4.3.4 execa: 8.0.1 - lilconfig: 2.1.0 - listr2: 7.0.2 + lilconfig: 3.0.0 + listr2: 8.0.0 micromatch: 4.0.5 pidtree: 0.6.0 string-argv: 0.3.2 yaml: 2.3.4 bin: lint-staged: bin/lint-staged.js - checksum: e99bdedb32d20fa22c0d0798ecf014fd00ac9cce1158373d7caf47855c0b9b4c20d228417677a05ea81f6941f957ae9347dccb3846a48bc1fdd0cdeed2ccf0ef + checksum: 4fb178b8d3ff454f7874697dfbd41017630f61a06296d12ac9dfd578d078c70aff7108b67fab38af94896ef2740a1e7541c1512d0d3c688ed90e6c3af3530f0d languageName: node linkType: hard @@ -18187,17 +18217,17 @@ __metadata: languageName: node linkType: hard -"listr2@npm:7.0.2": - version: 7.0.2 - resolution: "listr2@npm:7.0.2" +"listr2@npm:8.0.0": + version: 8.0.0 + resolution: "listr2@npm:8.0.0" dependencies: - cli-truncate: ^3.1.0 + cli-truncate: ^4.0.0 colorette: ^2.0.20 eventemitter3: ^5.0.1 - log-update: ^5.0.1 + log-update: ^6.0.0 rfdc: ^1.3.0 - wrap-ansi: ^8.1.0 - checksum: 1734c6b9367ceeb09bf372427930a4586b3727097373408f2f840896b9333cc80e53a1a696771a83a7d4d9ada46229843f3052b87f3b0b58c20e9451362c2dd3 + wrap-ansi: ^9.0.0 + checksum: 5cb110a710d14488c71d2207fc5141256abb1f21cbe5ebc12177ae640f94e040a1ef8c031b70ff9f24c4a8fa57c0825a54b534e52bdfaffc122a81082faae8ed languageName: node linkType: hard @@ -18503,16 +18533,16 @@ __metadata: languageName: node linkType: hard -"log-update@npm:^5.0.1": - version: 5.0.1 - resolution: "log-update@npm:5.0.1" +"log-update@npm:^6.0.0": + version: 6.0.0 + resolution: "log-update@npm:6.0.0" dependencies: - ansi-escapes: ^5.0.0 + ansi-escapes: ^6.2.0 cli-cursor: ^4.0.0 - slice-ansi: ^5.0.0 - strip-ansi: ^7.0.1 - wrap-ansi: ^8.0.1 - checksum: 2c6b47dcce6f9233df6d232a37d9834cb3657a0749ef6398f1706118de74c55f158587d4128c225297ea66803f35c5ac3db4f3f617046d817233c45eedc32ef1 + slice-ansi: ^7.0.0 + strip-ansi: ^7.1.0 + wrap-ansi: ^9.0.0 + checksum: 8803ceba2fb28626951b85de598c8d5a4f5e39f1f767cc54fd925412cc7780ba89ce1dbec24dc96fa46f89d226e1ae984534aa729dc9c9b734e36bb805428ffa languageName: node linkType: hard @@ -19866,7 +19896,7 @@ __metadata: graphql: 16.8.1 husky: 8.0.3 jest: 29.7.0 - lint-staged: 15.1.0 + lint-staged: 15.2.0 neo4j-driver: 5.15.0 npm-run-all: 4.1.5 prettier: 2.8.8 @@ -23772,6 +23802,16 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:^7.0.0": + version: 7.1.0 + resolution: "slice-ansi@npm:7.1.0" + dependencies: + ansi-styles: ^6.2.1 + is-fullwidth-code-point: ^5.0.0 + checksum: 10313dd3cf7a2e4b265f527b1684c7c568210b09743fd1bd74f2194715ed13ffba653dc93a5fa79e3b1711518b8990a732cb7143aa01ddafe626e99dfa6474b2 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -24358,7 +24398,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^5.0.0, string-width@npm:^5.0.1, string-width@npm:^5.1.2": +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": version: 5.1.2 resolution: "string-width@npm:5.1.2" dependencies: @@ -24369,6 +24409,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0": + version: 7.0.0 + resolution: "string-width@npm:7.0.0" + dependencies: + emoji-regex: ^10.3.0 + get-east-asian-width: ^1.0.0 + strip-ansi: ^7.1.0 + checksum: bc0de5700a2690895169fce447ec4ed44bc62de80312c2093d5606bfd48319bb88e48a99e97f269dff2bc9577448b91c26b3804c16e7d9b389699795e4655c3b + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.8": version: 4.0.10 resolution: "string.prototype.matchall@npm:4.0.10" @@ -24491,7 +24542,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -25588,13 +25639,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.2": - version: 1.4.0 - resolution: "type-fest@npm:1.4.0" - checksum: b011c3388665b097ae6a109a437a04d6f61d81b7357f74cbcb02246f2f5bd72b888ae33631b99871388122ba0a87f4ff1c94078e7119ff22c70e52c0ff828201 - languageName: node - linkType: hard - "type-fest@npm:^2.14.0": version: 2.19.0 resolution: "type-fest@npm:2.19.0" @@ -25602,6 +25646,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^3.0.0": + version: 3.13.1 + resolution: "type-fest@npm:3.13.1" + checksum: c06b0901d54391dc46de3802375f5579868949d71f93b425ce564e19a428a0d411ae8d8cb0e300d330071d86152c3ea86e744c3f2860a42a79585b6ec2fdae8e + languageName: node + linkType: hard + "type-is@npm:^1.6.16, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -26806,7 +26857,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^8.0.1, wrap-ansi@npm:^8.1.0": +"wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" dependencies: @@ -26817,6 +26868,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.0 + resolution: "wrap-ansi@npm:9.0.0" + dependencies: + ansi-styles: ^6.2.1 + string-width: ^7.0.0 + strip-ansi: ^7.1.0 + checksum: b2d43b76b3d8dcbdd64768165e548aad3e54e1cae4ecd31bac9966faaa7cf0b0345677ad6879db10ba58eb446ba8fa44fb82b4951872fd397f096712467a809f + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" From e6d18e7f5e7d936296a8906b75197bc25b404816 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Dec 2023 23:57:28 +0000 Subject: [PATCH 71/72] chore(deps): update dependency @types/node to v20.10.3 --- .../package.json | 2 +- packages/graphql/package.json | 2 +- packages/introspector/package.json | 2 +- packages/ogm/package.json | 2 +- yarn.lock | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/graphql-amqp-subscriptions-engine/package.json b/packages/graphql-amqp-subscriptions-engine/package.json index 54c6146c970..aa890ab7886 100644 --- a/packages/graphql-amqp-subscriptions-engine/package.json +++ b/packages/graphql-amqp-subscriptions-engine/package.json @@ -40,7 +40,7 @@ "@types/cors": "2.8.17", "@types/debug": "4.1.12", "@types/jest": "29.5.10", - "@types/node": "20.10.2", + "@types/node": "20.10.3", "camelcase": "6.3.0", "graphql-ws": "5.14.2", "jest": "29.7.0", diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 616556b5079..ff09cf84685 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -56,7 +56,7 @@ "@types/is-uuid": "1.0.2", "@types/jest": "29.5.10", "@types/jsonwebtoken": "9.0.5", - "@types/node": "20.10.2", + "@types/node": "20.10.3", "@types/pluralize": "0.0.33", "@types/randomstring": "1.1.11", "@types/semver": "7.5.6", diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 77fa3b18b08..947b2c6f40a 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@neo4j/graphql": "^4.0.0", "@types/jest": "29.5.10", - "@types/node": "20.10.2", + "@types/node": "20.10.3", "@types/pluralize": "0.0.33", "jest": "29.7.0", "ts-jest": "29.1.1", diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 5eb1c03d8c0..dd0e04de573 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@types/jest": "29.5.10", - "@types/node": "20.10.2", + "@types/node": "20.10.3", "camelcase": "6.3.0", "graphql-tag": "2.12.6", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index 03018abc526..78d254a8be4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3574,7 +3574,7 @@ __metadata: "@types/cors": 2.8.17 "@types/debug": 4.1.12 "@types/jest": 29.5.10 - "@types/node": 20.10.2 + "@types/node": 20.10.3 amqplib: 0.10.3 body-parser: ^1.20.2 camelcase: 6.3.0 @@ -3603,7 +3603,7 @@ __metadata: "@graphql-tools/merge": ^9.0.0 "@neo4j/graphql": ^4.4.3 "@types/jest": 29.5.10 - "@types/node": 20.10.2 + "@types/node": 20.10.3 camelcase: 6.3.0 graphql-tag: 2.12.6 jest: 29.7.0 @@ -3710,7 +3710,7 @@ __metadata: "@types/is-uuid": 1.0.2 "@types/jest": 29.5.10 "@types/jsonwebtoken": 9.0.5 - "@types/node": 20.10.2 + "@types/node": 20.10.3 "@types/pluralize": 0.0.33 "@types/randomstring": 1.1.11 "@types/semver": 7.5.6 @@ -3768,7 +3768,7 @@ __metadata: dependencies: "@neo4j/graphql": ^4.0.0 "@types/jest": 29.5.10 - "@types/node": 20.10.2 + "@types/node": 20.10.3 "@types/pluralize": 0.0.33 camelcase: ^6.3.0 debug: ^4.3.4 @@ -7666,12 +7666,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:20.10.2": - version: 20.10.2 - resolution: "@types/node@npm:20.10.2" +"@types/node@npm:20.10.3": + version: 20.10.3 + resolution: "@types/node@npm:20.10.3" dependencies: undici-types: ~5.26.4 - checksum: c0c84e8270cdf7a47a18c0230c0321537cc59506adb0e3cba51949b6f1ad4879f2e2ec3a29161f2f5321ebb6415460712d9f0a25ac5c02be0f5435464fe77c23 + checksum: 34a329494f0ea239af05eeb6f00f396963725b3eb9a2f79c5e6a6d37e823f2ab85e1079c2ee56723a37d8b89e7bbe2bd050c97144e5bb06dab93fd1cace65c97 languageName: node linkType: hard From ece281eaf5c8cdc2d2f0c0a6c1723c88e32b8b12 Mon Sep 17 00:00:00 2001 From: Neo4j Team GraphQL <88824828+neo4j-team-graphql@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:22:48 +0000 Subject: [PATCH 72/72] Version Packages (#4321) --- .changeset/afraid-countries-pretend.md | 5 ----- .changeset/chilly-bugs-pump.md | 5 ----- .changeset/happy-singers-fail.md | 5 ----- .changeset/strong-dodos-live.md | 5 ----- .changeset/warm-students-float.md | 5 ----- .../package.json | 2 +- packages/graphql-toolbox/CHANGELOG.md | 7 +++++++ packages/graphql-toolbox/package.json | 4 ++-- packages/graphql/CHANGELOG.md | 14 ++++++++++++++ packages/graphql/package.json | 2 +- packages/ogm/CHANGELOG.md | 7 +++++++ packages/ogm/package.json | 4 ++-- yarn.lock | 8 ++++---- 13 files changed, 38 insertions(+), 35 deletions(-) delete mode 100644 .changeset/afraid-countries-pretend.md delete mode 100644 .changeset/chilly-bugs-pump.md delete mode 100644 .changeset/happy-singers-fail.md delete mode 100644 .changeset/strong-dodos-live.md delete mode 100644 .changeset/warm-students-float.md diff --git a/.changeset/afraid-countries-pretend.md b/.changeset/afraid-countries-pretend.md deleted file mode 100644 index f6d7686ac4b..00000000000 --- a/.changeset/afraid-countries-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@neo4j/graphql": patch ---- - -Fix issue in authorization context generation. diff --git a/.changeset/chilly-bugs-pump.md b/.changeset/chilly-bugs-pump.md deleted file mode 100644 index e72b600425a..00000000000 --- a/.changeset/chilly-bugs-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@neo4j/graphql": patch ---- - -Update translation on fulltext to make it consistent for top level operations and phrase option diff --git a/.changeset/happy-singers-fail.md b/.changeset/happy-singers-fail.md deleted file mode 100644 index 2089a341775..00000000000 --- a/.changeset/happy-singers-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@neo4j/graphql": patch ---- - -Include the `@subscriptionsAuthorization` `events` argument in validation. diff --git a/.changeset/strong-dodos-live.md b/.changeset/strong-dodos-live.md deleted file mode 100644 index 62bb7e6bfd4..00000000000 --- a/.changeset/strong-dodos-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@neo4j/graphql": patch ---- - -Add filtering to interface aggregations diff --git a/.changeset/warm-students-float.md b/.changeset/warm-students-float.md deleted file mode 100644 index c77a95c2762..00000000000 --- a/.changeset/warm-students-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@neo4j/graphql": patch ---- - -Fix an authorization bug present for validation rules with a predicate against a nested field and the Connection API. https://github.com/neo4j/graphql/issues/4292. diff --git a/packages/apollo-federation-subgraph-compatibility/package.json b/packages/apollo-federation-subgraph-compatibility/package.json index bf247a48ac8..b5eed4f3cd2 100644 --- a/packages/apollo-federation-subgraph-compatibility/package.json +++ b/packages/apollo-federation-subgraph-compatibility/package.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/server": "^4.7.0", "@graphql-tools/wrap": "^10.0.0", - "@neo4j/graphql": "^4.4.3", + "@neo4j/graphql": "^4.4.4", "graphql": "16.8.1", "graphql-tag": "^2.12.6", "neo4j-driver": "^5.8.0" diff --git a/packages/graphql-toolbox/CHANGELOG.md b/packages/graphql-toolbox/CHANGELOG.md index 96e8446dc8b..6895d7e984e 100644 --- a/packages/graphql-toolbox/CHANGELOG.md +++ b/packages/graphql-toolbox/CHANGELOG.md @@ -1,5 +1,12 @@ # @neo4j/graphql-toolbox +## 2.1.6 + +### Patch Changes + +- Updated dependencies [[`226e5ed`](https://github.com/neo4j/graphql/commit/226e5edd22d4bff0767392079bedb58313dd606d), [`24728fe`](https://github.com/neo4j/graphql/commit/24728fedd50a8176c54f67009b2afc84dd91418e), [`c09aa9b`](https://github.com/neo4j/graphql/commit/c09aa9bb1a6ee3d13f918b0fed483893055fb1f1), [`7b310d6`](https://github.com/neo4j/graphql/commit/7b310d6d150c788e04af64f69029740913ddffad), [`1bf0773`](https://github.com/neo4j/graphql/commit/1bf077318d0ddbf730edf53d635f507e36fc7374)]: + - @neo4j/graphql@4.4.4 + ## 2.1.5 ### Patch Changes diff --git a/packages/graphql-toolbox/package.json b/packages/graphql-toolbox/package.json index fd184d99a56..19cfd018540 100644 --- a/packages/graphql-toolbox/package.json +++ b/packages/graphql-toolbox/package.json @@ -1,7 +1,7 @@ { "name": "@neo4j/graphql-toolbox", "private": true, - "version": "2.1.5", + "version": "2.1.6", "description": "Developer UI For Neo4j GraphQL", "exports": "./dist/main.js", "main": "./dist/main.js", @@ -52,7 +52,7 @@ "@graphiql/react": "0.20.2", "@neo4j-ndl/base": "2.0.7", "@neo4j-ndl/react": "2.0.14", - "@neo4j/graphql": "4.4.3", + "@neo4j/graphql": "4.4.4", "@neo4j/introspector": "2.0.0", "classnames": "2.3.2", "cm6-graphql": "0.0.12", diff --git a/packages/graphql/CHANGELOG.md b/packages/graphql/CHANGELOG.md index 8d72166bd5a..8c79fbfe432 100644 --- a/packages/graphql/CHANGELOG.md +++ b/packages/graphql/CHANGELOG.md @@ -1,5 +1,19 @@ # @neo4j/graphql +## 4.4.4 + +### Patch Changes + +- [#4247](https://github.com/neo4j/graphql/pull/4247) [`226e5ed`](https://github.com/neo4j/graphql/commit/226e5edd22d4bff0767392079bedb58313dd606d) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Fix issue in authorization context generation. + +- [#4330](https://github.com/neo4j/graphql/pull/4330) [`24728fe`](https://github.com/neo4j/graphql/commit/24728fedd50a8176c54f67009b2afc84dd91418e) Thanks [@angrykoala](https://github.com/angrykoala)! - Update translation on fulltext to make it consistent for top level operations and phrase option + +- [#4144](https://github.com/neo4j/graphql/pull/4144) [`c09aa9b`](https://github.com/neo4j/graphql/commit/c09aa9bb1a6ee3d13f918b0fed483893055fb1f1) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Include the `@subscriptionsAuthorization` `events` argument in validation. + +- [#4308](https://github.com/neo4j/graphql/pull/4308) [`7b310d6`](https://github.com/neo4j/graphql/commit/7b310d6d150c788e04af64f69029740913ddffad) Thanks [@mjfwebb](https://github.com/mjfwebb)! - Add filtering to interface aggregations + +- [#4309](https://github.com/neo4j/graphql/pull/4309) [`1bf0773`](https://github.com/neo4j/graphql/commit/1bf077318d0ddbf730edf53d635f507e36fc7374) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Fix an authorization bug present for validation rules with a predicate against a nested field and the Connection API. https://github.com/neo4j/graphql/issues/4292. + ## 4.4.3 ### Patch Changes diff --git a/packages/graphql/package.json b/packages/graphql/package.json index ff09cf84685..dbdbd406a64 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@neo4j/graphql", - "version": "4.4.3", + "version": "4.4.4", "description": "A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations", "keywords": [ "neo4j", diff --git a/packages/ogm/CHANGELOG.md b/packages/ogm/CHANGELOG.md index a09ab1eba7f..3327fdfd8a6 100644 --- a/packages/ogm/CHANGELOG.md +++ b/packages/ogm/CHANGELOG.md @@ -1,5 +1,12 @@ # @neo4j/graphql-ogm +## 4.4.4 + +### Patch Changes + +- Updated dependencies [[`226e5ed`](https://github.com/neo4j/graphql/commit/226e5edd22d4bff0767392079bedb58313dd606d), [`24728fe`](https://github.com/neo4j/graphql/commit/24728fedd50a8176c54f67009b2afc84dd91418e), [`c09aa9b`](https://github.com/neo4j/graphql/commit/c09aa9bb1a6ee3d13f918b0fed483893055fb1f1), [`7b310d6`](https://github.com/neo4j/graphql/commit/7b310d6d150c788e04af64f69029740913ddffad), [`1bf0773`](https://github.com/neo4j/graphql/commit/1bf077318d0ddbf730edf53d635f507e36fc7374)]: + - @neo4j/graphql@4.4.4 + ## 4.4.3 ### Patch Changes diff --git a/packages/ogm/package.json b/packages/ogm/package.json index dd0e04de573..58603cd0a82 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -1,6 +1,6 @@ { "name": "@neo4j/graphql-ogm", - "version": "4.4.3", + "version": "4.4.4", "description": "GraphQL powered OGM for Neo4j and Javascript applications", "keywords": [ "neo4j", @@ -38,7 +38,7 @@ "@graphql-codegen/plugin-helpers": "^5.0.0", "@graphql-codegen/typescript": "^4.0.0", "@graphql-tools/merge": "^9.0.0", - "@neo4j/graphql": "^4.4.3", + "@neo4j/graphql": "^4.4.4", "prettier": "^2.7.1" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index 78d254a8be4..535c5aeec04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3601,7 +3601,7 @@ __metadata: "@graphql-codegen/plugin-helpers": ^5.0.0 "@graphql-codegen/typescript": ^4.0.0 "@graphql-tools/merge": ^9.0.0 - "@neo4j/graphql": ^4.4.3 + "@neo4j/graphql": ^4.4.4 "@types/jest": 29.5.10 "@types/node": 20.10.3 camelcase: 6.3.0 @@ -3636,7 +3636,7 @@ __metadata: "@graphiql/react": 0.20.2 "@neo4j-ndl/base": 2.0.7 "@neo4j-ndl/react": 2.0.14 - "@neo4j/graphql": 4.4.3 + "@neo4j/graphql": 4.4.4 "@neo4j/introspector": 2.0.0 "@playwright/test": 1.40.1 "@tsconfig/create-react-app": 2.0.1 @@ -3693,7 +3693,7 @@ __metadata: languageName: unknown linkType: soft -"@neo4j/graphql@4.4.3, @neo4j/graphql@^4.0.0, @neo4j/graphql@^4.0.0-beta.0, @neo4j/graphql@^4.4.3, @neo4j/graphql@workspace:packages/graphql": +"@neo4j/graphql@4.4.4, @neo4j/graphql@^4.0.0, @neo4j/graphql@^4.0.0-beta.0, @neo4j/graphql@^4.4.4, @neo4j/graphql@workspace:packages/graphql": version: 0.0.0-use.local resolution: "@neo4j/graphql@workspace:packages/graphql" dependencies: @@ -8863,7 +8863,7 @@ __metadata: "@apollo/federation-subgraph-compatibility": 2.1.0 "@apollo/server": ^4.7.0 "@graphql-tools/wrap": ^10.0.0 - "@neo4j/graphql": ^4.4.3 + "@neo4j/graphql": ^4.4.4 fork-ts-checker-webpack-plugin: 9.0.2 graphql: 16.8.1 graphql-tag: ^2.12.6