diff --git a/.changeset/sixty-clocks-design.md b/.changeset/sixty-clocks-design.md new file mode 100644 index 0000000000..af71152ae1 --- /dev/null +++ b/.changeset/sixty-clocks-design.md @@ -0,0 +1,5 @@ +--- +"@neo4j/introspector": patch +--- + +Changed how "@neo4j/introspector" generates list fields that now are generated as a list of non-nullable elements, as a list of nullable elements is not supported by Neo4j. diff --git a/.changeset/tame-melons-confess.md b/.changeset/tame-melons-confess.md new file mode 100644 index 0000000000..8a3c1e57d1 --- /dev/null +++ b/.changeset/tame-melons-confess.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Added a validation rule to avoid defining fields as lists of nullable elements, as Neo4j does not support this. \ No newline at end of file diff --git a/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts new file mode 100644 index 0000000000..a7a658497d --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts @@ -0,0 +1,108 @@ +/* + * 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 { Kind, type ASTVisitor, type ObjectTypeDefinitionNode, type TypeNode } from "graphql"; + +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import { + cypherDirective, + nodeDirective, + relationshipDirective, + relationshipPropertiesDirective, +} from "../../../../graphql/directives"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { getInnerTypeName, getPrettyName } from "../utils/utils"; + +/** + * Validates that list types used in type annotated with the node directive are supported by Neo4j + **/ +export function ValidListInNodeType(context: SDLValidationContext): ASTVisitor { + return { + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent) { + const { directives } = objectTypeDefinitionNode; + + const nodeUsage = directives?.find((directive) => directive.name.value === nodeDirective.name); + const relationshipPropertiesUsage = directives?.find( + (directive) => directive.name.value === relationshipPropertiesDirective.name + ); + if (!directives) { + return; // Skip when no directives are present + } + + if (!nodeUsage && !relationshipPropertiesUsage) { + return; // Skip if is the type is neither annotated with node nor relationshipProperties + } + + objectTypeDefinitionNode.fields?.forEach((fieldDefinitionNode) => { + const { type, directives } = fieldDefinitionNode; + if ( + directives && + directives.some( + (directive) => + directive.name.value === cypherDirective.name || + directive.name.value === relationshipDirective.name + ) + ) { + return; // Skip cypher fields and relationship fields, relationship fields have their own validation + } + const { isValid, errorMsg, errorPath } = assertValid(() => { + const typePath = getTypePath(type); + if (typePath.includes(Kind.LIST_TYPE)) { + const wrappedType = getInnerTypeName(type); + const validTypePaths: string[][] = [ + [Kind.LIST_TYPE, Kind.NON_NULL_TYPE, wrappedType], + [Kind.NON_NULL_TYPE, Kind.LIST_TYPE, Kind.NON_NULL_TYPE, wrappedType], + ]; + if (!findTypePathInTypePaths(typePath, validTypePaths)) { + const typeStr = getPrettyName(type); + + const directiveName = (nodeUsage ?? relationshipPropertiesUsage)?.name?.value; + throw new DocumentValidationError( + `List of nullable elements are not supported in "@${directiveName}" types. Found: ${typeStr}`, + [] + ); + } + } + }); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [objectTypeDefinitionNode.name.value, fieldDefinitionNode.name.value, ...errorPath], + errorMsg, + }) + ); + } + }); + }, + }; +} + +function getTypePath(typeNode: TypeNode, currentPath: string[] = []): string[] { + if (typeNode.kind === Kind.NON_NULL_TYPE || typeNode.kind === Kind.LIST_TYPE) { + return getTypePath(typeNode.type, [...currentPath, typeNode.kind]); + } + return [...currentPath, typeNode.name.value]; +} + +function findTypePathInTypePaths(typePathToFind: string[], typePaths: string[][]): boolean { + const typePathString = typePathToFind.join(); + return typePaths.some((typePath) => typePathString === typePath.join()); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts deleted file mode 100644 index f4302f5024..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts +++ /dev/null @@ -1,54 +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 { Kind } from "graphql"; -import type { ListTypeNode, NonNullTypeNode, ASTVisitor, FieldDefinitionNode } from "graphql"; - -export function WarnIfListOfListsFieldDefinition(): ASTVisitor { - let warningAlreadyIssued = false; - - return { - FieldDefinition(field: FieldDefinitionNode) { - if (!warningAlreadyIssued) { - const listTypeNode = getListTypeNode(field); - - if (listTypeNode) { - if (getListTypeNode(listTypeNode)) { - console.warn( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." - ); - warningAlreadyIssued = true; - } - } - } - }, - }; -} - -function getListTypeNode(definition: FieldDefinitionNode | ListTypeNode | NonNullTypeNode): ListTypeNode | undefined { - if (definition.type.kind === Kind.NON_NULL_TYPE) { - return getListTypeNode(definition.type); - } - - if (definition.type.kind === Kind.LIST_TYPE) { - return definition.type; - } - - return; -} diff --git a/packages/graphql/src/schema/validation/validate-document.test.ts b/packages/graphql/src/schema/validation/validate-document.test.ts index da4176b8bc..029a927ecc 100644 --- a/packages/graphql/src/schema/validation/validate-document.test.ts +++ b/packages/graphql/src/schema/validation/validate-document.test.ts @@ -74,57 +74,147 @@ describe("authorization warning", () => { }); }); -describe("list of lists warning", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); +describe("valid list fields in @node and @relationshipProperties types", () => { + test("should raise when a list of nullable elements is found", () => { + const doc = gql` + type Movie @node { + id: [ID] + } + `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@node" types. Found: [ID]' + ); + expect(errors[0]).toHaveProperty("path", ["Movie", "id"]); }); - afterEach(() => { - warn.mockReset(); + test("should raise when a list of nullable elements is found on relationship properties", () => { + const relationshipProps = gql` + type ActedIn @relationshipProperties { + roles: [String] + } + `; + const doc = gql` + ${relationshipProps} + type Movie @node { + title: String + } + + type Actor @node { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + `; + const objects = relationshipProps.definitions as ObjectTypeDefinitionNode[]; + const executeValidate = () => + validateDocument({ + document: doc, + features: {}, + additionalDefinitions: { enums: [], interfaces: [], unions: [], objects }, + }); + + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@relationshipProperties" types. Found: [String]' + ); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "roles"]); }); - test("list of lists warning only occurs once for multiple fields", () => { + test("should not raise when a list of nullable elements is found on relationship properties", () => { + const relationshipProps = gql` + type ActedIn @relationshipProperties { + roles: [String!] + } + `; const doc = gql` + ${relationshipProps} type Movie @node { - id: [[ID]] + title: String } type Actor @node { - name: [[String]] + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; + const objects = relationshipProps.definitions as ObjectTypeDefinitionNode[]; + const executeValidate = () => + validateDocument({ + document: doc, + features: {}, + additionalDefinitions: { enums: [], interfaces: [], unions: [], objects }, + }); - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); + const errors = getError(executeValidate); - expect(warn).toHaveBeenCalledWith( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); + + test("should raise when a list of list is found", () => { + const doc = gql` + type Movie @node { + id: [[ID]!] + } + `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@node" types. Found: [[ID]!]' ); - expect(warn).toHaveBeenCalledOnce(); + expect(errors[0]).toHaveProperty("path", ["Movie", "id"]); }); - test("works for non-nullable lists", () => { + test("should not raise when a list of non-nullable elements is found", () => { const doc = gql` type Movie @node { - id: [[ID!]!]! + id: [ID!] } `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); + const errors = getError(executeValidate); - expect(warn).toHaveBeenCalledWith( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." - ); - expect(warn).toHaveBeenCalledOnce(); + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); + + test("should not raise when a list of non-nullable elements is found on @cypher fields", () => { + const doc = gql` + type Movie @node { + id: [ID] @cypher(statement: "RETURN [1,2,3] as ids", columnName: "ids") + } + `; + + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); + + test("should not raise when a list of non-nullable elements is found on non @node types", () => { + const doc = gql` + type Movie { + id: [ID] + } + `; + + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + + expect(errors).toBeInstanceOf(NoErrorThrownError); }); }); @@ -1225,7 +1315,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: "dummy") + statuses: [Status!] @default(value: "dummy") } `; @@ -1259,7 +1349,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: ["dummy"]) + statuses: [Status!] @default(value: ["dummy"]) } `; @@ -1293,7 +1383,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: [PENDING]) + statuses: [Status!] @default(value: [PENDING]) } `; const enums = enumTypes.definitions as EnumTypeDefinitionNode[]; @@ -1350,7 +1440,7 @@ describe("validation 2.0", () => { test("@default on int list must be list of int values", () => { const doc = gql` type User @node { - ages: [Int] @default(value: ["dummy"]) + ages: [Int!] @default(value: ["dummy"]) } `; @@ -1374,7 +1464,7 @@ describe("validation 2.0", () => { test("@default on int list must be list of int values correct", () => { const doc = gql` type User @node { - ages: [Int] @default(value: [12]) + ages: [Int!] @default(value: [12]) } `; @@ -1429,7 +1519,7 @@ describe("validation 2.0", () => { test("@default on float list must be list of float values", () => { const doc = gql` type User @node { - avgs: [Float] @default(value: [1]) + avgs: [Float!] @default(value: [1]) } `; @@ -1453,7 +1543,7 @@ describe("validation 2.0", () => { test("@default on float list must be list of float values correct", () => { const doc = gql` type User @node { - avgs: [Float] @default(value: [1.2]) + avgs: [Float!] @default(value: [1.2]) } `; @@ -1508,7 +1598,7 @@ describe("validation 2.0", () => { test("@default on boolean list must be list of boolean values", () => { const doc = gql` type User @node { - statuses: [Boolean] @default(value: [2]) + statuses: [Boolean!] @default(value: [2]) } `; @@ -1532,7 +1622,7 @@ describe("validation 2.0", () => { test("@default on boolean list must be list of boolean values correct", () => { const doc = gql` type User @node { - statuses: [Boolean] @default(value: [true]) + statuses: [Boolean!] @default(value: [true]) } `; @@ -1587,7 +1677,7 @@ describe("validation 2.0", () => { test("@default on string list must be list of string values", () => { const doc = gql` type User @node { - names: [String] @default(value: [2]) + names: [String!] @default(value: [2]) } `; @@ -1611,7 +1701,7 @@ describe("validation 2.0", () => { test("@default on string list must be list of string values correct", () => { const doc = gql` type User @node { - names: [String] @default(value: ["Bob"]) + names: [String!] @default(value: ["Bob"]) } `; @@ -1649,7 +1739,7 @@ describe("validation 2.0", () => { test("@default on ID list must be list of ID values", () => { const doc = gql` type User @node { - ids: [ID] @default(value: [2]) + ids: [ID!] @default(value: [2]) } `; @@ -1673,7 +1763,7 @@ describe("validation 2.0", () => { test("@default on ID list must be list of ID values correct", () => { const doc = gql` type User @node { - ids: [ID] @default(value: ["123-223"]) + ids: [ID!] @default(value: ["123-223"]) } `; @@ -1856,7 +1946,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: "dummy") + statuses: [Status!] @coalesce(value: "dummy") } `; @@ -1890,7 +1980,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: ["dummy"]) + statuses: [Status!] @coalesce(value: ["dummy"]) } `; @@ -1924,7 +2014,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: [PENDING]) + statuses: [Status!] @coalesce(value: [PENDING]) } `; @@ -1982,7 +2072,7 @@ describe("validation 2.0", () => { test("@coalesce on int list must be list of int values", () => { const doc = gql` type User @node { - ages: [Int] @coalesce(value: ["dummy"]) + ages: [Int!] @coalesce(value: ["dummy"]) } `; @@ -2006,7 +2096,7 @@ describe("validation 2.0", () => { test("@coalesce on int list must be list of int values correct", () => { const doc = gql` type User @node { - ages: [Int] @coalesce(value: [12]) + ages: [Int!] @coalesce(value: [12]) } `; @@ -2061,7 +2151,7 @@ describe("validation 2.0", () => { test("@coalesce on float list must be list of float values", () => { const doc = gql` type User @node { - avgs: [Float] @coalesce(value: [1]) + avgs: [Float!] @coalesce(value: [1]) } `; @@ -2085,7 +2175,7 @@ describe("validation 2.0", () => { test("@coalesce on float list must be list of float values correct", () => { const doc = gql` type User @node { - avgs: [Float] @coalesce(value: [1.2]) + avgs: [Float!] @coalesce(value: [1.2]) } `; @@ -2143,7 +2233,7 @@ describe("validation 2.0", () => { test("@coalesce on boolean list must be list of boolean values", () => { const doc = gql` type User @node { - statuses: [Boolean] @coalesce(value: [2]) + statuses: [Boolean!] @coalesce(value: [2]) } `; @@ -2167,7 +2257,7 @@ describe("validation 2.0", () => { test("@coalesce on boolean list must be list of boolean values correct", () => { const doc = gql` type User @node { - statuses: [Boolean] @coalesce(value: [true]) + statuses: [Boolean!] @coalesce(value: [true]) } `; @@ -2222,7 +2312,7 @@ describe("validation 2.0", () => { test("@coalesce on string list must be list of string values", () => { const doc = gql` type User @node { - names: [String] @coalesce(value: [2]) + names: [String!] @coalesce(value: [2]) } `; @@ -2246,7 +2336,7 @@ describe("validation 2.0", () => { test("@coalesce on string list must be list of string values correct", () => { const doc = gql` type User @node { - names: [String] @coalesce(value: ["Bob"]) + names: [String!] @coalesce(value: ["Bob"]) } `; @@ -2284,7 +2374,7 @@ describe("validation 2.0", () => { test("@coalesce on ID list must be list of ID values", () => { const doc = gql` type User @node { - ids: [ID] @coalesce(value: [2]) + ids: [ID!] @coalesce(value: [2]) } `; @@ -2308,7 +2398,7 @@ describe("validation 2.0", () => { test("@coalesce on ID list must be list of ID values correct", () => { const doc = gql` type User @node { - ids: [ID] @coalesce(value: ["123-223"]) + ids: [ID!] @coalesce(value: ["123-223"]) } `; @@ -3081,7 +3171,7 @@ describe("validation 2.0", () => { test("should throw cannot auto-generate an array", () => { const doc = gql` type Movie @node { - name: [ID] @id + name: [ID!] @id } `; @@ -3109,7 +3199,7 @@ describe("validation 2.0", () => { test("@timestamp cannot autogenerate array", () => { const doc = gql` type User @node { - lastSeenAt: [DateTime] @timestamp + lastSeenAt: [DateTime!] @timestamp } `; @@ -3125,7 +3215,7 @@ describe("validation 2.0", () => { test("should throw cannot timestamp on array of DateTime", () => { const doc = gql` type Movie @node { - name: [DateTime] @timestamp(operations: [CREATE]) + name: [DateTime!] @timestamp(operations: [CREATE]) } `; @@ -3173,7 +3263,7 @@ describe("validation 2.0", () => { test("@id autogenerate cannot autogenerate array", () => { const doc = gql` type User @node { - uid: [ID] @id + uid: [ID!] @id } `; @@ -5245,7 +5335,7 @@ describe("validation 2.0", () => { const relationshipProperties = gql` type ActedIn @relationshipProperties { id: ID @cypher(statement: "RETURN id(this) as id", columnName: "id") - roles: [String] + roles: [String!] } `; const doc = gql` @@ -5518,7 +5608,7 @@ describe("validation 2.0", () => { test("simple list", () => { const doc = gql` type Post @node { - titles: [String] + titles: [String!] } `; diff --git a/packages/graphql/src/schema/validation/validate-document.ts b/packages/graphql/src/schema/validation/validate-document.ts index 2fbc331b39..73860293c9 100644 --- a/packages/graphql/src/schema/validation/validate-document.ts +++ b/packages/graphql/src/schema/validation/validate-document.ts @@ -61,10 +61,10 @@ import { SchemaOrTypeDirectives, } from "./custom-rules/valid-types/valid-directive-combination"; import { ValidFieldTypes } from "./custom-rules/valid-types/valid-field-types"; +import { ValidListInNodeType } from "./custom-rules/valid-types/valid-list-in-node-type"; import { ValidObjectType } from "./custom-rules/valid-types/valid-object-type"; import { WarnIfAuthorizationFeatureDisabled } from "./custom-rules/warnings/authorization-feature-disabled"; import { WarnIfAMaxLimitCanBeBypassedThroughInterface } from "./custom-rules/warnings/limit-max-can-be-bypassed"; -import { WarnIfListOfListsFieldDefinition } from "./custom-rules/warnings/list-of-lists"; import { WarnObjectFieldsWithoutResolver } from "./custom-rules/warnings/object-fields-without-resolver"; import { WarnIfSubscriptionsAuthorizationMissing } from "./custom-rules/warnings/subscriptions-authorization-missing"; import { validateSchemaCustomizations } from "./validate-schema-customizations"; @@ -224,12 +224,12 @@ function runValidationRulesOnFilteredDocument({ ValidDirectiveInheritance, DirectiveArgumentOfCorrectType(false), WarnIfAuthorizationFeatureDisabled(features?.authorization), - WarnIfListOfListsFieldDefinition, ErrorIfSingleRelationships, WarnIfAMaxLimitCanBeBypassedThroughInterface(), WarnObjectFieldsWithoutResolver({ customResolvers: asArray(userCustomResolvers ?? []), }), + ValidListInNodeType, WarnIfSubscriptionsAuthorizationMissing(Boolean(features?.subscriptions)), ], schema diff --git a/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts index 3ea369c48b..442659596b 100644 --- a/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts @@ -39,10 +39,10 @@ describe("Create Subscription", () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - actors: [${typeActor}] + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) } type ${typeActor} @subscription(events: []) @node { name: String diff --git a/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts index 3a708e3df6..d431783a2b 100644 --- a/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts @@ -45,7 +45,7 @@ describe("Delete Subscriptions - with interfaces, unions and nested operations", typePerson = testHelper.createUniqueType("Person"); typeInfluencer = testHelper.createUniqueType("Influencer"); - typeDefs = ` + typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String! actors: [${typeActor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) diff --git a/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts index b54ce06845..396d0cbe32 100644 --- a/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts @@ -39,10 +39,10 @@ describe("$name Delete Subscription", () => { beforeEach(async () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - actors: [${typeActor}] + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) } type ${typeActor} @subscription(events: []) @node { name: String diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts index 25dbb0d619..9b2a65cfaa 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts index 3cbbf30ab0..8d2d121640 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Create Subscription with filters valid of number types (Int, Float, Bi type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts index 8284c87140..d9709dca17 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Create Subscription with filters valid on string types (String, ID)", type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts index 89f218c513..c1418b5f5b 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts @@ -46,7 +46,7 @@ describe("Create Subscription with optional filters valid for all types", () => averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts index 1fd34f0da2..c4d56cf6d9 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts index 9505e41b6e..4f890631c9 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Delete Subscription", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts index 2ce138bf73..8dc3cf1b98 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Delete Subscription", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts index 6e8bde45dc..220602813c 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts @@ -46,7 +46,7 @@ describe("Delete Subscription", () => { averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts index 9f8fb9426a..b41b5598f4 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts index 7e9aa65fd7..933f667762 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Update Subscriptions", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts index ac06314db6..385eaf6dd6 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Update Subscriptions", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts index b79a44c280..de34714ace 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts @@ -48,7 +48,7 @@ describe("Update Subscriptions", () => { averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } type ${typeActor} @node { diff --git a/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts index 6bc7a00dd4..d41fb36360 100644 --- a/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts @@ -39,15 +39,14 @@ describe("Delete Subscription", () => { beforeEach(async () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` - type ${typeMovie} @node { + const typeDefs = /* GraphQL */ ` + type ${typeMovie} @node { title: String - actors: [${typeActor}] - } - type ${typeActor} @subscription(events: []) @node { - name: String - } - `; + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + type ${typeActor} @subscription(events: []) @node { + name: String + }`; const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs, diff --git a/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts index bb01621134..2e4ef1e94a 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts @@ -39,8 +39,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -83,8 +83,8 @@ describe("array-pop-and-push", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) - moreTags: [String] + tags: [String!] @authentication(operations: [UPDATE]) + moreTags: [String!] } `; @@ -131,8 +131,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts index 2744e9270d..7e3e1468fe 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts @@ -36,8 +36,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts index 074f8d7459..7a74a84cbc 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts @@ -39,7 +39,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -83,8 +83,8 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - otherTags: [String] + tags: [String!] + otherTags: [String!] } `; @@ -130,7 +130,7 @@ describe("array-pop-errors", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) + tags: [String!] @authentication(operations: [UPDATE]) } `; @@ -179,7 +179,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -224,7 +224,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -277,7 +277,7 @@ describe("array-pop-errors", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts index 84f8290801..cf938192f4 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts @@ -96,7 +96,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -196,7 +196,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -296,7 +296,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -376,7 +376,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [Point] + tags: [Point!] } `; @@ -487,7 +487,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [CartesianPoint] + tags: [CartesianPoint!] } `; @@ -563,8 +563,8 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -611,7 +611,7 @@ describe("array-pop", () => { const actor = testHelper.createUniqueType("Actor"); const typeDefs = ` type ${movie.name} @node { - viewers: [Int]! + viewers: [Int!]! workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) } type ${actor.name} @node { @@ -690,7 +690,7 @@ describe("array-pop", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -773,7 +773,7 @@ describe("array-pop", () => { } type ActedIn @relationshipProperties { - locations: [Point] + locations: [Point!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts index f0fe245177..030fab8834 100644 --- a/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts @@ -38,7 +38,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -81,7 +81,7 @@ describe("array-push", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) + tags: [String!] @authentication(operations: [UPDATE]) } `; @@ -132,7 +132,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -176,7 +176,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -229,7 +229,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -240,7 +240,7 @@ describe("array-push", () => { }); const query = /* GraphQL */ ` - mutation Mutation($id: ID, $payIncrement: [Float]) { + mutation Mutation($id: ID, $payIncrement: [Float!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { diff --git a/packages/graphql/tests/integration/array-methods/array-push.int.test.ts b/packages/graphql/tests/integration/array-methods/array-push.int.test.ts index a4eecfdb5c..3763e6e966 100644 --- a/packages/graphql/tests/integration/array-methods/array-push.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-push.int.test.ts @@ -223,7 +223,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -297,7 +297,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -373,7 +373,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -425,8 +425,8 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -473,7 +473,7 @@ describe("array-push", () => { const actor = testHelper.createUniqueType("Actor"); const typeDefs = ` type ${movie.name} @node { - viewers: [Int]! + viewers: [Int!]! workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) } type ${actor.name} @node { @@ -490,7 +490,7 @@ describe("array-push", () => { }); const update = ` - mutation($id: ID, $value: [Int]) { + mutation($id: ID, $value: [Int!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { worksInMovies: [ @@ -553,7 +553,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -564,7 +564,7 @@ describe("array-push", () => { }); const query = ` - mutation Mutation($id: ID, $payIncrement: [Float]) { + mutation Mutation($id: ID, $payIncrement: [Float!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { @@ -636,7 +636,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - locations: [Point] + locations: [Point!] } `; @@ -647,7 +647,7 @@ describe("array-push", () => { }); const query = ` - mutation Mutation($id: ID, $location: [PointInput]) { + mutation Mutation($id: ID, $location: [PointInput!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { @@ -715,7 +715,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [LocalTime] + tags: [LocalTime!] } `; diff --git a/packages/graphql/tests/integration/connections/alias.int.test.ts b/packages/graphql/tests/integration/connections/alias.int.test.ts index edac86d11c..a0fef078d6 100644 --- a/packages/graphql/tests/integration/connections/alias.int.test.ts +++ b/packages/graphql/tests/integration/connections/alias.int.test.ts @@ -608,7 +608,7 @@ describe("Connections Alias", () => { } type ActedIn @relationshipProperties { - roles: [String]! + roles: [String!]! } `; @@ -663,7 +663,7 @@ describe("Connections Alias", () => { } type ActedIn @relationshipProperties { - roles: [String]! + roles: [String!]! } `; diff --git a/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts b/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts index 4c335a4a48..f2039e51a5 100644 --- a/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts @@ -29,14 +29,12 @@ describe("auth/roles", () => { let typeUser: UniqueType; let typeProduct: UniqueType; let typePost: UniqueType; - let typeComment: UniqueType; let typeHistory: UniqueType; beforeEach(async () => { typeUser = testHelper.createUniqueType("User"); typeProduct = testHelper.createUniqueType("Product"); typePost = testHelper.createUniqueType("Post"); - typeComment = testHelper.createUniqueType("Comment"); typeHistory = testHelper.createUniqueType("History"); await testHelper.executeCypher( diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts index fd216d9ac4..8061816261 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts @@ -236,12 +236,12 @@ describe("cypher directive filtering - Aggregation", () => { }); }); - test("String aggregation - min, filter on [Int]", async () => { + test("String aggregation - min, filter on [Int!]", async () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String released: Int - custom_field: [Int] + custom_field: [Int!] @cypher( statement: """ MATCH (this) @@ -287,12 +287,12 @@ describe("cypher directive filtering - Aggregation", () => { }); }); - test("String aggregation - min, filter on [String]", async () => { + test("String aggregation - min, filter on [String!]", async () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String released: Int - custom_field: [String] + custom_field: [String!] @cypher( statement: """ MATCH (this) diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts index a51306912b..654de5ecf4 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts @@ -34,7 +34,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node @authorization(filter: [{ where: { node: { custom_field_INCLUDES: "$jwt.custom_value" } } }]) { title: String - custom_field: [String] + custom_field: [String!] @cypher( statement: """ MATCH (this) diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts index 1f717dc346..6cc5c77d6d 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts @@ -153,7 +153,7 @@ describe("cypher directive filtering - List", () => { const typeDefs = /* GraphQL */ ` type ${CustomType} @node { title: String - custom_cypher_list: [Point] @cypher(statement: + custom_cypher_list: [Point!] @cypher(statement: """ MATCH (this) RETURN this.custom_data as list diff --git a/packages/graphql/tests/integration/issues/1320.int.test.ts b/packages/graphql/tests/integration/issues/1320.int.test.ts index 0b8a6f03e2..c107416d60 100644 --- a/packages/graphql/tests/integration/issues/1320.int.test.ts +++ b/packages/graphql/tests/integration/issues/1320.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { type ${riskType.name} @node { code: String! ownedBy: [${teamType.name}!]! @relationship(type: "OWNS_RISK", direction: IN) - mitigationState: [${mitigationStateType.name}] + mitigationState: [${mitigationStateType.name}!] } type ${teamType.name} @node { diff --git a/packages/graphql/tests/integration/types/date.int.test.ts b/packages/graphql/tests/integration/types/date.int.test.ts index 48f357ae64..dfa44ca55a 100644 --- a/packages/graphql/tests/integration/types/date.int.test.ts +++ b/packages/graphql/tests/integration/types/date.int.test.ts @@ -82,7 +82,7 @@ describe("Date", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID - dates: [Date] + dates: [Date!] } `; @@ -98,8 +98,8 @@ describe("Date", () => { const create = /* GraphQL */ ` mutation { ${Movie.operations.create}(input: [{ id: "${id}", dates: ["${ - date.toISOString().split("T")[0] - }", "${date.toISOString()}", "${date.toISOString()}"] }]) { + date.toISOString().split("T")[0] + }", "${date.toISOString()}", "${date.toISOString()}"] }]) { ${Movie.plural} { dates } diff --git a/packages/graphql/tests/integration/types/datetime.int.test.ts b/packages/graphql/tests/integration/types/datetime.int.test.ts index fb9ef99f0d..9d043fa5e8 100644 --- a/packages/graphql/tests/integration/types/datetime.int.test.ts +++ b/packages/graphql/tests/integration/types/datetime.int.test.ts @@ -83,7 +83,7 @@ describe("DateTime", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID - datetimes: [DateTime] + datetimes: [DateTime!] } `; diff --git a/packages/graphql/tests/schema/array-methods.test.ts b/packages/graphql/tests/schema/array-methods.test.ts index 985e3ce90e..4d506ae71a 100644 --- a/packages/graphql/tests/schema/array-methods.test.ts +++ b/packages/graphql/tests/schema/array-methods.test.ts @@ -38,7 +38,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; const neoSchema = new Neo4jGraphQL({ typeDefs }); @@ -56,11 +56,11 @@ describe("Arrays Methods", () => { * Movie.actors \\"\\"\\" type ActedIn { - pay: [Float] + pay: [Float!] } input ActedInCreateInput { - pay: [Float] + pay: [Float!] } input ActedInSort { @@ -69,15 +69,15 @@ describe("Arrays Methods", () => { input ActedInUpdateInput { pay_POP: Int - pay_PUSH: [Float] - pay_SET: [Float] + pay_PUSH: [Float!] + pay_SET: [Float!] } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - pay_EQ: [Float] + pay_EQ: [Float!] pay_INCLUDES: Float } diff --git a/packages/graphql/tests/schema/interfaces.test.ts b/packages/graphql/tests/schema/interfaces.test.ts index ce89edf5ea..2f6bb511d7 100644 --- a/packages/graphql/tests/schema/interfaces.test.ts +++ b/packages/graphql/tests/schema/interfaces.test.ts @@ -33,7 +33,7 @@ describe("Interfaces", () => { type Movie implements MovieNode @node { id: ID - nodes: [MovieNode] + nodes: [MovieNode!] movies: [Movie!]! @relationship(type: "HAS_MOVIE", direction: OUT) customQuery: [Movie] @cypher( @@ -86,7 +86,7 @@ describe("Interfaces", () => { movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieMovieMoviesAggregationSelection moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! - nodes: [MovieNode] + nodes: [MovieNode!] } type MovieAggregateSelection { @@ -428,7 +428,7 @@ describe("Interfaces", () => { type Movie implements MovieNode @node { id: ID - nodes: [MovieNode] + nodes: [MovieNode!] movies: [Movie!]! @relationship(type: "HAS_MOVIE", direction: OUT) customQuery: [Movie] @cypher( @@ -483,7 +483,7 @@ describe("Interfaces", () => { movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieMovieMoviesAggregationSelection moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! - nodes: [MovieNode] + nodes: [MovieNode!] } type MovieAggregateSelection { diff --git a/packages/graphql/tests/schema/issues/3905.test.ts b/packages/graphql/tests/schema/issues/3905.test.ts index e118f7151f..39869d4606 100644 --- a/packages/graphql/tests/schema/issues/3905.test.ts +++ b/packages/graphql/tests/schema/issues/3905.test.ts @@ -21,8 +21,9 @@ import { printSchemaWithDirectives } from "@graphql-tools/utils"; import { gql } from "graphql-tag"; import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; - -describe("https://github.com/neo4j/graphql/issues/3905", () => { +// This has to be reintroduced when user defined types are supported as target for cypher fields +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("https://github.com/neo4j/graphql/issues/3905", () => { test("custom Cypher result type with list of lists generates without error", async () => { const typeDefs = gql` type Query { @@ -36,11 +37,11 @@ describe("https://github.com/neo4j/graphql/issues/3905", () => { ) } - type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) @node { + type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { paths: [[pathLink]] } - type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) @node { + type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { entity_id: Int other_entity_id: Int } diff --git a/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts b/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts index e835c1aaec..d18850a2a2 100644 --- a/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts +++ b/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts @@ -38,7 +38,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; const neoSchema = new Neo4jGraphQL({ @@ -58,11 +58,11 @@ describe("Arrays Methods", () => { * Movie.actors \\"\\"\\" type ActedIn { - pay: [Float] + pay: [Float!] } input ActedInCreateInput { - pay: [Float] + pay: [Float!] } input ActedInSort { @@ -71,15 +71,15 @@ describe("Arrays Methods", () => { input ActedInUpdateInput { pay_POP: Int - pay_PUSH: [Float] - pay_SET: [Float] + pay_PUSH: [Float!] + pay_SET: [Float!] } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - pay_EQ: [Float] + pay_EQ: [Float!] pay_INCLUDES: Float } diff --git a/packages/graphql/tests/tck/array-methods.test.ts b/packages/graphql/tests/tck/array-methods.test.ts index 36b7dc4e28..e6a85fc886 100644 --- a/packages/graphql/tests/tck/array-methods.test.ts +++ b/packages/graphql/tests/tck/array-methods.test.ts @@ -451,7 +451,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -553,7 +553,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; diff --git a/packages/graphql/tests/tck/issues/1320.test.ts b/packages/graphql/tests/tck/issues/1320.test.ts index ea0fa0da94..ba4a69315d 100644 --- a/packages/graphql/tests/tck/issues/1320.test.ts +++ b/packages/graphql/tests/tck/issues/1320.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { type Risk @node { code: String! ownedBy: [Team!]! @relationship(type: "OWNS_RISK", direction: IN) - mitigationState: [MitigationState] + mitigationState: [MitigationState!] } type Team @node { diff --git a/packages/graphql/tests/tck/issues/3905.test.ts b/packages/graphql/tests/tck/issues/3905.test.ts index 9f6f52fb8c..28b410c837 100644 --- a/packages/graphql/tests/tck/issues/3905.test.ts +++ b/packages/graphql/tests/tck/issues/3905.test.ts @@ -20,7 +20,9 @@ import { Neo4jGraphQL } from "../../../src"; import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; -describe("https://github.com/neo4j/graphql/issues/3905", () => { +// This has to be reintroduced when user defined types are supported as target for cypher fields +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("https://github.com/neo4j/graphql/issues/3905", () => { let typeDefs: string; let neoSchema: Neo4jGraphQL; @@ -37,19 +39,11 @@ describe("https://github.com/neo4j/graphql/issues/3905", () => { ) } - type pathList - @query(read: false, aggregate: false) - @mutation(operations: []) - @subscription(events: []) - @node { - paths: [[pathLink]] + type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { + paths: [[pathLink!]] } - type pathLink - @query(read: false, aggregate: false) - @mutation(operations: []) - @subscription(events: []) - @node { + type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { entity_id: Int other_entity_id: Int } diff --git a/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts b/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts index 4ef7d2fae7..e1d7163d6f 100644 --- a/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts +++ b/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts @@ -94,9 +94,9 @@ describe("Missing custom Cypher on unions", () => { type choNode @mutation(operations: []) @node { iri: ID! @id @alias(property: "uri") identifier: String - title: [title] + title: [title!] hasSortKey: String - labels: [String] + labels: [String!] partOf: ID partOfType: String } diff --git a/packages/graphql/tests/tck/types/points.test.ts b/packages/graphql/tests/tck/types/points.test.ts index 26ed953e60..ff4218f22a 100644 --- a/packages/graphql/tests/tck/types/points.test.ts +++ b/packages/graphql/tests/tck/types/points.test.ts @@ -28,7 +28,7 @@ describe("Cypher Points", () => { typeDefs = /* GraphQL */ ` type PointContainer @node { id: String - points: [Point] + points: [Point!] } `; diff --git a/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts b/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts index c7c536370b..10bb3e3add 100644 --- a/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts +++ b/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts @@ -34,19 +34,19 @@ const map = { Point: "Point", // Array types - LongArray: "[BigInt]", - DoubleArray: "[Float]", - FloatArray: "[Float]", - IntegerArray: "[BigInt]", - BooleanArray: "[Boolean]", - StringArray: "[String]", - DateArray: "[Date]", - DateTimeArray: "[DateTime]", - TimeArray: "[Time]", - LocalTimeArray: "[LocalTime]", - LocalDateTimeArray: "[LocalDateTime]", - DurationArray: "[Duration]", - PointArray: "[Point]", + LongArray: "[BigInt!]", + DoubleArray: "[Float!]", + FloatArray: "[Float!]", + IntegerArray: "[BigInt!]", + BooleanArray: "[Boolean!]", + StringArray: "[String!]", + DateArray: "[Date!]", + DateTimeArray: "[DateTime!]", + TimeArray: "[Time!]", + LocalTimeArray: "[LocalTime!]", + LocalDateTimeArray: "[LocalDateTime!]", + DurationArray: "[Duration!]", + PointArray: "[Point!]", }; export default function mapNeo4jToGraphQLType(neo4jType: string[], mandatory?: boolean): string { diff --git a/packages/introspector/tests/integration/graphql/graphs.test.ts b/packages/introspector/tests/integration/graphql/graphs.test.ts index f9f65eb0dd..0812836ed1 100644 --- a/packages/introspector/tests/integration/graphql/graphs.test.ts +++ b/packages/introspector/tests/integration/graphql/graphs.test.ts @@ -213,7 +213,7 @@ describe("GraphQL - Infer Schema on graphs", () => { expect(typeDefs).toMatchInlineSnapshot(` "type ActedInProperties @relationshipProperties { pay: Float - roles: [String]! + roles: [String!]! } type Actor @node { @@ -269,7 +269,7 @@ describe("GraphQL - Infer Schema on graphs", () => { const typeDefs = await toGraphQLTypeDefs(sessionFactory(bm)); expect(typeDefs).toMatchInlineSnapshot(` "type ActedInProperties @relationshipProperties { - roles: [String]! + roles: [String!]! } type Actor_Label @node(labels: [\\"Actor-Label\\"]) { diff --git a/packages/introspector/tests/integration/graphql/nodes.test.ts b/packages/introspector/tests/integration/graphql/nodes.test.ts index 84dfd8befc..65c720afb2 100644 --- a/packages/introspector/tests/integration/graphql/nodes.test.ts +++ b/packages/introspector/tests/integration/graphql/nodes.test.ts @@ -127,7 +127,7 @@ describe("GraphQL - Infer Schema nodes basic tests", () => { "type TestLabel @node { intProp: BigInt! numberProp: Float! - strArrProp: [String]! + strArrProp: [String!]! strProp: String! }" `);