diff --git a/docs/asciidoc/type-definitions/cypher.adoc b/docs/asciidoc/type-definitions/cypher.adoc index 7321dc8cce..35e30a5c01 100644 --- a/docs/asciidoc/type-definitions/cypher.adoc +++ b/docs/asciidoc/type-definitions/cypher.adoc @@ -55,6 +55,36 @@ type Query { } ---- + +=== `cypherParams` +Use to inject values into the cypher query from the GraphQL context function. + +Inject into context: + +[source, typescript] +---- +const server = new ApolloServer({ + typeDefs, + context: () => { + return { + cypherParams: { userId: "user-id-01" } + } + } +}); +---- + +Use in cypher query: + +[source, graphql] +---- +type Query { + userPosts: [Post] @cypher(statement: """ + MATCH (:User {id: $cypherParams.userId})-[:POSTED]->(p:Post) + RETURN p + """) +} +---- + == Return values The return value of the Cypher statement must be of the same type to which the directive is applied. diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 95d357c970..c38ed5b5f4 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -69,8 +69,7 @@ "graphql-compose": "^7.25.1", "graphql-parse-resolve-info": "^4.11.0", "jsonwebtoken": "^8.5.1", - "pluralize": "^8.0.0", - "upper-case-first": "^2.0.2" + "pluralize": "^8.0.0" }, "peerDependencies": { "graphql": "^15.0.0", diff --git a/packages/graphql/src/index.ts b/packages/graphql/src/index.ts index bd70b588a2..9a81cdd662 100644 --- a/packages/graphql/src/index.ts +++ b/packages/graphql/src/index.ts @@ -17,5 +17,8 @@ * limitations under the License. */ +import { upperFirst } from "graphql-compose"; + +export { upperFirst }; export { DriverConfig, GraphQLOptionsArg, GraphQLWhereArg, DeleteInfo, GraphQLSortArg } from "./types"; export { Neo4jGraphQL, Neo4jGraphQLConstructor } from "./classes"; diff --git a/packages/graphql/src/schema/make-augmented-schema.test.ts b/packages/graphql/src/schema/make-augmented-schema.test.ts index 55ecfc02bf..d8bfa05f83 100644 --- a/packages/graphql/src/schema/make-augmented-schema.test.ts +++ b/packages/graphql/src/schema/make-augmented-schema.test.ts @@ -65,8 +65,8 @@ describe("makeAugmentedSchema", () => { // Find const nodeFindQuery = queryObject.fields?.find((x) => x.name.value === pluralize(camelCase(type))); - const nodeFindQueryType = ((nodeFindQuery?.type as NonNullTypeNode).type as ListTypeNode) - .type as NamedTypeNode; + const nodeFindQueryType = (((nodeFindQuery?.type as NonNullTypeNode).type as ListTypeNode) + .type as NonNullTypeNode).type as NamedTypeNode; expect(nodeFindQueryType.name.value).toEqual(type); // Options @@ -235,7 +235,7 @@ describe("makeAugmentedSchema", () => { type Node { createdAt: DateTime } - + type Query { nodes: [Node] @cypher(statement: "") } diff --git a/packages/graphql/src/schema/make-augmented-schema.ts b/packages/graphql/src/schema/make-augmented-schema.ts index eb46694c54..29462e6917 100644 --- a/packages/graphql/src/schema/make-augmented-schema.ts +++ b/packages/graphql/src/schema/make-augmented-schema.ts @@ -34,6 +34,7 @@ import { UnionTypeDefinitionNode, } from "graphql"; import { + upperFirst, SchemaComposer, InputTypeComposer, ObjectTypeComposer, @@ -43,7 +44,6 @@ import pluralize from "pluralize"; import { Node, Exclude } from "../classes"; import getAuth from "./get-auth"; import { PrimitiveField, Auth, CustomEnumField } from "../types"; -import { upperFirstLetter } from "../utils"; import { findResolver, createResolver, deleteResolver, cypherResolver, updateResolver } from "./resolvers"; import checkNodeImplementsInterfaces from "./check-node-implements-interfaces"; import * as Scalars from "./scalars"; @@ -588,9 +588,10 @@ function makeAugmentedSchema( refNodes.forEach((n) => { const concatFieldName = `${rel.fieldName}_${n.name}`; - const unionPrefix = `${node.name}${upperFirstLetter(rel.fieldName)}${n.name}`; + const unionPrefix = `${node.name}${upperFirst(rel.fieldName)}${n.name}`; const updateField = `${n.name}UpdateInput`; + const nodeFieldInputName = `${unionPrefix}FieldInput`; const nodeFieldUpdateInputName = `${unionPrefix}UpdateFieldInput`; const nodeFieldDeleteInputName = `${unionPrefix}DeleteFieldInput`; @@ -600,7 +601,7 @@ function makeAugmentedSchema( [n.name]: `${n.name}Where`, }); - const createName = `${node.name}${upperFirstLetter(rel.fieldName)}${n.name}CreateFieldInput`; + const createName = `${node.name}${upperFirst(rel.fieldName)}${n.name}CreateFieldInput`; const create = rel.typeMeta.array ? `[${createName}!]` : createName; if (!composer.has(createName)) { composer.createInputTC({ @@ -612,7 +613,7 @@ function makeAugmentedSchema( }); } - const connectName = `${node.name}${upperFirstLetter(rel.fieldName)}ConnectFieldInput`; + const connectName = `${node.name}${upperFirst(rel.fieldName)}ConnectFieldInput`; const connect = rel.typeMeta.array ? `[${connectName}!]` : `${connectName}`; if (!composer.has(connectName)) { composer.createInputTC({ @@ -635,7 +636,7 @@ function makeAugmentedSchema( name: nodeFieldUpdateInputName, fields: { ...(rel.properties ? { properties: `${rel.properties}UpdateInput` } : {}), - where: `${node.name}${upperFirstLetter(rel.fieldName)}ConnectionWhere`, + where: `${node.name}${upperFirst(rel.fieldName)}ConnectionWhere`, update: updateField, connect, disconnect: rel.typeMeta.array @@ -731,10 +732,11 @@ function makeAugmentedSchema( const n = nodes.find((x) => x.name === rel.typeMeta.name) as Node; const updateField = `${n.name}UpdateInput`; - const nodeFieldInputName = `${node.name}${upperFirstLetter(rel.fieldName)}FieldInput`; - const nodeFieldUpdateInputName = `${node.name}${upperFirstLetter(rel.fieldName)}UpdateFieldInput`; - const nodeFieldDeleteInputName = `${node.name}${upperFirstLetter(rel.fieldName)}DeleteFieldInput`; - const nodeFieldDisconnectInputName = `${node.name}${upperFirstLetter(rel.fieldName)}DisconnectFieldInput`; + + const nodeFieldInputName = `${node.name}${upperFirst(rel.fieldName)}FieldInput`; + const nodeFieldUpdateInputName = `${node.name}${upperFirst(rel.fieldName)}UpdateFieldInput`; + const nodeFieldDeleteInputName = `${node.name}${upperFirst(rel.fieldName)}DeleteFieldInput`; + const nodeFieldDisconnectInputName = `${node.name}${upperFirst(rel.fieldName)}DisconnectFieldInput`; whereInput.addFields({ ...{ [rel.fieldName]: `${n.name}Where`, [`${rel.fieldName}_NOT`]: `${n.name}Where` }, @@ -753,7 +755,7 @@ function makeAugmentedSchema( anyNonNullRelProperties = relFields.some((field) => field.typeMeta.required); } - const createName = `${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`; + const createName = `${node.name}${upperFirst(rel.fieldName)}CreateFieldInput`; const create = rel.typeMeta.array ? `[${createName}!]` : createName; if (!composer.has(createName)) { composer.createInputTC({ @@ -767,7 +769,7 @@ function makeAugmentedSchema( }); } - const connectName = `${node.name}${upperFirstLetter(rel.fieldName)}ConnectFieldInput`; + const connectName = `${node.name}${upperFirst(rel.fieldName)}ConnectFieldInput`; const connect = rel.typeMeta.array ? `[${connectName}!]` : connectName; if (!composer.has(connectName)) { composer.createInputTC({ @@ -798,7 +800,7 @@ function makeAugmentedSchema( name: nodeFieldUpdateInputName, fields: { ...(rel.properties ? { properties: `${rel.properties}UpdateInput` } : {}), - where: `${node.name}${upperFirstLetter(rel.fieldName)}ConnectionWhere`, + where: `${node.name}${upperFirst(rel.fieldName)}ConnectionWhere`, update: updateField, connect, disconnect: rel.typeMeta.array @@ -821,7 +823,7 @@ function makeAugmentedSchema( composer.createInputTC({ name: nodeFieldDeleteInputName, fields: { - where: `${node.name}${upperFirstLetter(rel.fieldName)}ConnectionWhere`, + where: `${node.name}${upperFirst(rel.fieldName)}ConnectionWhere`, ...(n.relationFields.length ? { delete: `${n.name}DeleteInput`, @@ -835,7 +837,7 @@ function makeAugmentedSchema( composer.createInputTC({ name: nodeFieldDisconnectInputName, fields: { - where: `${node.name}${upperFirstLetter(rel.fieldName)}ConnectionWhere`, + where: `${node.name}${upperFirst(rel.fieldName)}ConnectionWhere`, ...(n.relationFields.length ? { disconnect: `${n.name}DisconnectInput`, diff --git a/packages/graphql/src/schema/resolvers/cypher.ts b/packages/graphql/src/schema/resolvers/cypher.ts index 8bb0fc6856..2f30b1799d 100644 --- a/packages/graphql/src/schema/resolvers/cypher.ts +++ b/packages/graphql/src/schema/resolvers/cypher.ts @@ -29,7 +29,7 @@ export default function cypherResolver({ field, statement }: { field: BaseField; async function resolve(_root: any, args: any, _context: unknown) { const context = _context as Context; const cypherStrs: string[] = []; - let params = { ...args, auth: createAuthParam({ context }) }; + let params = { ...args, auth: createAuthParam({ context }), cypherParams: context.cypherParams }; const preAuth = createAuthAndParams({ entity: field, context }); if (preAuth[0]) { diff --git a/packages/graphql/src/schema/resolvers/read.test.ts b/packages/graphql/src/schema/resolvers/read.test.ts index baa05556da..76286dc5ad 100644 --- a/packages/graphql/src/schema/resolvers/read.test.ts +++ b/packages/graphql/src/schema/resolvers/read.test.ts @@ -29,7 +29,7 @@ describe("Read resolver", () => { }; const result = findResolver({ node }); - expect(result.type).toEqual(`[Movie]!`); + expect(result.type).toEqual(`[Movie!]!`); expect(result.resolve).toBeInstanceOf(Function); expect(result.args).toMatchObject({ where: `MovieWhere`, diff --git a/packages/graphql/src/schema/resolvers/read.ts b/packages/graphql/src/schema/resolvers/read.ts index 554a0f75ef..2e7fe39652 100644 --- a/packages/graphql/src/schema/resolvers/read.ts +++ b/packages/graphql/src/schema/resolvers/read.ts @@ -37,7 +37,7 @@ export default function findResolver({ node }: { node: Node }) { } return { - type: `[${node.name}]!`, + type: `[${node.name}!]!`, resolve, args: { where: `${node.name}Where`, options: `${node.name}Options` }, }; diff --git a/packages/graphql/src/translate/create-projection-and-params.ts b/packages/graphql/src/translate/create-projection-and-params.ts index dd1ffa469b..7dbd575e6b 100644 --- a/packages/graphql/src/translate/create-projection-and-params.ts +++ b/packages/graphql/src/translate/create-projection-and-params.ts @@ -215,6 +215,10 @@ function createProjectionAndParams({ } } + const initApocParamsStrs = [ + "auth: $auth", + ...(context.cypherParams ? ["cypherParams: $cypherParams"] : []), + ]; const apocParams = Object.entries(field.args).reduce( (r: { strs: string[]; params: any }, entry) => { const argName = `${param}_${entry[0]}`; @@ -224,9 +228,9 @@ function createProjectionAndParams({ params: { ...r.params, [argName]: entry[1] }, }; }, - { strs: ["auth: $auth"], params: {} } + { strs: initApocParamsStrs, params: {} } ) as { strs: string[]; params: any }; - res.params = { ...res.params, ...apocParams.params }; + res.params = { ...res.params, ...apocParams.params, cypherParams: context.cypherParams }; const expectMultipleValues = referenceNode && cypherField.typeMeta.array ? "true" : "false"; const apocWhere = `${ diff --git a/packages/graphql/src/utils/index.ts b/packages/graphql/src/utils/index.ts index 03603669cd..8296bf8fdc 100644 --- a/packages/graphql/src/utils/index.ts +++ b/packages/graphql/src/utils/index.ts @@ -20,4 +20,3 @@ export { default as trimmer } from "./trimmer"; export { default as execute } from "./execute"; export { default as checkNeo4jCompat } from "./verify-database"; -export { default as upperFirstLetter } from "./upper-first-letter"; diff --git a/packages/graphql/src/utils/upper-first-letter.test.ts b/packages/graphql/src/utils/upper-first-letter.test.ts deleted file mode 100644 index afab37001c..0000000000 --- a/packages/graphql/src/utils/upper-first-letter.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 upperFirstLetter from "./upper-first-letter"; - -describe("upperFirstLetter", () => { - test("should uppercase the first letter and return the word", () => { - const initial = "movie"; - - const result = upperFirstLetter(initial); - - expect(result).toEqual("Movie"); - }); -}); diff --git a/packages/graphql/src/utils/upper-first-letter.ts b/packages/graphql/src/utils/upper-first-letter.ts deleted file mode 100644 index cf45580b20..0000000000 --- a/packages/graphql/src/utils/upper-first-letter.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -function upperFirstLetter(word: string): string { - return word[0].toUpperCase() + word.slice(1); -} - -export default upperFirstLetter; diff --git a/packages/graphql/tests/integration/cypher-params.int.test.ts b/packages/graphql/tests/integration/cypher-params.int.test.ts new file mode 100644 index 0000000000..4ea806e130 --- /dev/null +++ b/packages/graphql/tests/integration/cypher-params.int.test.ts @@ -0,0 +1,186 @@ +/* + * 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 { Driver } from "neo4j-driver"; +import { graphql } from "graphql"; +import { generate } from "randomstring"; +import { Neo4jGraphQL } from "../../src/classes"; +import neo4j from "./neo4j"; + +describe("cypherParams", () => { + let driver: Driver; + + beforeAll(async () => { + driver = await neo4j(); + }); + + afterAll(async () => { + await driver.close(); + }); + + test("should inject cypherParams on top-level cypher query", async () => { + const session = driver.session(); + + const typeDefs = ` + type Movie { + id: ID + } + + type Query { + id: String! @cypher(statement: "RETURN $cypherParams.id") + } + `; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + + const id = generate({ + charset: "alphabetic", + }); + + const source = ` + { + id + } + `; + + try { + const gqlResult = await graphql({ + schema: neoSchema.schema, + source, + contextValue: { driver, cypherParams: { id } }, + }); + + expect(gqlResult.errors).toBeFalsy(); + + expect((gqlResult.data as any).id).toEqual(id); + } finally { + await session.close(); + } + }); + + test("should inject cypherParams on field level nested query", async () => { + const session = driver.session(); + + const typeDefs = ` + type CypherParams { + id: ID + } + + type Movie { + id: ID + cypherParams: CypherParams @cypher(statement: "RETURN $cypherParams") + } + `; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + + const movieId = generate({ + charset: "alphabetic", + }); + const cypherParamsId = generate({ + charset: "alphabetic", + }); + + const source = ` + query($id: ID) { + movies(where: {id: $id}) { + id + cypherParams { + id + } + } + } + `; + + try { + await session.run( + ` + CREATE (:Movie {id: $movieId}) + `, + { movieId } + ); + + const gqlResult = await graphql({ + schema: neoSchema.schema, + source, + variableValues: { + id: movieId, + }, + contextValue: { driver, cypherParams: { id: cypherParamsId } }, + }); + + expect(gqlResult.errors).toBeFalsy(); + + expect((gqlResult.data as any).movies[0]).toEqual({ + id: movieId, + cypherParams: { + id: cypherParamsId, + }, + }); + } finally { + await session.close(); + } + }); + + test("should inject cypherParams on top-level cypher mutation", async () => { + const session = driver.session(); + + const typeDefs = ` + type Movie { + id: ID + } + + type Mutation { + id: String! @cypher(statement: "RETURN $cypherParams.id") + } + `; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + + const id = generate({ + charset: "alphabetic", + }); + + const source = ` + mutation { + id + } + `; + + try { + const gqlResult = await graphql({ + schema: neoSchema.schema, + source, + contextValue: { driver, cypherParams: { id } }, + }); + + expect(gqlResult.errors).toBeFalsy(); + + expect((gqlResult.data as any).id).toEqual(id); + } finally { + await session.close(); + } + }); +}); diff --git a/packages/graphql/tests/tck/tck-test-files/schema/arrays.md b/packages/graphql/tests/tck/tck-test-files/schema/arrays.md index 1cf839f70e..78c456352f 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/arrays.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/arrays.md @@ -105,7 +105,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/comments.md b/packages/graphql/tests/tck/tck-test-files/schema/comments.md index 6b6359ccd0..52994dc8d3 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/comments.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/comments.md @@ -172,7 +172,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/connections/enums.md b/packages/graphql/tests/tck/tck-test-files/schema/connections/enums.md index 9a84ad55aa..cb314595be 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/connections/enums.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/connections/enums.md @@ -361,8 +361,8 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } enum SortDirection { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/connections/unions.md b/packages/graphql/tests/tck/tck-test-files/schema/connections/unions.md index 4c96799666..0fdd6b9884 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/connections/unions.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/connections/unions.md @@ -495,9 +495,9 @@ type Mutation { union Publication = Book | Journal type Query { - authors(where: AuthorWhere, options: AuthorOptions): [Author]! - books(where: BookWhere, options: BookOptions): [Book]! - journals(where: JournalWhere, options: JournalOptions): [Journal]! + authors(where: AuthorWhere, options: AuthorOptions): [Author!]! + books(where: BookWhere, options: BookOptions): [Book!]! + journals(where: JournalWhere, options: JournalOptions): [Journal!]! } input QueryOptions { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/custom-mutations.md b/packages/graphql/tests/tck/tck-test-files/schema/custom-mutations.md index 6ddc3a7a2f..4ed88d19e4 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/custom-mutations.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/custom-mutations.md @@ -108,7 +108,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! testQuery(input: ExampleInput): String testCypherQuery(input: ExampleInput): String } diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directive-preserve.md b/packages/graphql/tests/tck/tck-test-files/schema/directive-preserve.md index 24e2298851..ab1d1d9b0e 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directive-preserve.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directive-preserve.md @@ -90,7 +90,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/access-directives.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/access-directives.md index 0da65ded9a..2a861beabc 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/access-directives.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/access-directives.md @@ -112,7 +112,7 @@ type Mutation { } type Query { - users(where: UserWhere, options: UserOptions): [User]! + users(where: UserWhere, options: UserOptions): [User!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/autogenerate.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/autogenerate.md index feaa02db41..e453da5764 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/autogenerate.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/autogenerate.md @@ -97,7 +97,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/cypher.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/cypher.md index 9ab8d4fad6..e8e821b185 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/cypher.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/cypher.md @@ -144,8 +144,8 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/default.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/default.md index e0866ba7bc..6b666a3cd1 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/default.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/default.md @@ -147,7 +147,7 @@ type Mutation { } type Query { - users(where: UserWhere, options: UserOptions): [User]! + users(where: UserWhere, options: UserOptions): [User!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/exclude.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/exclude.md index 7efabbcf8d..34781b8791 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/exclude.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/exclude.md @@ -126,7 +126,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` @@ -202,7 +202,7 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! } ``` @@ -291,7 +291,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` @@ -389,7 +389,7 @@ type Mutation { type Query { customActorQuery: Actor - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` @@ -590,7 +590,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } enum SortDirection { @@ -687,7 +687,7 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/ignore.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/ignore.md index 6178dff150..a559936e4f 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/ignore.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/ignore.md @@ -115,7 +115,7 @@ type Mutation { } type Query { - users(where: UserWhere, options: UserOptions): [User]! + users(where: UserWhere, options: UserOptions): [User!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/private.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/private.md index bb547d2a3a..6bcf9cd86a 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/private.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/private.md @@ -84,7 +84,7 @@ type Mutation { } type Query { - users(where: UserWhere, options: UserOptions): [User]! + users(where: UserWhere, options: UserOptions): [User!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/directives/timestamps.md b/packages/graphql/tests/tck/tck-test-files/schema/directives/timestamps.md index bdb4157004..f3f878f0e5 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/directives/timestamps.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/directives/timestamps.md @@ -109,7 +109,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/enum.md b/packages/graphql/tests/tck/tck-test-files/schema/enum.md index 70bc69c5a5..9202dbffdb 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/enum.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/enum.md @@ -90,7 +90,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/extend.md b/packages/graphql/tests/tck/tck-test-files/schema/extend.md index e0e10cbd7d..c8729ee3a4 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/extend.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/extend.md @@ -101,7 +101,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/inputs.md b/packages/graphql/tests/tck/tck-test-files/schema/inputs.md index 2850f16559..8f911e3ca6 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/inputs.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/inputs.md @@ -95,7 +95,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! name(input: NodeInput): String } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/interfaces.md b/packages/graphql/tests/tck/tck-test-files/schema/interfaces.md index f98f39a18c..fc640102b0 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/interfaces.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/interfaces.md @@ -177,7 +177,7 @@ interface Node { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } enum SortDirection { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/issues/#162.md b/packages/graphql/tests/tck/tck-test-files/schema/issues/#162.md index 1d9ac9df69..61ac3ed0f6 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/issues/#162.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/issues/#162.md @@ -57,9 +57,9 @@ type Mutation { } type Query { - tigers(where: TigerWhere, options: TigerOptions): [Tiger]! - tigerJawLevel2s(where: TigerJawLevel2Where, options: TigerJawLevel2Options): [TigerJawLevel2]! - tigerJawLevel2Part1s(where: TigerJawLevel2Part1Where, options: TigerJawLevel2Part1Options): [TigerJawLevel2Part1]! + tigers(where: TigerWhere, options: TigerOptions): [Tiger!]! + tigerJawLevel2s(where: TigerJawLevel2Where, options: TigerJawLevel2Options): [TigerJawLevel2!]! + tigerJawLevel2Part1s(where: TigerJawLevel2Part1Where, options: TigerJawLevel2Part1Options): [TigerJawLevel2Part1!]! } enum SortDirection { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/issues/#200.md b/packages/graphql/tests/tck/tck-test-files/schema/issues/#200.md index 0f81e8fda4..85d1867473 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/issues/#200.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/issues/#200.md @@ -112,7 +112,7 @@ type Mutation { } type Query { - categories(where: CategoryWhere, options: CategoryOptions): [Category]! + categories(where: CategoryWhere, options: CategoryOptions): [Category!]! } enum SortDirection { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/null.md b/packages/graphql/tests/tck/tck-test-files/schema/null.md index 1df1a6a06f..9bfefa5f38 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/null.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/null.md @@ -227,7 +227,7 @@ input PointInput { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } type UpdateMoviesMutationResponse { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/relationship-properties.md b/packages/graphql/tests/tck/tck-test-files/schema/relationship-properties.md index cad6f82ca3..148b7fe848 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/relationship-properties.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/relationship-properties.md @@ -335,8 +335,8 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } enum SortDirection { diff --git a/packages/graphql/tests/tck/tck-test-files/schema/relationship.md b/packages/graphql/tests/tck/tck-test-files/schema/relationship.md index 705b830ddc..308dbb165c 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/relationship.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/relationship.md @@ -206,8 +206,8 @@ type Mutation { } type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! + actors(where: ActorWhere, options: ActorOptions): [Actor!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } enum SortDirection { @@ -503,20 +503,6 @@ input MovieWhere { actors_NOT: ActorWhere } -type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - deleteActors(where: ActorWhere, delete: ActorDeleteInput): DeleteInfo! - updateActors(where: ActorWhere, update: ActorUpdateInput, connect: ActorConnectInput, disconnect: ActorDisconnectInput, create: ActorRelationInput, delete: ActorDeleteInput): UpdateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteMovies(where: MovieWhere, delete: MovieDeleteInput): DeleteInfo! - updateMovies(where: MovieWhere, update: MovieUpdateInput, connect: MovieConnectInput, disconnect: MovieDisconnectInput, create: MovieRelationInput, delete: MovieDeleteInput): UpdateMoviesMutationResponse! -} - -type Query { - actors(where: ActorWhere, options: ActorOptions): [Actor]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! -} - enum SortDirection { """Sort by field values in ascending order.""" ASC @@ -532,6 +518,40 @@ type UpdateActorsMutationResponse { type UpdateMoviesMutationResponse { movies: [Movie!]! } + +type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + deleteActors( + where: ActorWhere + delete: ActorDeleteInput + ): DeleteInfo! + updateActors( + where: ActorWhere + update: ActorUpdateInput + connect: ActorConnectInput + disconnect: ActorDisconnectInput + create: ActorRelationInput + delete: ActorDeleteInput + ): UpdateActorsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + deleteMovies( + where: MovieWhere + delete: MovieDeleteInput + ): DeleteInfo! + updateMovies( + where: MovieWhere + update: MovieUpdateInput + connect: MovieConnectInput + disconnect: MovieDisconnectInput + create: MovieRelationInput + delete: MovieDeleteInput + ): UpdateMoviesMutationResponse! +} + +type Query { + actors(where: ActorWhere, options: ActorOptions): [Actor!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! +} ``` --- diff --git a/packages/graphql/tests/tck/tck-test-files/schema/scalar.md b/packages/graphql/tests/tck/tck-test-files/schema/scalar.md index b1a0f2436e..a174471190 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/scalar.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/scalar.md @@ -93,7 +93,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/simple.md b/packages/graphql/tests/tck/tck-test-files/schema/simple.md index 655158e961..740523f94c 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/simple.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/simple.md @@ -116,7 +116,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/types/bigint.md b/packages/graphql/tests/tck/tck-test-files/schema/types/bigint.md index 742889de65..55258a130e 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/types/bigint.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/types/bigint.md @@ -99,7 +99,7 @@ type Mutation { } type Query { - files(where: FileWhere, options: FileOptions): [File]! + files(where: FileWhere, options: FileOptions): [File!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/types/datetime.md b/packages/graphql/tests/tck/tck-test-files/schema/types/datetime.md index c4be33081c..d79d67cfa0 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/types/datetime.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/types/datetime.md @@ -100,7 +100,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/types/points.md b/packages/graphql/tests/tck/tck-test-files/schema/types/points.md index 326dfd4e3d..06e353fe51 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/types/points.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/types/points.md @@ -102,7 +102,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` @@ -205,7 +205,7 @@ type Mutation { } type Query { - machines(where: MachineWhere, options: MachineOptions): [Machine]! + machines(where: MachineWhere, options: MachineOptions): [Machine!]! } ``` @@ -284,7 +284,7 @@ type Mutation { } type Query { - movies(where: MovieWhere, options: MovieOptions): [Movie]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } ``` @@ -363,7 +363,7 @@ type Mutation { } type Query { - machines(where: MachineWhere, options: MachineOptions): [Machine]! + machines(where: MachineWhere, options: MachineOptions): [Machine!]! } ``` diff --git a/packages/graphql/tests/tck/tck-test-files/schema/unions.md b/packages/graphql/tests/tck/tck-test-files/schema/unions.md index 9c6fda2ba8..91bce6d288 100644 --- a/packages/graphql/tests/tck/tck-test-files/schema/unions.md +++ b/packages/graphql/tests/tck/tck-test-files/schema/unions.md @@ -264,8 +264,8 @@ type Mutation { } type Query { - genres(where: GenreWhere, options: GenreOptions): [Genre]! - movies(where: MovieWhere, options: MovieOptions): [Movie]! + genres(where: GenreWhere, options: GenreOptions): [Genre!]! + movies(where: MovieWhere, options: MovieOptions): [Movie!]! } input QueryOptions { diff --git a/packages/ogm/package.json b/packages/ogm/package.json index 3928bce0dd..73fcb6e626 100644 --- a/packages/ogm/package.json +++ b/packages/ogm/package.json @@ -29,8 +29,7 @@ "@graphql-tools/merge": "^6.2.13", "@neo4j/graphql": "^1.0.2", "camelcase": "^6.2.0", - "pluralize": "^8.0.0", - "upper-case-first": "^2.0.2" + "pluralize": "^8.0.0" }, "peerDependencies": { "graphql": "^15.0.0", diff --git a/packages/ogm/src/classes/Model.ts b/packages/ogm/src/classes/Model.ts index a44364c001..7a3ef7849c 100644 --- a/packages/ogm/src/classes/Model.ts +++ b/packages/ogm/src/classes/Model.ts @@ -20,8 +20,7 @@ import { DocumentNode, graphql, parse, print } from "graphql"; import pluralize from "pluralize"; import camelCase from "camelcase"; -import { Neo4jGraphQL } from "@neo4j/graphql"; -import { upperCaseFirst } from "upper-case-first"; +import { Neo4jGraphQL, upperFirst } from "@neo4j/graphql"; import { GraphQLOptionsArg, GraphQLWhereArg, DeleteInfo } from "../types"; export interface ModelConstructor { @@ -122,7 +121,7 @@ class Model { context?: any; rootValue?: any; } = {}): Promise { - const mutationName = `create${upperCaseFirst(this.namePluralized)}`; + const mutationName = `create${upperFirst(this.namePluralized)}`; let selection = ""; if (selectionSet) { @@ -174,7 +173,7 @@ class Model { context?: any; rootValue?: any; } = {}): Promise { - const mutationName = `update${upperCaseFirst(this.namePluralized)}`; + const mutationName = `update${upperFirst(this.namePluralized)}`; let selection = ""; if (selectionSet) { @@ -239,7 +238,7 @@ class Model { context?: any; rootValue?: any; } = {}): Promise { - const mutationName = `delete${upperCaseFirst(this.namePluralized)}`; + const mutationName = `delete${upperFirst(this.namePluralized)}`; const argWorthy = where || deleteInput; diff --git a/packages/ogm/src/classes/OGM.test.ts b/packages/ogm/src/classes/OGM.test.ts index 935d2c59e4..da68dc4d90 100644 --- a/packages/ogm/src/classes/OGM.test.ts +++ b/packages/ogm/src/classes/OGM.test.ts @@ -17,7 +17,6 @@ * limitations under the License. */ -import { printSchema } from "graphql"; import OGM from "./OGM"; describe("OGM", () => { @@ -27,16 +26,6 @@ describe("OGM", () => { }); describe("methods", () => { - describe("constructor", () => { - test("should evoke the filterDocument method", () => { - const ogm = new OGM({ typeDefs: `type User @auth {id:ID}` }); - - const printed = printSchema(ogm.neoSchema.schema); - - expect(printed.includes("@auth")).toBeFalsy(); - }); - }); - describe("checkNeo4jCompat", () => { test("should neo4j-driver Driver missing", async () => { // @ts-ignore diff --git a/packages/ogm/src/classes/OGM.ts b/packages/ogm/src/classes/OGM.ts index 5350440441..01eeebf79e 100644 --- a/packages/ogm/src/classes/OGM.ts +++ b/packages/ogm/src/classes/OGM.ts @@ -24,22 +24,26 @@ import { filterDocument } from "../utils"; export type OGMConstructor = Neo4jGraphQLConstructor; class OGM { - public neoSchema: Neo4jGraphQL; - public models: Model[]; - public input: OGMConstructor; + checkNeo4jCompat: () => Promise; constructor(input: OGMConstructor) { - this.input = input; const { typeDefs, ...rest } = input; - this.neoSchema = new Neo4jGraphQL({ + const neoSchema = new Neo4jGraphQL({ ...rest, typeDefs: filterDocument(typeDefs), }); - this.models = this.neoSchema.nodes.map((n) => { + this.checkNeo4jCompat = function checkNeo4jCompat() { + return neoSchema.checkNeo4jCompat({ + driver: rest.driver, + ...(rest.config?.driverConfig ? { driverConfig: rest.config.driverConfig } : {}), + }); + }; + + this.models = neoSchema.nodes.map((n) => { const selectionSet = ` { ${[n.primitiveFields, n.scalarFields, n.enumFields, n.dateTimeFields].reduce( @@ -50,7 +54,7 @@ class OGM { `; return new Model({ - neoSchema: this.neoSchema, + neoSchema, name: n.name, selectionSet, }); @@ -66,12 +70,6 @@ class OGM { return found; } - - checkNeo4jCompat(): Promise { - const { driver, config: { driverConfig } = {} } = this.input; - - return this.neoSchema.checkNeo4jCompat({ driver, driverConfig }); - } } export default OGM; diff --git a/packages/ogm/tests/integration/ogm.int.test.ts b/packages/ogm/tests/integration/ogm.int.test.ts index 3b102849fd..f1dde0fa85 100644 --- a/packages/ogm/tests/integration/ogm.int.test.ts +++ b/packages/ogm/tests/integration/ogm.int.test.ts @@ -50,6 +50,39 @@ describe("OGM", () => { await session.close(); }); + test("should filter the document and remove directives such as auth", async () => { + const session = driver.session(); + + const typeDefs = ` + type Movie @auth(rules: [{ isAuthenticated: true }]){ + id: ID + } + `; + + const ogm = new OGM({ typeDefs, driver }); + + const id = generate({ + charset: "alphabetic", + }); + + try { + await ogm.checkNeo4jCompat(); + + await session.run(` + CREATE (:Movie {id: "${id}"}) + `); + + const Movie = ogm.model("Movie"); + + const movies = await Movie?.find({ where: { id } }); + + // should return without error due to the fact auth should be removed + expect(movies).toEqual([{ id }]); + } finally { + await session.close(); + } + }); + describe("find", () => { test("find a single node", async () => { const session = driver.session(); diff --git a/yarn.lock b/yarn.lock index 565f99fb08..08bff27954 100644 --- a/yarn.lock +++ b/yarn.lock @@ -917,7 +917,6 @@ __metadata: semver: 7.3.5 ts-jest: 26.1.4 typescript: 3.9.7 - upper-case-first: ^2.0.2 peerDependencies: graphql: ^15.0.0 neo4j-driver: ^4.1.0 @@ -960,7 +959,6 @@ __metadata: semver: 7.3.5 ts-jest: 26.1.4 typescript: 3.9.7 - upper-case-first: ^2.0.2 peerDependencies: graphql: ^15.0.0 neo4j-driver: ^4.1.0