diff --git a/.changeset/cyan-grapes-laugh.md b/.changeset/cyan-grapes-laugh.md new file mode 100644 index 0000000000..41fd62574b --- /dev/null +++ b/.changeset/cyan-grapes-laugh.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove support for `connectOrCreate` operations diff --git a/.changeset/short-pillows-itch.md b/.changeset/short-pillows-itch.md new file mode 100644 index 0000000000..d33cdecc13 --- /dev/null +++ b/.changeset/short-pillows-itch.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove support for `@unique` directive diff --git a/packages/graphql/src/classes/Node.ts b/packages/graphql/src/classes/Node.ts index 899a171c16..810b42369f 100644 --- a/packages/graphql/src/classes/Node.ts +++ b/packages/graphql/src/classes/Node.ts @@ -74,8 +74,6 @@ type MutableField = PrimitiveField | CustomScalarField | CustomEnumField | Union type AuthableField = PrimitiveField | CustomScalarField | CustomEnumField | UnionField | TemporalField | CypherField; -type ConstrainableField = PrimitiveField | CustomScalarField | CustomEnumField | TemporalField; - export type RootTypeFieldNames = { create: string; read: string; @@ -178,20 +176,6 @@ class Node extends GraphElement { ]; } - public get constrainableFields(): ConstrainableField[] { - return [ - ...this.primitiveFields, - ...this.scalarFields, - ...this.enumFields, - ...this.temporalFields, - ...this.pointFields, - ]; - } - - public get uniqueFields(): ConstrainableField[] { - return this.constrainableFields.filter((field) => field.unique); - } - private get pascalCaseSingular(): string { return upperFirst(this.singular); } diff --git a/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts b/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts index a5c3bf2d6e..ca1a386f19 100644 --- a/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts +++ b/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts @@ -22,7 +22,6 @@ import type { Driver, Session } from "neo4j-driver"; import { DEBUG_EXECUTE } from "../../constants"; import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel"; import type { ConcreteEntity } from "../../schema-model/entity/ConcreteEntity"; -import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { Neo4jGraphQLSessionConfig } from "../Executor"; const debug = Debug(DEBUG_EXECUTE); @@ -229,36 +228,5 @@ async function getMissingConstraints({ const missingConstraints: MissingConstraint[] = []; - for (const entity of schemaModel.concreteEntities) { - const entityAdapter = new ConcreteEntityAdapter(entity); - for (const uniqueField of entityAdapter.uniqueFields) { - if (!uniqueField.annotations.unique) { - continue; - } - - let anyLabelHasConstraint = false; - for (const label of entity.labels) { - // If any of the constraints for the label already exist, skip to the next unique field - if (existingConstraints[label]?.includes(uniqueField.databaseName)) { - anyLabelHasConstraint = true; - break; - } - } - - if (anyLabelHasConstraint === false) { - // TODO: The fallback value of `${entity.name}_${uniqueField.databaseName}` should be changed to use the main label of the entity - // But this can only be done once the translation layer has been updated to use the schema model instead of the Node class - const constraintName = - uniqueField.annotations.unique.constraintName || `${entity.name}_${uniqueField.databaseName}`; - - missingConstraints.push({ - constraintName, - label: entityAdapter.getMainLabel(), - property: uniqueField.databaseName, - }); - } - } - } - return missingConstraints; } diff --git a/packages/graphql/src/graphql/directives/index.ts b/packages/graphql/src/graphql/directives/index.ts index 594ca4ac19..54c1559d9c 100644 --- a/packages/graphql/src/graphql/directives/index.ts +++ b/packages/graphql/src/graphql/directives/index.ts @@ -42,5 +42,4 @@ export { selectableDirective } from "./selectable"; export { settableDirective } from "./settable"; export { subscriptionDirective } from "./subscription"; export { timestampDirective } from "./timestamp"; -export { uniqueDirective } from "./unique"; export { vectorDirective } from "./vector"; diff --git a/packages/graphql/src/graphql/directives/unique.ts b/packages/graphql/src/graphql/directives/unique.ts deleted file mode 100644 index 0a0e84b9c1..0000000000 --- a/packages/graphql/src/graphql/directives/unique.ts +++ /dev/null @@ -1,34 +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 { DirectiveLocation, GraphQLDirective, GraphQLString } from "graphql"; - -export const uniqueDirective = new GraphQLDirective({ - name: "unique", - description: - "Informs @neo4j/graphql that there should be a uniqueness constraint in the database for the decorated field.", - locations: [DirectiveLocation.FIELD_DEFINITION], - args: { - constraintName: { - description: - "The name which should be used for this constraint. By default; type name, followed by an underscore, followed by the field name.", - type: GraphQLString, - }, - }, -}); diff --git a/packages/graphql/src/schema-model/annotation/Annotation.ts b/packages/graphql/src/schema-model/annotation/Annotation.ts index c495d6b59e..fba2968888 100644 --- a/packages/graphql/src/schema-model/annotation/Annotation.ts +++ b/packages/graphql/src/schema-model/annotation/Annotation.ts @@ -38,7 +38,6 @@ import { parseSettableAnnotation } from "../parser/annotations-parser/settable-a import { parseSubscriptionAnnotation } from "../parser/annotations-parser/subscription-annotation"; import { parseSubscriptionsAuthorizationAnnotation } from "../parser/annotations-parser/subscriptions-authorization-annotation"; import { parseTimestampAnnotation } from "../parser/annotations-parser/timestamp-annotation"; -import { parseUniqueAnnotation } from "../parser/annotations-parser/unique-annotation"; import { parseVectorAnnotation } from "../parser/annotations-parser/vector-annotation"; import type { AuthenticationAnnotation } from "./AuthenticationAnnotation"; import type { AuthorizationAnnotation } from "./AuthorizationAnnotation"; @@ -64,7 +63,6 @@ import type { SettableAnnotation } from "./SettableAnnotation"; import type { SubscriptionAnnotation } from "./SubscriptionAnnotation"; import type { SubscriptionsAuthorizationAnnotation } from "./SubscriptionsAuthorizationAnnotation"; import type { TimestampAnnotation } from "./TimestampAnnotation"; -import type { UniqueAnnotation } from "./UniqueAnnotation"; import type { VectorAnnotation } from "./VectorAnnotation"; export interface Annotation { @@ -102,7 +100,6 @@ export type Annotations = CheckAnnotationName<{ subscription: SubscriptionAnnotation; subscriptionsAuthorization: SubscriptionsAuthorizationAnnotation; timestamp: TimestampAnnotation; - unique: UniqueAnnotation; }>; export type AnnotationParser = ( @@ -134,7 +131,6 @@ export const annotationsParsers: { [key in keyof Annotations]: AnnotationParser< subscription: parseSubscriptionAnnotation, subscriptionsAuthorization: parseSubscriptionsAuthorizationAnnotation, timestamp: parseTimestampAnnotation, - unique: parseUniqueAnnotation, relayId: () => new RelayIDAnnotation(), vector: parseVectorAnnotation, }; diff --git a/packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts b/packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts deleted file mode 100644 index 367f69ff21..0000000000 --- a/packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts +++ /dev/null @@ -1,29 +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 type { Annotation } from "./Annotation"; - -export class UniqueAnnotation implements Annotation { - readonly name = "unique"; - public readonly constraintName?: string; - - constructor({ constraintName }: { constraintName?: string }) { - this.constraintName = constraintName; - } -} diff --git a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts index 4f84f7898f..3f910d2f0b 100644 --- a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts +++ b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts @@ -18,7 +18,6 @@ */ import { CypherAnnotation } from "../../annotation/CypherAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../Attribute"; import { EnumType, @@ -555,18 +554,6 @@ describe("Attribute", () => { }); describe("annotation assertions", () => { - test("isUnique", () => { - const attribute = new AttributeAdapter( - new Attribute({ - name: "test", - annotations: { unique: new UniqueAnnotation({ constraintName: "test" }) }, - type: new ScalarType(GraphQLBuiltInScalarType.ID, true), - args: [], - }) - ); - expect(attribute.isUnique()).toBe(true); - }); - test("isCypher", () => { const attribute = new AttributeAdapter( new Attribute({ diff --git a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts index 7ef2dabff7..8774328a73 100644 --- a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts +++ b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts @@ -94,25 +94,10 @@ export class AttributeAdapter { ); } - isUnique(): boolean { - return !!this.annotations.unique || this.isGlobalIDAttribute() === true; - } - isCypher(): boolean { return !!this.annotations.cypher; } - isConstrainable(): boolean { - return ( - this.typeHelper.isGraphQLBuiltInScalar() || - this.typeHelper.isUserScalar() || - this.typeHelper.isEnum() || - this.typeHelper.isTemporal() || - this.typeHelper.isSpatial() || - this.typeHelper.isBigInt() - ); - } - isObjectField(): boolean { return ( this.typeHelper.isScalar() || diff --git a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts index 2887718393..c9ba2cc9ae 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts @@ -18,7 +18,6 @@ */ import { CypherAnnotation } from "../../annotation/CypherAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../../attribute/Attribute"; import { GraphQLBuiltInScalarType, ScalarType } from "../../attribute/AttributeType"; import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter"; @@ -34,7 +33,7 @@ describe("ConcreteEntityAdapter", () => { beforeAll(() => { const idAttribute = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); @@ -95,11 +94,6 @@ describe("ConcreteEntityAdapter", () => { expect(userAdapter.getMainLabel()).toBe("User"); }); - test("should return the correct unique fields", () => { - expect(userAdapter.uniqueFields).toHaveLength(1); - expect(userAdapter.uniqueFields).toStrictEqual([userId]); - }); - test("should return the correct singular name", () => { expect(userAdapter.singular).toBe("user"); }); diff --git a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts index 5543b19dc9..9c0faeb615 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts @@ -44,8 +44,6 @@ export class ConcreteEntityAdapter { // These keys allow to store the keys of the map in memory and avoid keep iterating over the map. private mutableFieldsKeys: string[] = []; - private uniqueFieldsKeys: string[] = []; - private constrainableFieldsKeys: string[] = []; private _relatedEntities: EntityAdapter[] | undefined; @@ -78,13 +76,6 @@ export class ConcreteEntityAdapter { this.mutableFieldsKeys.push(attribute.name); } - if (attributeAdapter.isConstrainable()) { - this.constrainableFieldsKeys.push(attribute.name); - if (attributeAdapter.isUnique()) { - this.uniqueFieldsKeys.push(attribute.name); - } - } - if (attributeAdapter.isGlobalIDAttribute()) { this._globalIdField = attributeAdapter; } @@ -157,14 +148,6 @@ export class ConcreteEntityAdapter { return this.mutableFieldsKeys.map((key) => getFromMap(this.attributes, key)); } - public get uniqueFields(): AttributeAdapter[] { - return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - - public get constrainableFields(): AttributeAdapter[] { - return this.constrainableFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - public get relatedEntities(): EntityAdapter[] { if (!this._relatedEntities) { this._relatedEntities = [...this.relationships.values()].map((relationship) => relationship.target); diff --git a/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityAdapter.ts b/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityAdapter.ts index b6c5d6e643..87d5aebbdf 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityAdapter.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityAdapter.ts @@ -23,7 +23,6 @@ import type { Attribute } from "../../attribute/Attribute"; import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter"; import { RelationshipDeclarationAdapter } from "../../relationship/model-adapters/RelationshipDeclarationAdapter"; import type { RelationshipDeclaration } from "../../relationship/RelationshipDeclaration"; -import { getFromMap } from "../../utils/get-from-map"; import { plural, singular } from "../../utils/string-manipulation"; import type { ConcreteEntity } from "../ConcreteEntity"; import type { InterfaceEntity } from "../InterfaceEntity"; @@ -36,7 +35,6 @@ export class InterfaceEntityAdapter { public readonly attributes: Map = new Map(); public readonly relationshipDeclarations: Map = new Map(); public readonly annotations: Partial; - private uniqueFieldsKeys: string[] = []; private _singular: string | undefined; private _plural: string | undefined; @@ -112,10 +110,6 @@ export class InterfaceEntityAdapter { * used to generate different types for the Entity that contains these Attributes */ - public get uniqueFields(): AttributeAdapter[] { - return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - public get sortableFields(): AttributeAdapter[] { return Array.from(this.attributes.values()).filter((attribute) => attribute.isSortableField()); } @@ -167,9 +161,6 @@ export class InterfaceEntityAdapter { for (const [attributeName, attribute] of attributes.entries()) { const attributeAdapter = new AttributeAdapter(attribute); this.attributes.set(attributeName, attributeAdapter); - if (attributeAdapter.isConstrainable() && attributeAdapter.isUnique()) { - this.uniqueFieldsKeys.push(attribute.name); - } } } diff --git a/packages/graphql/src/schema-model/library-directives.ts b/packages/graphql/src/schema-model/library-directives.ts index 1e56e864b0..d06ff58ca0 100644 --- a/packages/graphql/src/schema-model/library-directives.ts +++ b/packages/graphql/src/schema-model/library-directives.ts @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { Annotations } from "./annotation/Annotation"; -import { annotationsParsers } from "./annotation/Annotation"; import type { DEPRECATED } from "../constants"; import { SHAREABLE } from "../constants"; import type { ValueOf } from "../utils/value-of"; +import type { Annotations } from "./annotation/Annotation"; +import { annotationsParsers } from "./annotation/Annotation"; const additionalDirectives = [ "alias", @@ -60,7 +60,6 @@ export const FIELD_DIRECTIVES = [ "relayId", "subscriptionsAuthorization", "timestamp", - "unique", "declareRelationship", ...SCHEMA_CONFIGURATION_FIELD_DIRECTIVES, ] as const satisfies readonly LibraryDirectives[]; diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts b/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts deleted file mode 100644 index f744105963..0000000000 --- a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts +++ /dev/null @@ -1,32 +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 { makeDirectiveNode } from "@graphql-tools/utils"; -import type { DirectiveNode } from "graphql"; -import { parseUniqueAnnotation } from "./unique-annotation"; -import { uniqueDirective } from "../../../graphql/directives"; - -describe("parseUniqueAnnotation", () => { - test("should correctly parse unique constraint name", () => { - const directive: DirectiveNode = makeDirectiveNode("unique", { constraintName: "uniqueConstraintName" }, uniqueDirective); - const uniqueAnnotation = parseUniqueAnnotation(directive); - - expect(uniqueAnnotation.constraintName).toBe("uniqueConstraintName"); - }); -}); diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.ts b/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.ts deleted file mode 100644 index a180e40050..0000000000 --- a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.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 type { DirectiveNode } from "graphql"; -import { uniqueDirective } from "../../../graphql/directives"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; -import { parseArguments } from "../parse-arguments"; - -export function parseUniqueAnnotation(directive: DirectiveNode): UniqueAnnotation { - const { constraintName } = parseArguments<{ constraintName?: string }>(uniqueDirective, directive); - - return new UniqueAnnotation({ - constraintName, - }); -} diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts index ba352de775..db5f506743 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts @@ -18,7 +18,6 @@ */ import { SelectableAnnotation } from "../../annotation/SelectableAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../../attribute/Attribute"; import { GraphQLBuiltInScalarType, ScalarType } from "../../attribute/AttributeType"; import { ConcreteEntity } from "../../entity/ConcreteEntity"; @@ -33,7 +32,7 @@ describe("RelationshipAdapter", () => { beforeAll(() => { const userId = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); @@ -47,7 +46,7 @@ describe("RelationshipAdapter", () => { const accountId = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts index f9ae3eeca1..c15c028ac8 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts @@ -219,7 +219,7 @@ export class RelationshipAdapter { // The connectOrCreate field is not generated if the related type does not have a unique field (this.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE) && relationshipTarget instanceof ConcreteEntityAdapter && - relationshipTarget.uniqueFields.length > 0) + false) ); } @@ -235,11 +235,10 @@ export class RelationshipAdapter { if (!ifUnionRelationshipTargetEntity) { throw new Error("Expected member entity"); } - const onlyConnectOrCreateAndNoUniqueFields = - onlyConnectOrCreate && !ifUnionRelationshipTargetEntity.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } - const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate && !this.target.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationAdapter.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationAdapter.ts index 279e7c592f..17cc658a10 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationAdapter.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationAdapter.ts @@ -192,7 +192,7 @@ export class RelationshipDeclarationAdapter { // The connectOrCreate field is not generated if the related type does not have a unique field (this.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE) && relationshipTarget instanceof ConcreteEntityAdapter && - relationshipTarget.uniqueFields.length > 0) + false) ); } @@ -208,11 +208,10 @@ export class RelationshipDeclarationAdapter { if (!ifUnionRelationshipTargetEntity) { throw new Error("Expected member entity"); } - const onlyConnectOrCreateAndNoUniqueFields = - onlyConnectOrCreate && !ifUnionRelationshipTargetEntity.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } - const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate && !this.target.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } } diff --git a/packages/graphql/src/schema/generation/connect-or-create-input.ts b/packages/graphql/src/schema/generation/connect-or-create-input.ts index 729a8603ec..f19ab1a03a 100644 --- a/packages/graphql/src/schema/generation/connect-or-create-input.ts +++ b/packages/graphql/src/schema/generation/connect-or-create-input.ts @@ -24,12 +24,10 @@ import type { InputTypeComposerFieldConfigMapDefinition, SchemaComposer, } from "graphql-compose"; -import { RelationshipNestedOperationsOption } from "../../constants"; -import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; +import type { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; -import { createOnCreateITC } from "../create-relationship-fields/create-connect-or-create-field"; // TODO: refactor this export function withConnectOrCreateFieldInputType({ @@ -43,53 +41,55 @@ export function withConnectOrCreateFieldInputType({ userDefinedFieldDirectives: Map; ifUnionMemberEntity?: ConcreteEntityAdapter; }): InputTypeComposer | undefined { - if (!relationshipAdapter.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE)) { - return; - } + return; + // TODO: Fix connectOrCreate without unique + // if (!relationshipAdapter.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE)) { + // return; + // } - let targetEntity: ConcreteEntityAdapter | undefined; - if (relationshipAdapter.target instanceof UnionEntityAdapter) { - if (!ifUnionMemberEntity) { - throw new Error("Expected member entity."); - } - targetEntity = ifUnionMemberEntity; - } else { - if (!(relationshipAdapter.target instanceof ConcreteEntityAdapter)) { - throw new Error("Expected concrete target"); - } - targetEntity = relationshipAdapter.target; - } - if ( - !relationshipAdapter.shouldGenerateFieldInputType(ifUnionMemberEntity) && - !relationshipAdapter.shouldGenerateUpdateFieldInputType(ifUnionMemberEntity) - ) { - return; - } + // let targetEntity: ConcreteEntityAdapter | undefined; + // if (relationshipAdapter.target instanceof UnionEntityAdapter) { + // if (!ifUnionMemberEntity) { + // throw new Error("Expected member entity."); + // } + // targetEntity = ifUnionMemberEntity; + // } else { + // if (!(relationshipAdapter.target instanceof ConcreteEntityAdapter)) { + // throw new Error("Expected concrete target"); + // } + // targetEntity = relationshipAdapter.target; + // } + // if ( + // !relationshipAdapter.shouldGenerateFieldInputType(ifUnionMemberEntity) && + // !relationshipAdapter.shouldGenerateUpdateFieldInputType(ifUnionMemberEntity) + // ) { + // return; + // } - const hasUniqueFields = targetEntity.uniqueFields.length > 0; - if (hasUniqueFields !== true) { - return; - } + // const hasUniqueFields: boolean = false; + // if (hasUniqueFields !== true) { + // return; + // } - createOnCreateITC({ - schemaComposer: composer, - relationshipAdapter, - targetEntityAdapter: targetEntity, - userDefinedFieldDirectives, - }); + // createOnCreateITC({ + // schemaComposer: composer, + // relationshipAdapter, + // targetEntityAdapter: targetEntity, + // userDefinedFieldDirectives, + // }); - // TODO: this should live in the where-fields.ts - composer.getOrCreateITC(targetEntity.operations.connectOrCreateWhereInputTypeName, (tc) => { - tc.addFields((targetEntity as ConcreteEntityAdapter).operations.connectOrCreateWhereInputFieldNames); - }); + // // TODO: this should live in the where-fields.ts + // composer.getOrCreateITC(targetEntity.operations.connectOrCreateWhereInputTypeName, (tc) => { + // tc.addFields((targetEntity as ConcreteEntityAdapter).operations.connectOrCreateWhereInputFieldNames); + // }); - const connectOrCreateName = relationshipAdapter.operations.getConnectOrCreateFieldInputTypeName(targetEntity); - const connectOrCreateFieldInput = composer.getOrCreateITC(connectOrCreateName, (tc) => { - tc.addFields( - relationshipAdapter.operations.getConnectOrCreateInputFields(targetEntity as ConcreteEntityAdapter) || {} - ); - }); - return connectOrCreateFieldInput; + // const connectOrCreateName = relationshipAdapter.operations.getConnectOrCreateFieldInputTypeName(targetEntity); + // const connectOrCreateFieldInput = composer.getOrCreateITC(connectOrCreateName, (tc) => { + // tc.addFields( + // relationshipAdapter.operations.getConnectOrCreateInputFields(targetEntity as ConcreteEntityAdapter) || {} + // ); + // }); + // return connectOrCreateFieldInput; } export function withConnectOrCreateInputType({ diff --git a/packages/graphql/src/schema/generation/where-input.ts b/packages/graphql/src/schema/generation/where-input.ts index 3bb14b31a9..215f8f8ec0 100644 --- a/packages/graphql/src/schema/generation/where-input.ts +++ b/packages/graphql/src/schema/generation/where-input.ts @@ -32,14 +32,12 @@ import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/Uni import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; -import { DEPRECATE_IMPLICIT_EQUAL_FILTERS } from "../constants"; import { getWhereFieldsForAttributes } from "../get-where-fields"; import { withAggregateInputType } from "./aggregate-types"; import { augmentWhereInputTypeWithConnectionFields, augmentWhereInputTypeWithRelationshipFields, } from "./augment-where-input"; -import { shouldAddDeprecatedFields } from "./utils"; function isEmptyObject(obj: Record): boolean { return !Object.keys(obj).length; @@ -55,17 +53,6 @@ export function withUniqueWhereInputType({ features?: Neo4jFeaturesSettings; }): InputTypeComposer { const uniqueWhereFields: InputTypeComposerFieldConfigMapDefinition = {}; - for (const attribute of concreteEntityAdapter.uniqueFields) { - if (shouldAddDeprecatedFields(features, "implicitEqualFilters")) { - uniqueWhereFields[attribute.name] = { - type: attribute.getFieldTypeName(), - directives: [DEPRECATE_IMPLICIT_EQUAL_FILTERS], - }; - } - uniqueWhereFields[`${attribute.name}_EQ`] = { - type: attribute.getFieldTypeName(), - }; - } const uniqueWhereInputType = composer.createInputTC({ name: concreteEntityAdapter.operations.uniqueWhereInputTypeName, diff --git a/packages/graphql/src/schema/get-obj-field-meta.ts b/packages/graphql/src/schema/get-obj-field-meta.ts index 9fb89eb69f..06f3aae8ee 100644 --- a/packages/graphql/src/schema/get-obj-field-meta.ts +++ b/packages/graphql/src/schema/get-obj-field-meta.ts @@ -62,7 +62,6 @@ import { getCypherMeta } from "./get-cypher-meta"; import getFieldTypeMeta from "./get-field-type-meta"; import { getPopulatedByMeta } from "./get-populated-by-meta"; import getRelationshipMeta from "./get-relationship-meta"; -import getUniqueMeta from "./parse/get-unique-meta"; export interface ObjectFields { relationFields: RelationField[]; @@ -141,7 +140,6 @@ function getObjFieldMeta({ const selectableDirective = directives.find((x) => x.name.value === "selectable"); const settableDirective = directives.find((x) => x.name.value === "settable"); const filterableDirective = directives.find((x) => x.name.value === "filterable"); - const unique = getUniqueMeta(directives, obj, field.name.value); const fieldInterface = interfaces.find((x) => x.name.value === typeMeta.name); const fieldUnion = unions.find((x) => x.name.value === typeMeta.name); @@ -175,7 +173,6 @@ function getObjFieldMeta({ "coalesce", "timestamp", "alias", - "unique", "callback", "populatedBy", "jwtClaim", @@ -188,7 +185,6 @@ function getObjFieldMeta({ ), arguments: [...(field.arguments || [])], description: field.description?.value, - ...(unique ? { unique } : {}), }; if (aliasDirective) { diff --git a/packages/graphql/src/schema/parse/get-unique-meta.ts b/packages/graphql/src/schema/parse/get-unique-meta.ts deleted file mode 100644 index b551cd6f3c..0000000000 --- a/packages/graphql/src/schema/parse/get-unique-meta.ts +++ /dev/null @@ -1,48 +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 type { DirectiveNode, InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { Kind } from "graphql"; -import type { Unique } from "../../types"; - -function getUniqueMeta( - directives: DirectiveNode[], - type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, - fieldName: string -): Unique | undefined { - const uniqueDirective = directives.find((x) => x.name.value === "unique"); - - if (uniqueDirective && type.kind === Kind.INTERFACE_TYPE_DEFINITION) { - throw new Error(`@unique directive cannot be used on interface type fields: ${type.name.value}.${fieldName}`); - } - - if (uniqueDirective) { - const constraintName = uniqueDirective.arguments?.find((a) => a.name.value === "constraintName"); - if (constraintName && constraintName.value.kind === Kind.STRING) { - return { constraintName: constraintName.value.value }; - } - return { constraintName: `${type.name.value}_${fieldName}` }; - } - - if (directives.some((directive) => directive.name.value === "relayId")) { - return { constraintName: `${type.name.value}_${fieldName}` }; - } -} - -export default getUniqueMeta; diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts deleted file mode 100644 index 39e0c14315..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts +++ /dev/null @@ -1,51 +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 type { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; -import { uniqueDirective } from "../../../../graphql/directives"; - -export function WarnUniqueDeprecation() { - return function (): ASTVisitor { - let warningAlreadyIssued = false; - - return { - ObjectTypeDefinition(objectTypeDefinition: ObjectTypeDefinitionNode) { - if (["Query", "Mutation", "Subscription"].includes(objectTypeDefinition.name.value)) { - return; - } - if (warningAlreadyIssued) { - return; - } - let hasUniqueDirective = false; - for (const field of objectTypeDefinition.fields || []) { - for (const directive of field.directives ?? []) { - if (directive.name.value === uniqueDirective.name) { - hasUniqueDirective = true; - } - } - } - - if (hasUniqueDirective) { - console.warn(`Future library versions will not support @unique directive.`); - warningAlreadyIssued = true; - } - }, - }; - }; -} diff --git a/packages/graphql/src/schema/validation/schema-validation.test.ts b/packages/graphql/src/schema/validation/schema-validation.test.ts index 7461225ccc..d56a1661c2 100644 --- a/packages/graphql/src/schema/validation/schema-validation.test.ts +++ b/packages/graphql/src/schema/validation/schema-validation.test.ts @@ -89,7 +89,9 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authorization(filter: [{ where: { jwt: { thisClaimDoesNotExist: "something" } } }]) { + type User + @node + @authorization(filter: [{ where: { jwt: { thisClaimDoesNotExist: "something" } } }]) { id: ID! name: String! } @@ -727,7 +729,9 @@ describe("schema validation", () => { test("should validate directive argument name", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User + @node + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1446,7 +1450,9 @@ describe("schema validation", () => { name: String! } - type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { content: "$jwt.sub" } } }]) { + type Post + @node + @subscriptionsAuthorization(filter: [{ where: { node: { content: "$jwt.sub" } } }]) { content: String! author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) } @@ -2391,7 +2397,11 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User + @node + @shareable + @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + @node { id: ID! name: String! } @@ -2631,7 +2641,10 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @plural(value: "Users") @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User + @node + @plural(value: "Users") + @authentication(operations: [CREATE], jwt: { sub: "test" }) { id: ID! name: String! } @@ -2744,29 +2757,6 @@ describe("schema validation", () => { expect(errors[0]).toHaveProperty("path", ["User", "@authentication", "operations", 0]); }); - test("validation should works when used with other directives", () => { - const userDocument = gql` - type User @node { - id: ID! @authentication(operations: [CREATE]) @unique - name: String! - posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) - } - - type Post @node { - id: ID! - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - - const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); - expect(executeValidate).not.toThrow(); - }); - test("should validate directive argument name, when used with other directives", () => { const userDocument = gql` type User @node { @@ -3495,7 +3485,11 @@ describe("schema validation", () => { extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) ${jwtType} - type User @node @plural(value: "Users") @shareable @authentication(ops: [CREATE], jwt: { sub: "test" }) { + type User + @node + @plural(value: "Users") + @shareable + @authentication(ops: [CREATE], jwt: { sub: "test" }) { id: ID! name: String! } @@ -3867,7 +3861,8 @@ describe("schema validation", () => { name: String! } - type Post @node + type Post + @node @authorization( filter: [{ where: { node: { author_NOT_A_QUANTIFIER: { name: "Simone" } } } }] ) { diff --git a/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts b/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts index a8701517b0..9c37219ea6 100644 --- a/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts +++ b/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts @@ -40,12 +40,11 @@ export const invalidFieldCombinations: InvalidFieldCombinations = { "subscriptionsAuthorization", "id", "relationship", - "unique", "filterable", "settable", "selectable", ], - cypher: ["jwtClaim", "alias", "id", "relationship", "unique"], + cypher: ["jwtClaim", "alias", "id", "relationship"], default: ["jwtClaim", "populatedBy", "relationship"], id: ["jwtClaim", "cypher", "populatedBy", "customResolver", "relationship", "timestamp"], populatedBy: ["jwtClaim", "id", "default", "relationship"], @@ -61,10 +60,8 @@ export const invalidFieldCombinations: InvalidFieldCombinations = { "id", "customResolver", "populatedBy", - "unique", ], - timestamp: ["jwtClaim", "id", "unique"], - unique: ["jwtClaim", "cypher", "customResolver", "relationship", "timestamp"], + timestamp: ["jwtClaim", "id"], jwtClaim: FIELD_DIRECTIVES, relayId: ["jwtClaim"], subscriptionsAuthorization: ["jwtClaim", "customResolver", "relationship"], diff --git a/packages/graphql/src/schema/validation/validate-document.test.ts b/packages/graphql/src/schema/validation/validate-document.test.ts index f1e9a10a6c..fda0a4ce2e 100644 --- a/packages/graphql/src/schema/validation/validate-document.test.ts +++ b/packages/graphql/src/schema/validation/validate-document.test.ts @@ -2986,38 +2986,6 @@ describe("validation 2.0", () => { }); }); - describe("@unique", () => { - test("@unique valid", () => { - const doc = gql` - type User @node { - name: String @unique - } - `; - - const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - expect(executeValidate).not.toThrow(); - }); - - test("@unique cannot be used on fields of Interface types", () => { - const doc = gql` - interface IUser { - name: String @unique - } - `; - - 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", - "Invalid directive usage: Directive @unique is not supported on fields of the IUser type." - ); - expect(errors[0]).toHaveProperty("path", ["IUser", "name", "@unique"]); - }); - }); - test("should throw cannot auto-generate a non ID field", () => { const doc = gql` type Movie @node { @@ -3212,36 +3180,6 @@ describe("validation 2.0", () => { }); }); describe("invalid", () => { - test("@unique can't be used with @relationship", () => { - const doc = gql` - type Movie @node { - id: ID - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) @unique - } - - type Actor @node { - name: String - } - `; - - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @relationship cannot be used in combination with @unique" - ); - expect(errors[0]).toHaveProperty("path", ["Movie", "actors"]); - }); - test("@authentication can't be used with @relationship", () => { const doc = gql` type Movie @node { @@ -6026,7 +5964,7 @@ describe("validation 2.0", () => { } type Post @node { - id: ID! @id @unique + id: ID! @id title: String! datetime: DateTime @timestamp(operations: [CREATE]) } @@ -6336,7 +6274,7 @@ describe("validation 2.0", () => { test("should not throw error on validation of schema", () => { const doc = gql` type Order @node { - orderID: ID! @id @unique + orderID: ID! @id placedAt: DateTime @timestamp shipTo: Address! @relationship(type: "SHIPS_TO", direction: OUT) customer: Customer! @relationship(type: "PLACED", direction: IN) diff --git a/packages/graphql/src/schema/validation/validate-document.ts b/packages/graphql/src/schema/validation/validate-document.ts index 40280169a1..40cc9fcbaf 100644 --- a/packages/graphql/src/schema/validation/validate-document.ts +++ b/packages/graphql/src/schema/validation/validate-document.ts @@ -62,7 +62,6 @@ import { import { ValidFieldTypes } from "./custom-rules/valid-types/valid-field-types"; import { ValidObjectType } from "./custom-rules/valid-types/valid-object-type"; import { WarnIfAuthorizationFeatureDisabled } from "./custom-rules/warnings/authorization-feature-disabled"; -import { WarnUniqueDeprecation } from "./custom-rules/warnings/deprecated-unique"; 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"; @@ -234,7 +233,6 @@ function runValidationRulesOnFilteredDocument({ WarnIfSubscriptionsAuthorizationMissing(Boolean(features?.subscriptions)), WarnIfTypeIsNotMarkedAsNode(), WarnIfQueryDirectionIsUsedWithDeprecatedValues, - WarnUniqueDeprecation(), ], schema ); diff --git a/packages/graphql/src/translate/create-connect-or-create-and-params.ts b/packages/graphql/src/translate/create-connect-or-create-and-params.ts deleted file mode 100644 index 5f87418abb..0000000000 --- a/packages/graphql/src/translate/create-connect-or-create-and-params.ts +++ /dev/null @@ -1,451 +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 Cypher from "@neo4j/cypher-builder"; -import type { Node, Relationship } from "../classes"; -import { Neo4jGraphQLError } from "../classes"; -import type { CallbackBucket } from "../classes/CallbackBucket"; -import type { PredicateReturn, PrimitiveField, RelationField } from "../types"; -import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { findConflictingProperties } from "../utils/find-conflicting-properties"; -import { getCypherRelationshipDirection } from "../utils/get-relationship-direction"; -import { asArray, omitFields } from "../utils/utils"; -import { checkAuthentication } from "./authorization/check-authentication"; -import { createAuthorizationAfterPredicate } from "./authorization/create-authorization-after-predicate"; -import { createAuthorizationBeforePredicate } from "./authorization/create-authorization-before-predicate"; -import { parseWhereField } from "./queryAST/factory/parsers/parse-where-field"; -import { addCallbackAndSetParamCypher } from "./utils/callback-utils"; - -type CreateOrConnectInput = { - where?: { - node: Record; - }; - onCreate?: { - node?: Record; - edge?: Record; - }; -}; - -export function createConnectOrCreateAndParams({ - input, - varName, - parentVar, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, -}: { - input: CreateOrConnectInput[] | CreateOrConnectInput; - varName: string; - parentVar: string; - relationField: RelationField; - refNode: Node; - node: Node; - context: Neo4jGraphQLTranslationContext; - withVars: string[]; - callbackBucket: CallbackBucket; -}): Cypher.CypherResult { - asArray(input).forEach((connectOrCreateItem) => { - const conflictingProperties = findConflictingProperties({ - node: refNode, - input: connectOrCreateItem.onCreate?.node, - }); - if (conflictingProperties.length > 0) { - throw new Neo4jGraphQLError( - `Conflicting modification of ${conflictingProperties.map((n) => `[[${n}]]`).join(", ")} on type ${ - refNode.name - }` - ); - } - }); - - // todo: add create - checkAuthentication({ context, node, targetOperations: ["CREATE", "CREATE_RELATIONSHIP"] }); - checkAuthentication({ context, node: refNode, targetOperations: ["CREATE", "CREATE_RELATIONSHIP"] }); - - const withVarsVariables = withVars.map((name) => new Cypher.NamedVariable(name)); - - const statements = asArray(input).map((inputItem, index) => { - const subqueryBaseName = `${varName}${index}`; - const result = createConnectOrCreatePartialStatement({ - input: inputItem, - baseName: subqueryBaseName, - parentVar, - relationField, - refNode, - node, - context, - callbackBucket, - withVars, - }); - return result; - }); - - const wrappedQueries = statements.map((statement) => { - const returnStatement = new Cypher.Return([Cypher.count(new Cypher.Raw("*")), "_"]); - - const subqueryClause = new Cypher.With(...withVarsVariables) - .call(Cypher.utils.concat(statement, returnStatement)) - .importWith(...withVarsVariables); - - return subqueryClause; - }); - - const query = Cypher.utils.concat(...wrappedQueries); - return query.build(`${varName}_`); -} - -function createConnectOrCreatePartialStatement({ - input, - baseName, - parentVar, - relationField, - refNode, - node, - context, - callbackBucket, - withVars, -}: { - input: CreateOrConnectInput; - baseName: string; - parentVar: string; - relationField: RelationField; - refNode: Node; - node: Node; - context: Neo4jGraphQLTranslationContext; - callbackBucket: CallbackBucket; - withVars: string[]; -}): Cypher.Clause { - let mergeQuery: Cypher.CompositeClause | undefined; - - // TODO: connectOrCreate currently doesn't honour field-level authorization - this should be fixed - const authorizationBeforePredicateReturn = createAuthorizationBeforeConnectOrCreate({ - context, - sourceNode: node, - sourceName: parentVar, - targetNode: refNode, - targetName: baseName, - }); - - if (authorizationBeforePredicateReturn.predicate) { - if (authorizationBeforePredicateReturn.preComputedSubqueries) { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*"), - authorizationBeforePredicateReturn.preComputedSubqueries - ); - } - - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*").where(authorizationBeforePredicateReturn.predicate) - ); - } - - const mergeCypher = mergeStatement({ - input, - refNode, - parentRefNode: node, - context, - relationField, - parentNode: new Cypher.NamedNode(parentVar), - varName: baseName, - callbackBucket, - withVars, - }); - - mergeQuery = Cypher.utils.concat(mergeQuery, mergeCypher); - - const authorizationAfterPredicateReturn = createAuthorizationAfterConnectOrCreate({ - context, - sourceNode: node, - sourceName: parentVar, - targetNode: refNode, - targetName: baseName, - }); - - if (authorizationAfterPredicateReturn.predicate) { - if ( - authorizationAfterPredicateReturn.preComputedSubqueries && - !authorizationAfterPredicateReturn.preComputedSubqueries.empty - ) { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*"), - authorizationAfterPredicateReturn.preComputedSubqueries, - new Cypher.With("*").where(authorizationAfterPredicateReturn.predicate) - ); - } else { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*").where(authorizationAfterPredicateReturn.predicate) - ); - } - } - - return mergeQuery; -} - -function mergeStatement({ - input, - refNode, - parentRefNode, - context, - relationField, - parentNode, - varName, - callbackBucket, - withVars, -}: { - input: CreateOrConnectInput; - refNode: Node; - parentRefNode: Node; - context: Neo4jGraphQLTranslationContext; - relationField: RelationField; - parentNode: Cypher.Node; - varName: string; - callbackBucket: CallbackBucket; - withVars: string[]; -}): Cypher.Clause { - const whereAttributes = Object.fromEntries( - Object.entries(input.where?.node ?? {}).map((whereAttribute) => { - const { fieldName } = parseWhereField(whereAttribute[0]); - return [fieldName, whereAttribute[1]]; - }) - ); - - const whereNodeParameters = getCypherParameters(whereAttributes, refNode); - const onCreateNodeParameters = getCypherParameters(input.onCreate?.node, refNode); - - const autogeneratedParams = getAutogeneratedParams(refNode); - const node = new Cypher.NamedNode(varName); - const nodePattern = new Cypher.Pattern(node, { - properties: whereNodeParameters, - labels: refNode.getLabels(context), - }); - - const unsetAutogeneratedParams = omitFields(autogeneratedParams, Object.keys(whereAttributes)); - const callbackFields = getCallbackFields(refNode); - - const callbackParams = callbackFields - .map((callbackField): [Cypher.Property, Cypher.Raw] | [] => { - const varNameVariable = new Cypher.NamedVariable(varName); - return addCallbackAndSetParamCypher( - callbackField, - varNameVariable, - parentNode, - callbackBucket, - "CREATE", - node - ); - }) - .filter((tuple) => tuple.length !== 0) as [Cypher.Property, Cypher.Raw][]; - - const rawNodeParams = { - ...unsetAutogeneratedParams, - ...onCreateNodeParameters, - }; - - const onCreateParams = Object.entries(rawNodeParams).map(([key, param]): [Cypher.Property, Cypher.Param] => { - return [node.property(key), param]; - }); - - const merge = new Cypher.Merge(nodePattern).onCreateSet(...onCreateParams, ...callbackParams); - - const relationshipFields = context.relationships.find((x) => x.properties === relationField.properties); - const autogeneratedRelationshipParams = relationshipFields ? getAutogeneratedParams(relationshipFields) : {}; - const rawOnCreateRelationshipParams = Cypher.utils.toCypherParams(input.onCreate?.edge || {}); - - const rawRelationshipParams = { - ...autogeneratedRelationshipParams, - ...rawOnCreateRelationshipParams, - }; - - const relationship = new Cypher.Relationship(); - const direction = getCypherRelationshipDirection(relationField); - const relationshipPattern = new Cypher.Pattern(parentNode) - .related(relationship, { type: relationField.type, direction }) - .to(node); - - const onCreateRelationshipParams = Object.entries(rawRelationshipParams).map( - ([key, param]): [Cypher.Property, Cypher.Param] => { - return [relationship.property(key), param]; - } - ); - const relationshipMerge = new Cypher.Merge(relationshipPattern).onCreate(...onCreateRelationshipParams); - - let withClause: Cypher.Clause | undefined; - - return Cypher.utils.concat(merge, relationshipMerge, withClause); -} - -function createAuthorizationBeforeConnectOrCreate({ - context, - sourceNode, - sourceName, -}: { - context: Neo4jGraphQLTranslationContext; - sourceNode: Node; - sourceName: string; - targetNode: Node; - targetName: string; -}): PredicateReturn { - const predicates: Cypher.Predicate[] = []; - let subqueries: Cypher.CompositeClause | undefined; - - const sourceAuthorizationBefore = createAuthorizationBeforePredicate({ - context, - nodes: [ - { - node: sourceNode, - variable: new Cypher.NamedNode(sourceName), - }, - ], - operations: ["CREATE_RELATIONSHIP"], - }); - - if (sourceAuthorizationBefore) { - const { predicate, preComputedSubqueries } = sourceAuthorizationBefore; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - return { - predicate: Cypher.and(...predicates), - preComputedSubqueries: subqueries, - }; -} - -function createAuthorizationAfterConnectOrCreate({ - context, - sourceNode, - sourceName, - targetNode, - targetName, -}: { - context: Neo4jGraphQLTranslationContext; - sourceNode: Node; - sourceName: string; - targetNode: Node; - targetName: string; -}): PredicateReturn { - const predicates: Cypher.Predicate[] = []; - let subqueries: Cypher.CompositeClause | undefined; - - const sourceAuthorizationAfter = createAuthorizationAfterPredicate({ - context, - nodes: [ - { - node: sourceNode, - variable: new Cypher.NamedNode(sourceName), - }, - ], - operations: ["CREATE_RELATIONSHIP"], - }); - - const targetAuthorizationAfter = createAuthorizationAfterPredicate({ - context, - nodes: [ - { - node: targetNode, - variable: new Cypher.NamedNode(targetName), - }, - ], - operations: ["CREATE_RELATIONSHIP", "CREATE"], - }); - - if (sourceAuthorizationAfter) { - const { predicate, preComputedSubqueries } = sourceAuthorizationAfter; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - if (targetAuthorizationAfter) { - const { predicate, preComputedSubqueries } = targetAuthorizationAfter; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - return { - predicate: Cypher.and(...predicates), - preComputedSubqueries: subqueries, - }; -} - -function getCallbackFields(node: Node | Relationship): PrimitiveField[] { - const callbackFields = [ - ...node.primitiveFields.filter((f) => f.callback), - ...node.temporalFields.filter((f) => f.callback), - ]; - return callbackFields; -} - -// Helper for compatibility reasons -function getAutogeneratedParams(node: Node | Relationship): Record> { - const autogeneratedFields = node.primitiveFields - .filter((f) => f.autogenerate) - .reduce((acc, field) => { - if (field.dbPropertyName) { - acc[field.dbPropertyName] = new Cypher.Raw("randomUUID()"); - } - return acc; - }, {}); - - const autogeneratedTemporalFields = node.temporalFields - .filter((field) => ["DateTime", "Time"].includes(field.typeMeta.name) && field.timestamps?.includes("CREATE")) - .reduce((acc, field) => { - if (field.dbPropertyName) { - acc[field.dbPropertyName] = new Cypher.Raw(`${field.typeMeta.name.toLowerCase()}()`); - } - return acc; - }, {}); - return { ...autogeneratedTemporalFields, ...autogeneratedFields }; -} - -function getCypherParameters(onCreateParams: Record = {}, node?: Node): Record> { - const params = Object.entries(onCreateParams).reduce((acc, [key, value]) => { - const nodeField = node?.constrainableFields.find((f) => f.fieldName === key); - const nodeFieldName = nodeField?.dbPropertyNameUnescaped || nodeField?.fieldName; - const fieldName = nodeFieldName || key; - const valueOrArray = nodeField?.typeMeta.array ? asArray(value) : value; - acc[fieldName] = valueOrArray; - return acc; - }, {}); - return Cypher.utils.toCypherParams(params); -} diff --git a/packages/graphql/src/translate/create-create-and-params.ts b/packages/graphql/src/translate/create-create-and-params.ts index 1590040c70..c3b2d365cc 100644 --- a/packages/graphql/src/translate/create-create-and-params.ts +++ b/packages/graphql/src/translate/create-create-and-params.ts @@ -29,7 +29,6 @@ import { createAuthorizationAfterAndParamsField, } from "./authorization/compatibility/create-authorization-after-and-params"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; import { createRelationshipValidationString } from "./create-relationship-validation-string"; import createSetRelationshipPropertiesAndParams from "./create-set-relationship-properties-and-params"; import { addCallbackAndSetParam } from "./utils/callback-utils"; @@ -219,22 +218,6 @@ function createCreateAndParams({ res.creates.push(connectAndParams[0]); res.params = { ...res.params, ...connectAndParams[1] }; } - - if (v.connectOrCreate) { - const { cypher, params } = createConnectOrCreateAndParams({ - input: v.connectOrCreate, - varName: `${varNameKey}${relationField.union ? "_" : ""}${unionTypeName}_connectOrCreate`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - res.creates.push(cypher); - res.params = { ...res.params, ...params }; - } }); if (relationField.interface && value.connect) { diff --git a/packages/graphql/src/translate/create-update-and-params.ts b/packages/graphql/src/translate/create-update-and-params.ts index 1f4b203b80..7a1df11b30 100644 --- a/packages/graphql/src/translate/create-update-and-params.ts +++ b/packages/graphql/src/translate/create-update-and-params.ts @@ -38,7 +38,6 @@ import { createAuthorizationBeforeAndParamsField, } from "./authorization/compatibility/create-authorization-before-and-params"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; import createCreateAndParams from "./create-create-and-params"; import createDeleteAndParams from "./create-delete-and-params"; import createDisconnectAndParams from "./create-disconnect-and-params"; @@ -370,22 +369,6 @@ export default function createUpdateAndParams({ res.params = { ...res.params, ...connectAndParams[1] }; } - if (update.connectOrCreate) { - const { cypher, params } = createConnectOrCreateAndParams({ - input: update.connectOrCreate, - varName: `${variableName}_connectOrCreate`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - subquery.push(cypher); - res.params = { ...res.params, ...params }; - } - if (update.create) { if (withVars) { subquery.push(`WITH ${withVars.join(", ")}`); diff --git a/packages/graphql/src/translate/translate-update.ts b/packages/graphql/src/translate/translate-update.ts index 5baf462133..ee67ca7af0 100644 --- a/packages/graphql/src/translate/translate-update.ts +++ b/packages/graphql/src/translate/translate-update.ts @@ -26,7 +26,6 @@ import type { GraphQLWhereArg, RelationField } from "../types"; import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; import { compileCypher } from "../utils/compile-cypher"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; import createCreateAndParams from "./create-create-and-params"; import createDeleteAndParams from "./create-delete-and-params"; import createDisconnectAndParams from "./create-disconnect-and-params"; @@ -283,22 +282,6 @@ export default async function translateUpdate({ } else { refNodes.push(context.nodes.find((x) => x.name === relationField.typeMeta.name) as Node); } - - refNodes.forEach((refNode) => { - const { cypher, params } = createConnectOrCreateAndParams({ - input: input[refNode.name] || input, // Deals with different input from update -> connectOrCreate - varName: `${varName}_connectOrCreate_${key}${relationField.union ? `_${refNode.name}` : ""}`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - connectStrs.push(cypher); - cypherParams = { ...cypherParams, ...params }; - }); }); } diff --git a/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts index bad6b626a0..4cff4eb14a 100644 --- a/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts @@ -684,12 +684,12 @@ describe("Subscription authentication", () => { actors: [${typeActor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type ${typeActor} @authentication(operations: [READ]) @node { name: String! - id: Int @unique + id: Int movies: [${typeMovie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) } @@ -708,8 +708,8 @@ describe("Subscription authentication", () => { type ${typePerson} implements Reviewer @node { name: String! reputation: Int! @authentication(operations: [READ]) - id: Int @unique - reviewerId: Int @unique @authentication(operations: [READ]) + id: Int + reviewerId: Int @authentication(operations: [READ]) movies: [${typeMovie}!]! @relationship(type: "REVIEWED", direction: OUT, properties: "Review") } diff --git a/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts index 9e5c465f89..75240c8957 100644 --- a/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts @@ -57,7 +57,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } type ${typeGenre} @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } diff --git a/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts index 8cd1fbee23..b75ffb1f88 100644 --- a/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/5586", () => { typeDefs = /* GraphQL */ ` type ${Entity} { - id: ID! @id @unique + id: ID! @id name: String } `; diff --git a/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts b/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts deleted file mode 100644 index d0f062bec2..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts +++ /dev/null @@ -1,258 +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 { type Integer } from "neo4j-driver"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("connectOrCreate", () => { - describe("Update -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let queryUpdate: string; - let queryCreate: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeGenre = testHelper.createUniqueType("Genre"); - const secret = "secret"; - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeMovie.name} @node { - title: String - genres: [${typeGenre.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${typeGenre.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - name: String @unique - } - `; - - queryUpdate = ` - mutation { - ${typeMovie.operations.update}( - update: { - title: "Forrest Gump 2" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - queryCreate = ` - mutation { - ${typeMovie.operations.create}( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Comedy" } }, - onCreate: { node: { name: "Comedy" } } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("cannot update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "RandomMovie1"})`); - const gqlResult = await testHelper.executeGraphQL(queryUpdate); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); - - test("update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "Forrest Gump"})`); - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryUpdate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Horror" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - - test("create with ConnectOrCreate auth", async () => { - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryCreate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Comedy" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - }); - - describe("authorization rules on source and target types", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let queryUpdate: string; - let queryCreate: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeGenre = testHelper.createUniqueType("Genre"); - const secret = "secret"; - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeMovie.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - title: String - genres: [${typeGenre.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${typeGenre.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - name: String @unique - } - `; - - queryUpdate = ` - mutation { - ${typeMovie.operations.update}( - update: { - title: "Forrest Gump 2" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - queryCreate = ` - mutation { - ${typeMovie.operations.create}( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Comedy" } }, - onCreate: { node: { name: "Comedy" } } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("cannot update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "RandomMovie1"})`); - const gqlResult = await testHelper.executeGraphQL(queryUpdate); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); - - test("update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "Forrest Gump"})`); - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryUpdate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Horror" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - - test("create with ConnectOrCreate auth", async () => { - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryCreate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Comedy" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts b/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts deleted file mode 100644 index 07c36de3ce..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts +++ /dev/null @@ -1,258 +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 { generate } from "randomstring"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("connect-or-create with @id", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! @unique - subtitle: String @unique - id: ID! @id @unique - actors: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type ${typeActor.name} @node { - name: String - movies: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("create -> connectOrCreate with autogenerated ID", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { title: "${title}" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.name).toBe("Tom Hanks"); - - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].title).toBe(title); - - expect(typeof resultActor.movies[0].id).toBe("string"); - }); - - test("create -> connectOrCreate errors if title is not provided onCreate", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toHaveLength(1); - expect((gqlResult?.errors as any[])[0].message).toBe( - `Field "${typeMovie.name}OnCreateInput.title" of required type "String!" was not provided.` - ); - }); - - test("create -> connectOrCreate with specified ID should throw", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "myid" } } - onCreate: { node: { id: "myid", title: "The Terminal" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toHaveLength(1); - expect((gqlResult?.errors as any[])[0].message).toBe( - `Field "id" is not defined by type "${typeMovie.name}OnCreateInput".` - ); - }); - - test("create -> connectOrCreate overrides title on create", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { title: "${title}-2" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].title).toBe(`${title}-2`); - }); - - // NOTE: This test case covers a known bug #1124 in which the where field update the values on connectOrCreate - /* eslint-disable-next-line jest/no-disabled-tests */ - test.skip("create -> connectOrCreate does not change value on where", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { subtitle_EQ: "${title}" } } - onCreate: { node: { title: "${title}"} } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - subtitle - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].subtitle).toBeNull(); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts b/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts deleted file mode 100644 index f639ad12c7..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts +++ /dev/null @@ -1,229 +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 { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Create -> ConnectOrCreate Union", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: { - name: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts b/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts deleted file mode 100644 index b8f045b673..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts +++ /dev/null @@ -1,303 +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 type { Integer, QueryResult } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -async function runAndParseRecords>( - testHelper: TestHelper, - cypher: string, - params?: Record -): Promise { - const result = await testHelper.executeCypher(cypher, params); - return extractFirstRecord(result); -} - -function extractFirstRecord(records: QueryResult>): T { - const record = records.records[0]; - if (!record) throw new Error("Record is undefined, i.e. no columns returned from neo4j-driver in test"); - return record.toObject(); -} - -describe("Create -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - id: Int! @unique - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 22, - name: "Tom Hanks" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", id: 5 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitleAndId = await runAndParseRecords<{ title: string; id: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - ` - ); - - expect(movieTitleAndId.title).toBe("The Terminal"); - expect(movieTitleAndId.id.toNumber()).toBe(5); - - const actedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - }); - - test("ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 234, - name: "${testActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: testActorName, - }, - ]); - - const actorsWithMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (a:${typeActor.name} {name: "${testActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - ` - ); - - expect(actorsWithMovieCount.count.toInt()).toBe(1); - - const moviesWithIdCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - ` - ); - - expect(moviesWithIdCount.count.toInt()).toBe(1); - - const theTerminalMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - ` - ); - - expect(theTerminalMovieCount.count.toInt()).toBe(0); - - const actedInRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${testActorName}"}) - RETURN r.screentime as screentime - ` - ); - - expect(actedInRelation.screentime.toNumber()).toBe(105); - - const newIdMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - ` - ); - expect(newIdMovieCount.count.toInt()).toBe(0); - }); - - test("ConnectOrCreate creates new node with edge data", async () => { - const actorName = "Tommy Hanks The Little"; - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 239, - name: "${actorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 52 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal 2", id: 52 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: actorName, - }, - ]); - - const movieTitleAndId = await runAndParseRecords<{ title: string; id: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 52}) - RETURN m.title as title, m.id as id - ` - ); - - expect(movieTitleAndId.title).toBe("The Terminal 2"); - expect(movieTitleAndId.id.toNumber()).toBe(52); - - const actedInRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} {id: 52})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - ` - ); - - expect(actedInRelation.screentime.toNumber()).toBe(105); - }); - test("ConnectOrCreate creates a new node with the correct relationship direction", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeMovie.operations.create}( - input: [{ - id: 339, - title: "The Matrix", - ${typeActor.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 305 } } - onCreate: { node: { id: 305, name: "Keanu" }, edge: { screentime: 105 } } - } - } - }] - ) { - ${typeMovie.plural} { - id - title - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeMovie.operations.create][`${typeMovie.plural}`]).toEqual([ - { - id: 339, - title: "The Matrix", - }, - ]); - - const actorsRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} { id: 339 })<-[r:ACTED_IN]-(:${typeActor.name} { name: "Keanu" }) - RETURN r.screentime as screentime - ` - ); - - expect(actorsRelation.screentime.toNumber()).toBe(105); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts deleted file mode 100644 index adf50bb92a..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts +++ /dev/null @@ -1,175 +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 type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate Top Level", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update with ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks 2" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 5 } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks 2", - }, - ]); - - const movieTitleAndId: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0].toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0].toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks 2"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - }); - - test("Update with ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - const updatedActorName = "updatedActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${testActorName}"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "${updatedActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - where: { name_EQ: "${testActorName}"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: updatedActorName, - }, - ]); - - const actorsWithMovieCount: any = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name: "${updatedActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0].toObject().count.toInt()).toBe(1); - - const moviesWithIdCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - `); - - expect(moviesWithIdCount.records[0].toObject().count.toInt()).toBe(1); - - const theTerminalMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - `); - - expect(theTerminalMovieCount.records[0].toObject().count.toInt()).toBe(0); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${updatedActorName}"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - - const newIdMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - `); - expect(newIdMovieCount.records[0].toObject().count.toInt()).toBe(0); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts deleted file mode 100644 index c363992d78..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts +++ /dev/null @@ -1,230 +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 { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate union top level", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new nodes", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${actorName}"})`); - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - where: { name_EQ: "${actorName}"}){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts deleted file mode 100644 index 7c79b00814..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts +++ /dev/null @@ -1,234 +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 { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate Union", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${actorName}"})`); - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }, - where: { - name_EQ: "${actorName}" - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts deleted file mode 100644 index d902e6c394..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts +++ /dev/null @@ -1,175 +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 type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update with ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks 2" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 5 } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks 2", - }, - ]); - - const movieTitleAndId: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0].toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0].toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks 2"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - }); - - test("Update with ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - const updatedActorName = "updatedActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${testActorName}"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name: "${updatedActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - where: { name_EQ: "${testActorName}"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][`${typeActor.plural}`]).toEqual([ - { - name: updatedActorName, - }, - ]); - - const actorsWithMovieCount: any = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name: "${updatedActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0].toObject().count.toInt()).toBe(1); - - const moviesWithIdCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - `); - - expect(moviesWithIdCount.records[0].toObject().count.toInt()).toBe(1); - - const theTerminalMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - `); - - expect(theTerminalMovieCount.records[0].toObject().count.toInt()).toBe(0); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${updatedActorName}"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - - const newIdMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - `); - expect(newIdMovieCount.records[0].toObject().count.toInt()).toBe(0); - }); -}); diff --git a/packages/graphql/tests/integration/directives/alias.int.test.ts b/packages/graphql/tests/integration/directives/alias.int.test.ts index f985eed7df..3d40a6f688 100644 --- a/packages/graphql/tests/integration/directives/alias.int.test.ts +++ b/packages/graphql/tests/integration/directives/alias.int.test.ts @@ -47,7 +47,7 @@ describe("@alias directive", () => { } type ${AliasDirectiveTestUser} implements AliasInterface @node { - id: ID! @id @unique @alias(property: "dbId") + id: ID! @id @alias(property: "dbId") name: String! @alias(property: "dbName") likes: [${AliasDirectiveTestMovie}!]! @relationship(direction: OUT, type: "LIKES", properties: "AliasDirectiveTestLikesProps") createdAt: DateTime! @timestamp(operations: [CREATE]) @alias(property: "dbCreatedAt") diff --git a/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts b/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts deleted file mode 100644 index 1d6ed96f0a..0000000000 --- a/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts +++ /dev/null @@ -1,350 +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 type { UniqueType } from "../../../utils/graphql-types"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("@alias directive", () => { - const testHelper = new TestHelper(); - - let typeActor: UniqueType; - let typeMovie: UniqueType; - - beforeEach(async () => { - typeMovie = testHelper.createUniqueType("Movie"); - typeActor = testHelper.createUniqueType("Actor"); - - const typeDefs = ` - type ${typeActor} @node { - name: String! - nameAgain: String @alias(property: "name") - movies: [${typeMovie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${typeMovie} @node { - title: String - titleAgain: String @alias(property: "title") - id: ID! @id @unique - actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update mutation with top-level connectOrCreate alias referring to existing field, include both fields as inputs", async () => { - const query = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", titleAgain: "oops" } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - }); - - test("Create mutation with alias referring to existing field, include only field as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Tom Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "1234" } } - onCreate: { node: { title: "Forrest Gump" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.info.nodesCreated).toBe(2); - }); - test("Create mutation with alias referring to existing field, include only alias as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "2341" } } - onCreate: { node: { titleAgain: "Forrest Run" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.info.nodesCreated).toBe(2); - }); - test("Create mutation with alias referring to existing field, include both fields as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Tom Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "3412" } } - onCreate: { node: { title: "Forrest Gump", titleAgain: "Forrest G" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.actors).toBeUndefined(); - }); - - test("Update mutation with alias referring to existing field, include only one field as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.update}(update: { - name: "a", - movies: [ - { - connectOrCreate: [ - { - onCreate: { - node: { - title: "b" - } - }, - where: { - node: { - id_EQ: "123" - } - } - } - ] - } - ] - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - test("Update mutation with alias referring to existing field, include both fields as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.update}(update: { - name: "a", - movies: [ - { - connectOrCreate: [ - { - onCreate: { - node: { - title: "b", - titleAgain: "bad" - } - }, - where: { - node: { - id_EQ: "123" - } - } - } - ] - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect((gqlResult?.data as any)?.[typeActor.operations.update]?.info).toBeUndefined(); - }); - test("Update mutation nested with create, alias referring to existing field, include both fields as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.update}(update: { - name: "a", - movies: [ - { - update: { - node: { - title: "b", - actors: [ - { - create: [ - { - node: { - name: "c", - movies: { - connectOrCreate: [ - { - onCreate: { - node: { - title: "d", - titleAgain: "bad" - } - }, - where: { - node: { - id_EQ: "1" - } - } - } - ] - } - } - } - ] - } - ] - } - } - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect((gqlResult?.data as any)?.[typeActor.operations.update]?.info).toBeUndefined(); - }); - test("Update mutation nested with create, alias referring to existing field, include only one field as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.update}(update: { - name: "a", - movies: [ - { - update: { - node: { - title: "b", - actors: [ - { - create: [ - { - node: { - name: "c", - movies: { - connectOrCreate: [ - { - onCreate: { - node: { - title: "d", - } - }, - where: { - node: { - id_EQ: "1" - } - } - } - ] - } - } - } - ] - } - ] - } - } - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/directives/alias/unions.int.test.ts b/packages/graphql/tests/integration/directives/alias/unions.int.test.ts deleted file mode 100644 index b02935dc26..0000000000 --- a/packages/graphql/tests/integration/directives/alias/unions.int.test.ts +++ /dev/null @@ -1,609 +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 { gql } from "graphql-tag"; -import type { UniqueType } from "../../../utils/graphql-types"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("@alias directive", () => { - const testHelper = new TestHelper(); - - let typeMovie: UniqueType; - let typeSeries: UniqueType; - let typeActor: UniqueType; - - beforeEach(async () => { - typeMovie = testHelper.createUniqueType("Movie"); - typeSeries = testHelper.createUniqueType("Series"); - typeActor = testHelper.createUniqueType("Actor"); - - const typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - titleAgain: String @alias(property: "title") - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - titleAgain: String @alias(property: "title") - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - nameAgain: String @alias(property: "name") - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Create mutation with alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect((gqlResult?.data as any)?.createDirectors?.directors).toBeUndefined(); - }); - test("Create mutation with alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}` - ); - expect((gqlResult?.data as any)?.createDirectors?.directors).toBeUndefined(); - }); - test("Create mutation alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - update type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = ` - mutation { - ${typeActor.operations.update}( - update: { - nameAgain: "oops" - name: "Tom Hanks", - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[name]], [[nameAgain]] on type ${typeActor.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - - test("Update mutation alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - update type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.update}( - update: { - name: "Tom Hanks", - nameAgain: "oops", - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors?.[0]?.message).toBe( - `Conflicting modification of [[name]], [[nameAgain]] on type ${typeActor.name}` - ); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts b/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts index 78f37da2ee..0ed6e4724a 100644 --- a/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts @@ -1538,452 +1538,6 @@ describe("auth/is-authenticated", () => { }); }); - describe("connectOrCreate", () => { - test("should not throw if authenticated", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP]) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should not throw if authenticated with correct role", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should not throw if authenticated with correct role at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should throw if not authenticated", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP]) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - // missing super-admin - const token = "not valid token"; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should throw if not authenticated at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - // missing super-admin - const token = "not valid token"; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should throw if authenticated with incorrect roles", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["not-an-admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should throw if authenticated with incorrect roles at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["not-an-admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - }); - describe("disconnect", () => { test("should not throw if authenticated", async () => { const Post = testHelper.createUniqueType("Post"); @@ -3624,103 +3178,7 @@ describe("auth/is-authenticated", () => { expect(gqlResult.errors).toBeUndefined(); }); }); - describe("connectOrCreate", () => { - let Post: UniqueType; - - beforeEach(async () => { - Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - extend schema @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - test("should throw if not authenticated type definition", async () => { - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - const token = "not valid token"; - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should not throw if authenticated type definition", async () => { - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - const token = createBearerToken(secret); - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - }); describe("disconnect", () => { let Post: UniqueType; diff --git a/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts index c5b2dc3d5e..337551e0f2 100644 --- a/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, ] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { { where: { node: { from: { id_EQ: "$jwt.sub" } } } } ] ) { - id: ID! @id @unique + id: ID! @id from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) to: ${User}! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) } diff --git a/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts b/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts index 330142981d..14d516f444 100644 --- a/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts @@ -838,7 +838,7 @@ describe("cypher directive", () => { } type ${User} @node { - id: ID @id @unique + id: ID @id updates: [PostMovieUser!]! @cypher( statement: """ diff --git a/packages/graphql/tests/integration/directives/id.int.test.ts b/packages/graphql/tests/integration/directives/id.int.test.ts index 916763a12c..3bdbde279c 100644 --- a/packages/graphql/tests/integration/directives/id.int.test.ts +++ b/packages/graphql/tests/integration/directives/id.int.test.ts @@ -41,7 +41,7 @@ describe("@id directive", () => { test("should create a movie with autogenerate id", async () => { const typeDefs = ` type ${Movie} @node { - id: ID! @id @unique + id: ID! @id name: String } `; @@ -107,12 +107,12 @@ describe("@id directive", () => { test("should create a movie with autogenerate id and a nested genre with autogenerate id", async () => { const typeDefs = ` type ${Genre} @node { - id: ID! @id @unique + id: ID! @id name: String! } type ${Movie} @node { - id: ID! @id @unique + id: ID! @id name: String! genres: [${Genre}!]! @relationship(type: "HAS_GENRE", direction: OUT) } @@ -157,7 +157,7 @@ describe("@id directive", () => { test("should autogenerate an ID for a relationship property", async () => { const typeDefs = ` type ${Actor} @node { - id: ID! @id @unique + id: ID! @id name: String! } @@ -167,7 +167,7 @@ describe("@id directive", () => { } type ${Movie} @node { - id: ID! @id @unique + id: ID! @id title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } diff --git a/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts b/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts index 664592167d..624da3a53e 100644 --- a/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts +++ b/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts @@ -38,10 +38,8 @@ describe("@relationhip - nestedOperations", () => { describe("Related to a concrete type", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -66,25 +64,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; + updateMutationWithNestedCreate = `#graphql mutation { ${Movie.operations.update}(update: { actors: { create: { node: { name: "someName" } } } }) { @@ -107,25 +87,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -184,14 +146,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -204,19 +162,13 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeFalsy(); expect(updateWithNestedConnectResult.errors).toBeDefined(); expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -250,14 +202,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -270,86 +218,13 @@ describe("@relationhip - nestedOperations", () => { 'Field "create" is not defined by type' ); expect(createWithNestedConnectResult.errors).toBeFalsy(); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' ); expect(updateWithNestedConnectResult.errors).toBeFalsy(); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeDefined(); - expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( - 'Field "disconnect" is not defined by type' - ); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the connectOrCreate nested op when CONNECT_OR_CREATE is the only nestedOperation specified", async () => { - const typeDefs = `#graphql - type ${Person} @node { - id: ID! @id @unique - name: String - } - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -383,14 +258,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -406,10 +277,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -418,10 +286,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeFalsy(); expect(updateWithNestedDisconnectResult.errors).toBeDefined(); expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( @@ -452,14 +317,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -475,10 +336,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -487,10 +345,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -521,14 +376,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -544,10 +395,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -556,10 +404,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -579,10 +424,9 @@ describe("@relationhip - nestedOperations", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; + let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -610,27 +454,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; + updateMutationWithNestedCreate = `#graphql mutation { ${Movie.operations.update}(update: { actors: { ${PersonOne}: { create: { node: { name: "someName" } } } } }) { @@ -653,27 +477,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -737,14 +541,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -757,19 +557,13 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeFalsy(); expect(updateWithNestedConnectResult.errors).toBeDefined(); expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -809,14 +603,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -829,92 +619,13 @@ describe("@relationhip - nestedOperations", () => { 'Field "create" is not defined by type' ); expect(createWithNestedConnectResult.errors).toBeFalsy(); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' ); expect(updateWithNestedConnectResult.errors).toBeFalsy(); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeDefined(); - expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( - 'Field "disconnect" is not defined by type' - ); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the connectOrCreate nested op when CONNECT_OR_CREATE is the only nestedOperation specified", async () => { - const typeDefs = `#graphql - type ${PersonOne} @node { - id: ID! @id @unique - name: String - } - - type ${PersonTwo} @node { - nameTwo: String - } - - union ${Person} = ${PersonOne} | ${PersonTwo} - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -954,14 +665,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -977,10 +684,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -989,10 +693,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeFalsy(); expect(updateWithNestedDisconnectResult.errors).toBeDefined(); expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( @@ -1029,14 +730,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -1052,10 +749,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -1064,10 +758,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -1104,14 +795,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -1127,10 +814,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -1139,10 +823,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -1157,7 +838,6 @@ describe("@relationhip - nestedOperations", () => { }); describe("Related to an interface type", () => { - // TODO: add tests/expects for connectOrCreate once implemented for interfaces let PersonOne: UniqueType; let PersonTwo: UniqueType; diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts index 3dfa199f54..17649bbcf1 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts @@ -34,19 +34,19 @@ describe("RelayId projection with different database name", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") title: String! genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") name: String! } `; diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts index ba6134878b..83e539a64f 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts @@ -35,19 +35,19 @@ describe("RelayId projection with GraphQL field alias", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId title: String! genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } `; diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts index cc73e1e00e..30e4ee4eae 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts @@ -34,19 +34,19 @@ describe("RelayId projection", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId title: String! genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } `; diff --git a/packages/graphql/tests/integration/directives/unique.int.test.ts b/packages/graphql/tests/integration/directives/unique.int.test.ts deleted file mode 100644 index 74c04123c1..0000000000 --- a/packages/graphql/tests/integration/directives/unique.int.test.ts +++ /dev/null @@ -1,515 +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 type { Driver } from "neo4j-driver"; -import { generate } from "randomstring"; -import type { Neo4jDatabaseInfo } from "../../../src/classes/Neo4jDatabaseInfo"; -import { isMultiDbUnsupportedError } from "../../utils/is-multi-db-unsupported-error"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("assertIndexesAndConstraints/unique", () => { - const testHelper = new TestHelper(); - let driver: Driver; - let databaseName: string; - let MULTIDB_SUPPORT = true; - let dbInfo: Neo4jDatabaseInfo; - - beforeAll(async () => { - dbInfo = await testHelper.getDatabaseInfo(); - - databaseName = generate({ readable: true, charset: "alphabetic" }); - - try { - await testHelper.createDatabase(databaseName); - } catch (e) { - if (e instanceof Error) { - if (isMultiDbUnsupportedError(e)) { - // No multi-db support, so we skip tests - MULTIDB_SUPPORT = false; - await testHelper.close(); - } else { - throw e; - } - } - } - }); - - beforeEach(async () => { - if (MULTIDB_SUPPORT) { - driver = await testHelper.getDriver(); - } - }); - - afterEach(async () => { - if (MULTIDB_SUPPORT) { - await testHelper.close(); - } - }); - - afterAll(async () => { - if (MULTIDB_SUPPORT) { - await testHelper.dropDatabase(); - await testHelper.close(); - } - }); - - describe("@unique", () => { - test("should throw an error when all necessary constraints do not exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.isbn`); - }); - - test("should throw an error when all necessary constraints do not exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique @alias(property: "internationalStandardBookNumber") - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.internationalStandardBookNumber`); - }); - - test("should not throw an error when all necessary constraints exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_isbn ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.isbn IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw an error when all necessary constraints exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique @alias(property: "internationalStandardBookNumber") - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_isbn ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.internationalStandardBookNumber IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw if constraint exists on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someIntProperty: Int! - title: String! @unique - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_title ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.title IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ - driver, - sessionConfig: { database: databaseName }, - }) - ).resolves.not.toThrow(); - }); - - test("should not allow creating duplicate @unique properties when constraint is on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someStringProperty: String! @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const mutation = ` - mutation { - ${baseType.operations.create}(input: [ - { - someStringProperty: "notUnique", - title: "someTitle" - }, - { - someStringProperty: "notUnique", - title: "someTitle2" - }, - ]) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const query = ` - query { - ${baseType.plural} { - someStringProperty - } - } - `; - - const mutationGqlResult = await testHelper.executeGraphQL(mutation); - - const queryGqlResult = await testHelper.executeGraphQL(query); - - expect((mutationGqlResult?.errors as any[])[0].message).toBe("Constraint validation failed"); - - expect(queryGqlResult.errors).toBeFalsy(); - expect(queryGqlResult.data?.[baseType.plural]).toBeArrayOfSize(0); - }); - - test("should not allow updating to duplicate @unique properties when constraint is on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someStringProperty: String! @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const uniqueVal1 = "someVal1"; - const uniqueVal2 = "someUniqueVal2"; - - const createMutation = ` - mutation { - ${baseType.operations.create}(input: [ - { - someStringProperty: "${uniqueVal1}", - title: "someTitle" - }, - { - someStringProperty: "${uniqueVal2}", - title: "someTitle2" - }, - ]) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const updateMutation = ` - mutation { - ${baseType.operations.update}(update: { - someStringProperty: "notUnique" - }) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const query = ` - query { - ${baseType.plural} { - someStringProperty - } - } - `; - - const createGqlResult = await testHelper.executeGraphQL(createMutation); - const updateGqlResult = await testHelper.executeGraphQL(updateMutation); - const queryGqlResult = await testHelper.executeGraphQL(query); - - expect(createGqlResult?.errors).toBeFalsy(); - expect((updateGqlResult?.errors as any[])[0].message).toBe("Constraint validation failed"); - - expect(queryGqlResult.errors).toBeFalsy(); - expect(queryGqlResult.data?.[baseType.plural]).toIncludeSameMembers([ - { - someStringProperty: uniqueVal1, - }, - { - someStringProperty: uniqueVal2, - }, - ]); - }); - }); - - describe("@id", () => { - test("should throw an error when all necessary constraints do not exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.id`); - }); - - test("should throw an error when all necessary constraints do not exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique @alias(property: "identifier") - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.identifier`); - }); - - test("should not throw an error when all necessary constraints exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_id ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.id IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw an error when all necessary constraints exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique @alias(property: "identifier") - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_id ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.identifier IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw if constraint exists on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) @mutation(operations: []) { - someIdProperty: ID! @id @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ - driver, - sessionConfig: { database: databaseName }, - }) - ).resolves.not.toThrow(); - }); - }); -}); diff --git a/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts b/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts index 7906479bd0..3f720f28ea 100644 --- a/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts +++ b/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts @@ -1420,13 +1420,13 @@ describe("Advanced Filtering", () => { const typeDefs = ` type ${Movie} @node { - id: ID! @id @unique + id: ID! @id budget: Int! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } type ${Actor} @node { - id: ID! @id @unique + id: ID! @id flag: Boolean! actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/global-node.int.test.ts b/packages/graphql/tests/integration/global-node.int.test.ts index 40dabe9be2..80c94c96f0 100644 --- a/packages/graphql/tests/integration/global-node.int.test.ts +++ b/packages/graphql/tests/integration/global-node.int.test.ts @@ -35,7 +35,7 @@ describe("Global node resolution", () => { test("returns the correct id after create mutation when the id is autogenerated", async () => { const typeDefs = `type ${typeFilm.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! }`; @@ -392,7 +392,7 @@ describe("Global node resolution", () => { test("it should throw forbidden when incorrect allow on a top-level node query", async () => { const typeDefs = ` type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! created: [${typeFilm.name}!]! @relationship(type: "CREATED", direction: OUT) } @@ -447,7 +447,7 @@ describe("Global node resolution", () => { const typeDefs = ` type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! } @@ -500,7 +500,7 @@ describe("Global node resolution", () => { } type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! } diff --git a/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts b/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts index 1af188e2cc..3fa331a751 100644 --- a/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts @@ -40,13 +40,13 @@ describe("Top-level interface query fields", () => { typeDefs = ` type ${SomeNodeType} implements MyOtherInterface & MyInterface @node { - id: ID! @id @unique + id: ID! @id something: String somethingElse: String other: [${OtherNodeType}!]! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique + id: ID! @id interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } interface MyInterface { @@ -58,11 +58,11 @@ describe("Top-level interface query fields", () => { } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } type ${MyOtherImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id someField: String } diff --git a/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts b/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts index b3edcd4ca6..40e6014cc6 100644 --- a/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts @@ -32,18 +32,18 @@ describe("Interfaces tests", () => { beforeAll(async () => { const typeDefs = ` type ${SomeNodeType} @node { - id: ID! @id @unique + id: ID! @id other: ${OtherNodeType}! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique + id: ID! @id interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } interface MyInterface { id: ID! } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } extend type ${SomeNodeType} @authentication diff --git a/packages/graphql/tests/integration/issues/1115.int.test.ts b/packages/graphql/tests/integration/issues/1115.int.test.ts deleted file mode 100644 index cbf322067d..0000000000 --- a/packages/graphql/tests/integration/issues/1115.int.test.ts +++ /dev/null @@ -1,104 +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 type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1115", () => { - let parentType: UniqueType; - let childType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - parentType = testHelper.createUniqueType("Parent"); - childType = testHelper.createUniqueType("Child"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${parentType} @node { - children: [${childType}!]! @relationship(type: "HAS", direction: IN) - } - - type ${childType} @node { - tcId: String @unique - } - - extend type ${childType} - @authorization( - validate: [ - { operations: [READ, CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP], where: { jwt: { roles_INCLUDES: "upstream" } } } - { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } - ] - ) - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not throw on multiple connectOrCreate with auth", async () => { - await testHelper.executeCypher(`CREATE (:${parentType})<-[:HAS]-(:${childType} {tcId: "123"})`); - - const token = testHelper.createBearerToken("secret", { roles: ["upstream"] }); - const query = /* GraphQL */ ` - mutation { - ${parentType.operations.update}( - update: { - children: [ - { - connectOrCreate: { - where: { node: { tcId_EQ: "123" } } - onCreate: { node: { tcId: "123" } } - }} - { - connectOrCreate: { - where: { node: { tcId_EQ: "456" } } - onCreate: { node: { tcId: "456" } } - }} - ] - } - ) { - info { - nodesCreated - } - } - } - `; - - const res = await testHelper.executeGraphQLWithToken(query, token); - - expect(res.errors).toBeUndefined(); - expect(res.data).toEqual({ - [parentType.operations.update]: { info: { nodesCreated: 1 } }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1121.int.test.ts b/packages/graphql/tests/integration/issues/1121.int.test.ts index dbe808f586..dc56962978 100644 --- a/packages/graphql/tests/integration/issues/1121.int.test.ts +++ b/packages/graphql/tests/integration/issues/1121.int.test.ts @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/1121", () => { } type ${Banana.name} implements Ingredient_Interface @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float @@ -58,14 +58,14 @@ describe("https://github.com/neo4j/graphql/issues/1121", () => { union Ingredient_Union = ${Sugar.name} | ${Syrup.name} type ${Sugar.name} @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float } type ${Syrup.name} @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float diff --git a/packages/graphql/tests/integration/issues/1127.int.test.ts b/packages/graphql/tests/integration/issues/1127.int.test.ts deleted file mode 100644 index 9ebd076249..0000000000 --- a/packages/graphql/tests/integration/issues/1127.int.test.ts +++ /dev/null @@ -1,131 +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 type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1127", () => { - let customerType: UniqueType; - let addressType: UniqueType; - let postalCodeType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - customerType = testHelper.createUniqueType("Customer"); - addressType = testHelper.createUniqueType("Address"); - postalCodeType = testHelper.createUniqueType("PostalCode"); - - const typeDefs = ` - type ${customerType.name} @node { - uuid: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - - address: ${addressType.name}! @relationship(type: "TEST_HAS_ADDRESS", direction: OUT) - } - - type ${addressType.name} @node { - uuid: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - - postalCode: ${postalCodeType.name}! @relationship(type: "TEST_HAS_POSTAL_CODE", direction: OUT) - } - - type ${postalCodeType.name} @node { - number: String! @unique - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should be able to connectOrCreate under nested create", async () => { - const query = ` - mutation CreateTestCustomers($input: [${customerType.name}CreateInput!]!) { - ${customerType.operations.create}(input: $input) { - info { - nodesCreated - relationshipsCreated - } - ${customerType.plural} { - address { - postalCode { - number - } - } - } - } - } - `; - - const res = await testHelper.executeGraphQL(query, { - variableValues: { - input: [ - { - address: { - create: { - node: { - postalCode: { - connectOrCreate: { - where: { - node: { - number_EQ: "00001", - }, - }, - onCreate: { - node: { - number: "00001", - }, - }, - }, - }, - }, - }, - }, - }, - ], - }, - }); - - expect(res.errors).toBeUndefined(); - - expect(res.data).toEqual({ - [customerType.operations.create]: { - info: { - nodesCreated: 3, - relationshipsCreated: 2, - }, - [customerType.plural]: [ - { - address: { - postalCode: { - number: "00001", - }, - }, - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1150.int.test.ts b/packages/graphql/tests/integration/issues/1150.int.test.ts index 8fbad714d3..bf93182c38 100644 --- a/packages/graphql/tests/integration/issues/1150.int.test.ts +++ b/packages/graphql/tests/integration/issues/1150.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { } type ${Battery} @node { - id: ID! @unique + id: ID! current: Boolean! } @@ -51,12 +51,12 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "admin" } } }]) type ${CombustionEngine} @node { - id: ID! @unique + id: ID! current: Boolean! } type ${Drive} @node { - id: ID! @unique + id: ID! current: Boolean! driveCompositions: [${DriveComposition}!]! @relationship(type: "CONSISTS_OF", properties: "RelationProps", direction: OUT) @@ -65,7 +65,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { union DriveComponent = ${Battery} | ${CombustionEngine} type ${DriveComposition} @node { - id: ID! @unique + id: ID! current: Boolean! driveComponent: [DriveComponent!]! @relationship(type: "HAS", properties: "RelationProps", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/1221.int.test.ts b/packages/graphql/tests/integration/issues/1221.int.test.ts index e50e11c8aa..6d9d3c1658 100644 --- a/packages/graphql/tests/integration/issues/1221.int.test.ts +++ b/packages/graphql/tests/integration/issues/1221.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { typeDefs = ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -55,7 +55,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } @@ -63,7 +63,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { extendedTypeDefs = ` type ${testMain} @node { - id: ID! @unique + id: ID! current: Boolean! main: [${testSeries}!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/1287.int.test.ts b/packages/graphql/tests/integration/issues/1287.int.test.ts index 55a986c4b5..6fc9a718a2 100644 --- a/packages/graphql/tests/integration/issues/1287.int.test.ts +++ b/packages/graphql/tests/integration/issues/1287.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/1287", () => { typeDefs = ` type ${screeningsType} @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! movie: ${norwegianScreenable}! @relationship(type: "SCREENS_MOVIE", direction: OUT) @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/1287", () => { } type ${norwegianScreenable} implements ScreenableMeta @node { - id: ID! @id @unique + id: ID! @id spokenLanguage: String! subtitlesLanguage: String! premiere: DateTime! diff --git a/packages/graphql/tests/integration/issues/1430.int.test.ts b/packages/graphql/tests/integration/issues/1430.int.test.ts index ab11024bab..ff5e5ba260 100644 --- a/packages/graphql/tests/integration/issues/1430.int.test.ts +++ b/packages/graphql/tests/integration/issues/1430.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/1430", () => { const typeDefs = ` type ${testAbce.name} @node { - id:ID @id @unique + id:ID @id name: String interface: InterfaceMom @relationship(type:"HAS_INTERFACE", direction:OUT) } @@ -45,13 +45,13 @@ describe("https://github.com/neo4j/graphql/issues/1430", () => { } type ${testChildOne.name} implements InterfaceMom @node { - id:ID @id @unique + id:ID @id name:String feathur: String } type ${testChildTwo.name} implements InterfaceMom @node { - id:ID @id @unique + id:ID @id name:String sth: String } diff --git a/packages/graphql/tests/integration/issues/1535.int.test.ts b/packages/graphql/tests/integration/issues/1535.int.test.ts index e0c203a9a5..ff1a626d6c 100644 --- a/packages/graphql/tests/integration/issues/1535.int.test.ts +++ b/packages/graphql/tests/integration/issues/1535.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { const typeDefs = ` type ${testTenant} @node { - id: ID! @id @unique + id: ID! @id name: String! events: [Event!]! @relationship(type: "HOSTED_BY", direction: IN) fooBars: [${FooBar}!]! @relationship(type: "HAS_FOOBARS", direction: OUT) @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type Screening implements Event @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! } @@ -60,7 +60,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type ${FooBar} @node { - id: ID! @id @unique + id: ID! @id name: String! } `; diff --git a/packages/graphql/tests/integration/issues/1536.int.test.ts b/packages/graphql/tests/integration/issues/1536.int.test.ts index 44ecd00237..8ce98421b2 100644 --- a/packages/graphql/tests/integration/issues/1536.int.test.ts +++ b/packages/graphql/tests/integration/issues/1536.int.test.ts @@ -34,12 +34,12 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { const typeDefs = ` type ${SomeNodeType} @node { - id: ID! @id @unique + id: ID! @id other: ${OtherNodeType}! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique + id: ID! @id interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } @@ -48,7 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } `; diff --git a/packages/graphql/tests/integration/issues/1628.int.test.ts b/packages/graphql/tests/integration/issues/1628.int.test.ts index 9cef9a5e4f..20a69093b1 100644 --- a/packages/graphql/tests/integration/issues/1628.int.test.ts +++ b/packages/graphql/tests/integration/issues/1628.int.test.ts @@ -30,7 +30,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { """ IRI """ - iri: ID! @unique @alias(property: "uri") + iri: ID! @alias(property: "uri") title: [${titleType}!]! @relationship(type: "title", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/1735.int.test.ts b/packages/graphql/tests/integration/issues/1735.int.test.ts index 9e1b3142b9..7a401c4521 100644 --- a/packages/graphql/tests/integration/issues/1735.int.test.ts +++ b/packages/graphql/tests/integration/issues/1735.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${movieType.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! actors: [${actorType.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "MovieActorEdgeProperties") leadActorsCount: Int! @cypher(statement:""" @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${actorType.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [${movieType.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "MovieActorEdgeProperties") } diff --git a/packages/graphql/tests/integration/issues/1751.int.test.ts b/packages/graphql/tests/integration/issues/1751.int.test.ts index 63c827c736..551525fe6f 100644 --- a/packages/graphql/tests/integration/issues/1751.int.test.ts +++ b/packages/graphql/tests/integration/issues/1751.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${adminType} @node { - adminId: ID! @id @unique + adminId: ID! @id organizations: [${organizationType}!]! @relationship(type: "HAS_ADMINISTRATOR", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/1756.int.test.ts b/packages/graphql/tests/integration/issues/1756.int.test.ts deleted file mode 100644 index f691b2e217..0000000000 --- a/packages/graphql/tests/integration/issues/1756.int.test.ts +++ /dev/null @@ -1,152 +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 type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1756", () => { - let productType: UniqueType; - let genreType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - productType = testHelper.createUniqueType("Product"); - genreType = testHelper.createUniqueType("Genre"); - const typeDefs = ` - interface INode { - id: ID! - } - - type ${productType.name} implements INode @node { - id: ID! @populatedBy(operations: [CREATE], callback: "nanoid") - name: String! - genre: [${genreType.name}!]! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type ${genreType.name} implements INode @node { - id: ID! @populatedBy(operations: [CREATE], callback: "nanoid") - value: String! @unique - } - `; - - const nanoid = () => { - return `callback_value`; - }; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { populatedBy: { callbacks: { nanoid } } } }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not raise a GraphQL validation error if invoked without passing the id field", async () => { - const query = ` - mutation { - ${productType.operations.create}(input: { - name: "TestProduct", - genre: { - connectOrCreate: [ - { - where: { - node: { - value_EQ: "Action" - } - }, - onCreate: { - node: { - value: "Action" - } - } - } - ] - } - }) { - ${productType.plural} { - id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeFalsy(); - expect(result?.data?.[productType.operations.create]).toEqual({ - [productType.plural]: [ - { - id: "callback_value", - }, - ], - }); - }); - test("should define the ID using the callback function", async () => { - const query = ` - mutation { - ${productType.operations.create}(input: { - name: "TestProduct", - genre: { - connectOrCreate: [ - { - where: { - node: { - value_EQ: "Action" - } - }, - onCreate: { - node: { - value: "Action" - } - } - } - ] - } - }) { - ${productType.plural} { - id - name - genre { - id - value - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeFalsy(); - expect(result?.data?.[productType.operations.create]).toEqual({ - [productType.plural]: [ - { - id: "callback_value", - name: "TestProduct", - genre: [ - { - id: "callback_value", - value: "Action", - }, - ], - }, - ], - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1760.int.test.ts b/packages/graphql/tests/integration/issues/1760.int.test.ts index e848e02b8c..c6da846d10 100644 --- a/packages/graphql/tests/integration/issues/1760.int.test.ts +++ b/packages/graphql/tests/integration/issues/1760.int.test.ts @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { markets: [${Market}!]! @relationship(type: "HAS_MARKETS", direction: OUT) - id: ID! @unique + id: ID! relatedId: ID @cypher(statement: "MATCH (this)<-[:HAS_BASE]-(n:${BaseObject}) RETURN n.id as res", columnName: "res") baseObject: ${BaseObject}! @relationship(type: "HAS_BASE", direction: IN) @@ -69,14 +69,14 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { type ${Market} implements BusinessObject @node @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @unique + id: ID! nameDetails: ${NameDetails} @relationship(type: "HAS_NAME", direction: OUT) } type ${BaseObject} @node @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @id @unique + id: ID! @id } `; diff --git a/packages/graphql/tests/integration/issues/1782.int.test.ts b/packages/graphql/tests/integration/issues/1782.int.test.ts index c2015f75aa..e00924ea05 100644 --- a/packages/graphql/tests/integration/issues/1782.int.test.ts +++ b/packages/graphql/tests/integration/issues/1782.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { const typeDefs = ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } @@ -59,7 +59,7 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { const extendedTypeDefs = ` type ${testMain} @node { - id: ID! @unique + id: ID! current: Boolean! main: [${testSeries}!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/1783.int.test.ts b/packages/graphql/tests/integration/issues/1783.int.test.ts index b65b02efd2..0b0940c230 100644 --- a/packages/graphql/tests/integration/issues/1783.int.test.ts +++ b/packages/graphql/tests/integration/issues/1783.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { const typeDefs = ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -50,7 +50,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/1817.int.test.ts b/packages/graphql/tests/integration/issues/1817.int.test.ts index e9e6d225d1..95a8773ba6 100644 --- a/packages/graphql/tests/integration/issues/1817.int.test.ts +++ b/packages/graphql/tests/integration/issues/1817.int.test.ts @@ -34,21 +34,21 @@ describe("https://github.com/neo4j/graphql/issues/1817", () => { const typeDefs = ` type ${TypeContainerType} @node { - id: ID! @id @unique + id: ID! @id name: String! specifiesContainers: [${TypeContainer}!]! @relationship(type: "hasContainer", properties: "CoT_Co_hasContainer", direction: OUT) } type ${TypeContainer} @node { - id: ID! @id @unique + id: ID! @id name: String containsMaterial: [${TypeMaterial}!]! @relationship(type: "hasMaterial", properties: "Co_Ma_hasMaterial", direction: OUT) } type ${TypeMaterial} @node { - id: ID! @id @unique + id: ID! @id name: String } diff --git a/packages/graphql/tests/integration/issues/1848.int.test.ts b/packages/graphql/tests/integration/issues/1848.int.test.ts index 033479934a..5fdd0b5895 100644 --- a/packages/graphql/tests/integration/issues/1848.int.test.ts +++ b/packages/graphql/tests/integration/issues/1848.int.test.ts @@ -33,17 +33,17 @@ describe("https://github.com/neo4j/graphql/issues/1848", () => { Community = testHelper.createUniqueType("Community"); const typeDefs = ` type ${ContentPiece} @node(labels: ["${ContentPiece}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type ${Project} @node(labels: ["${Project}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type ${Community} @node(labels: ["${Community}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int hasContentPieces: [${ContentPiece}!]! @relationship(type: "COMMUNITY_CONTENTPIECE_HASCONTENTPIECES", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/1933.int.test.ts b/packages/graphql/tests/integration/issues/1933.int.test.ts index 34afc69339..1ee25ad404 100644 --- a/packages/graphql/tests/integration/issues/1933.int.test.ts +++ b/packages/graphql/tests/integration/issues/1933.int.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { const typeDefs = ` type ${employeeType} @node { - employeeId: ID! @unique + employeeId: ID! firstName: String! @settable(onCreate: false, onUpdate: false) lastName: String @settable(onCreate: false, onUpdate: false) projects: [${projectType}!]! @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } type ${projectType} @node { - projectId: ID! @unique + projectId: ID! name: String! @settable(onCreate: false, onUpdate: false) description: String employees: [${employeeType}!]! diff --git a/packages/graphql/tests/integration/issues/200.int.test.ts b/packages/graphql/tests/integration/issues/200.int.test.ts index 93925efddb..d2f9072426 100644 --- a/packages/graphql/tests/integration/issues/200.int.test.ts +++ b/packages/graphql/tests/integration/issues/200.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/200", () => { test("should successfully execute given mutation", async () => { const typeDefs = ` type ${Category} @node { - categoryId: ID! @id @unique + categoryId: ID! @id name: String! description: String! @default(value: "") exampleImageLocations: [String!] diff --git a/packages/graphql/tests/integration/issues/2022.int.test.ts b/packages/graphql/tests/integration/issues/2022.int.test.ts index fac68c8bc3..7368bc6975 100644 --- a/packages/graphql/tests/integration/issues/2022.int.test.ts +++ b/packages/graphql/tests/integration/issues/2022.int.test.ts @@ -34,14 +34,14 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { const typeDefs = ` type ${ArtPiece} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! auction: ${AuctionItem}! @relationship(type: "SOLD_AT_AUCTION_AS", direction: OUT) owner: ${Organization}! @relationship(type: "OWNED_BY", direction: OUT) } type ${AuctionItem} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") auctionName: String! lotNumber: Int! @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { } type ${Organization} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! artCollection: [${ArtPiece}!]! @relationship(type: "OWNED_BY", direction: IN) diff --git a/packages/graphql/tests/integration/issues/2068.int.test.ts b/packages/graphql/tests/integration/issues/2068.int.test.ts deleted file mode 100644 index daa96c508a..0000000000 --- a/packages/graphql/tests/integration/issues/2068.int.test.ts +++ /dev/null @@ -1,854 +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 { createBearerToken } from "../../utils/create-bearer-token"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/pull/2068", () => { - const testHelper = new TestHelper(); - - beforeEach(() => {}); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Unions in cypher directives", async () => { - const actorName = "someName"; - const actorAge = 43; - const numberOfSeasons = 2; - const sharedTitle = "someTitle"; - - const actorType = testHelper.createUniqueType("Actor"); - const movieType = testHelper.createUniqueType("Movie"); - const tvShowType = testHelper.createUniqueType("TVShow"); - const movieOrTVShowType = testHelper.createUniqueType("MovieOrTVShow"); - - const typeDefs = ` - type ${actorType.name} @node { - name: String - age: Int - movies(title: String): [${movieType.name}] - @cypher( - statement: """ - MATCH (m:${movieType.name} {title: $title}) - RETURN m - """, - columnName: "m" - ) - - tvShows(title: String): [${movieType.name}] - @cypher( - statement: """ - MATCH (t:${tvShowType.name} {title: $title}) - RETURN t - """, - columnName: "t" - ) - - movieOrTVShow(title: String): [${movieOrTVShowType.name}] - @cypher( - statement: """ - MATCH (n) - WHERE (n:${tvShowType.name} OR n:${movieType.name}) AND ($title IS NULL OR n.title = $title) - RETURN n - """, - columnName: "n" - ) - } - - union ${movieOrTVShowType.name} = ${movieType.name} | ${tvShowType.name} - - type ${tvShowType.name} @node { - id: ID - title: String - numSeasons: Int - actors: [${actorType.name}] - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - topActor: ${actorType.name} - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - } - - type ${movieType.name} @node { - id: ID - title: String - actors: [${actorType.name}] - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - topActor: ${actorType.name} - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - } - `; - - const query = ` - { - ${actorType.plural} { - movieOrTVShow(title: "${sharedTitle}") { - ... on ${movieType.name} { - title - topActor { - name - age - } - } - ... on ${tvShowType.name} { - title - numSeasons - topActor { - name - } - } - } - } - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher(` - CREATE (:${actorType.name} { name: "${actorName}", age: ${actorAge} }) - CREATE (:${tvShowType.name} { title: "${sharedTitle}", numSeasons: ${numberOfSeasons} }) - CREATE (:${movieType.name} { title: "${sharedTitle}" }) - `); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)?.[actorType.plural]?.[0].movieOrTVShow).toIncludeSameMembers([ - { - title: sharedTitle, - numSeasons: numberOfSeasons, - topActor: { - name: actorName, - }, - }, - { - title: sharedTitle, - topActor: { - name: actorName, - age: actorAge, - }, - }, - ]); - }); - - describe("Updates within updates", () => { - let contentType: UniqueType; - let userType: UniqueType; - let commentType: UniqueType; - let typeDefs: string; - - const secret = "secret"; - - beforeEach(() => { - contentType = testHelper.createUniqueType("Content"); - userType = testHelper.createUniqueType("User"); - commentType = testHelper.createUniqueType("Comment"); - - typeDefs = ` - interface ${contentType.name} { - id: ID - content: String - creator: ${userType.name}! @declareRelationship - } - - type ${userType.name} @node { - id: ID - name: String - content: [${contentType.name}!]! @relationship(type: "HAS_CONTENT", direction: OUT) - } - - type ${commentType.name} implements ${contentType.name} @node { - id: ID - content: String - creator: ${userType.name}! @relationship(type: "HAS_CONTENT", direction: IN) - } - - extend type ${userType.name} - @authorization(filter: [{ operations: [READ, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) - - extend type ${userType.name} @node { - password: String! @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) - } - `; - }); - - test("Connect node - update within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - const query = ` - mutation { - ${userType.operations.update}(update: { content: { connect: { where: { node: {} } } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"}) - CREATE (:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - - test("Connect node - user defined update where within an update", async () => { - const userID1 = "someID"; - const userID2 = "differentID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: { connect: { where: { node: { id_EQ: "${userID2}" } } } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID1}"}) - CREATE (:${userType.name} {id: "${userID2}"}) - CREATE (:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID1 }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID1, contentConnection: { totalCount: 0 } }]); - }); - - test("Disconnect node - update within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: { disconnect: { where: {} } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"})-[:HAS_CONTENT]->(:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - test("Disconnect node - user defined update where within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: [{ disconnect: { where: { node: { id_EQ: "${userID}" } } } }] }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"})-[:HAS_CONTENT]->(:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - }); - describe("connectOrCreate auth ordering", () => { - const secret = "secret"; - - const movieTitle = "Cool Movie"; - - const requiredRole = "admin"; - const forbiddenMessage = "Forbidden"; - const validToken = createBearerToken(secret, { roles: [requiredRole] }); - const invalidToken = createBearerToken(secret, { roles: [] }); - - /** - * Generate type definitions for connectOrCreate auth tests. - * @param operations The operations argument of auth rules. - * @returns Unique types and a graphql type deinition string. - */ - function getTypedef(operations: string): [UniqueType, UniqueType, string] { - const movieType = testHelper.createUniqueType("Movie"); - const genreType = testHelper.createUniqueType("Genre"); - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${movieType.name} @node { - title: String - genres: [${genreType.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${genreType.name} @authorization(validate: [{ operations: ${operations}, where: { jwt: { roles_INCLUDES: "${requiredRole}" } } }]) @node { - name: String @unique - } - `; - - return [movieType, genreType, typeDefs]; - } - - /** - * Generate a query for connectOrCreate auth tests. - * @param mutationType The type of mutation to perform. - * @param movieTypePlural The plural of the movie type. - * @returns A graphql query. - */ - function getQuery(mutationType: string, movieTypePlural: string): string { - let argType = "update"; - - if (mutationType.startsWith("create")) { - argType = "input"; - } - - return ` - mutation { - ${mutationType}( - ${argType}: { - title: "${movieTitle}" - genres: { - connectOrCreate: [ - { where: { node: { name_EQ: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ) { - ${movieTypePlural} { - title - } - } - } - `; - } - test("Create with createOrConnect and CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and CREATE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and DELETE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[DELETE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and CREATE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - - test("Update with createOrConnect and CREATE operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and DELETE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[DELETE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - }); - describe("Select connections following the creation of a multiple nodes", () => { - let movieType: UniqueType; - let actorType: UniqueType; - - let typeDefs: string; - - beforeEach(() => { - movieType = testHelper.createUniqueType("Movie"); - actorType = testHelper.createUniqueType("Actor"); - - typeDefs = ` - type ${movieType.name} @node { - title: String - actors: [${actorType.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${actorType.name} @node { - name: String - movies: [${movieType.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int - } - `; - }); - - test("Filtered results", async () => { - const filmName1 = "Forrest Gump"; - const filmName2 = "Toy Story"; - const actorName = "Tom Hanks"; - - const query = ` - mutation { - ${movieType.operations.create}(input: [{ title: "${filmName1}" }, { title: "${filmName2}" }]) { - ${movieType.plural} { - title - actorsConnection(where: { node: { name_EQ: "${actorName}" } }) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [movieType.operations.create]: { - [movieType.plural]: [ - { - actorsConnection: { - edges: [], - }, - title: filmName1, - }, - { - actorsConnection: { - edges: [], - }, - title: filmName2, - }, - ], - }, - }); - }); - test("Unfiltered results", async () => { - const filmName1 = "Forrest Gump"; - const filmName2 = "Toy Story"; - - const query = ` - mutation { - ${movieType.operations.create}(input: [{ title: "${filmName1}" }, { title: "${filmName2}" }]) { - ${movieType.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [movieType.operations.create]: { - [movieType.plural]: [ - { - actorsConnection: { - edges: [], - }, - title: filmName1, - }, - { - actorsConnection: { - edges: [], - }, - title: filmName2, - }, - ], - }, - }); - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/2100.int.test.ts b/packages/graphql/tests/integration/issues/2100.int.test.ts index 62ee370e9f..14d9555678 100644 --- a/packages/graphql/tests/integration/issues/2100.int.test.ts +++ b/packages/graphql/tests/integration/issues/2100.int.test.ts @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { } type ${BacentaType} implements Church @authentication @node { - id: ID @id @unique + id: ID @id name: String! serviceLogs: [${ServiceLogType}!]! @relationship(type: "HAS_HISTORY", direction: OUT) bussing(limit: Int!): [${BussingRecordType}!]! diff --git a/packages/graphql/tests/integration/issues/2189.int.test.ts b/packages/graphql/tests/integration/issues/2189.int.test.ts index a468fe4654..dd6cf245c1 100644 --- a/packages/graphql/tests/integration/issues/2189.int.test.ts +++ b/packages/graphql/tests/integration/issues/2189.int.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const typeDefs = ` type ${Test_Item} @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { ) } type ${Test_Feedback} @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean diff --git a/packages/graphql/tests/integration/issues/2249.int.test.ts b/packages/graphql/tests/integration/issues/2249.int.test.ts index aea32189f1..60d44f4d91 100644 --- a/packages/graphql/tests/integration/issues/2249.int.test.ts +++ b/packages/graphql/tests/integration/issues/2249.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { type ${Movie} @node { title: String! reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type Review @relationshipProperties { diff --git a/packages/graphql/tests/integration/issues/2261.int.test.ts b/packages/graphql/tests/integration/issues/2261.int.test.ts index b685e451a4..36b2a68997 100644 --- a/packages/graphql/tests/integration/issues/2261.int.test.ts +++ b/packages/graphql/tests/integration/issues/2261.int.test.ts @@ -43,13 +43,13 @@ describe("https://github.com/neo4j/graphql/issues/2261", () => { } type ${ProgrammeItem} implements Product @node { - id: ID! @id @unique + id: ID! @id uri: String! @cypher(statement: "RETURN 'example://programme-item/' + this.id as x", columnName: "x") editions: [${Edition}!]! @relationship(type: "HAS_EDITION", direction: OUT) } type ${Edition} @node { - id: ID! @id @unique + id: ID! @id uri: String! @cypher(statement: "RETURN 'example://edition/' + this.id as x", columnName: "x") product: Product! @relationship(type: "HAS_EDITION", direction: IN) } diff --git a/packages/graphql/tests/integration/issues/235.int.test.ts b/packages/graphql/tests/integration/issues/235.int.test.ts index 2c18638600..f9032e1f3a 100644 --- a/packages/graphql/tests/integration/issues/235.int.test.ts +++ b/packages/graphql/tests/integration/issues/235.int.test.ts @@ -40,19 +40,19 @@ describe("https://github.com/neo4j/graphql/issues/235", () => { test("should create the correct number of nodes following multiple connect", async () => { const typeDefs = /* GraphQL */ ` type ${A} @node { - ID: ID! @id @unique + ID: ID! @id name: String rel_b: [${B}!]! @relationship(type: "REL_B", direction: OUT) rel_c: [${C}!]! @relationship(type: "REL_C", direction: OUT) } type ${B} @node { - ID: ID! @id @unique + ID: ID! @id name: String } type ${C} @node { - ID: ID! @id @unique + ID: ID! @id name: String } `; diff --git a/packages/graphql/tests/integration/issues/2388.int.test.ts b/packages/graphql/tests/integration/issues/2388.int.test.ts index a87b37f386..e56bfa7ef6 100644 --- a/packages/graphql/tests/integration/issues/2388.int.test.ts +++ b/packages/graphql/tests/integration/issues/2388.int.test.ts @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2388", () => { { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } ]) { - id: ID! @id @unique + id: ID! @id } type ${PartUsage} @node diff --git a/packages/graphql/tests/integration/issues/2396.int.test.ts b/packages/graphql/tests/integration/issues/2396.int.test.ts index ceaa8bc0e6..d79b29d70b 100644 --- a/packages/graphql/tests/integration/issues/2396.int.test.ts +++ b/packages/graphql/tests/integration/issues/2396.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { const typeDefs = ` type ${PostalCode} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! address: [${Address}!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Address} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -60,7 +60,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Mandate} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: ID! @id @unique # numéro + number: ID! @id # numéro createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -73,7 +73,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Valuation} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -99,7 +99,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Estate} @node @mutation(operations: [CREATE, UPDATE]) { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) diff --git a/packages/graphql/tests/integration/issues/2437.int.test.ts b/packages/graphql/tests/integration/issues/2437.int.test.ts index 6b3dd1e073..95837879f3 100644 --- a/packages/graphql/tests/integration/issues/2437.int.test.ts +++ b/packages/graphql/tests/integration/issues/2437.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { } type ${Agent} @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime valuations: [${Valuation}!]! @relationship(type: "IS_VALUATION_AGENT", direction: OUT) @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { @authorization(validate: [{ operations: [CREATE], where: { jwt: { roles_INCLUDES: "Admin" } } }], filter: [{ where: { node: { archivedAt_EQ: null } } }]) type ${Valuation} @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime agent: ${Agent}! @relationship(type: "IS_VALUATION_AGENT", direction: IN) diff --git a/packages/graphql/tests/integration/issues/2474.int.test.ts b/packages/graphql/tests/integration/issues/2474.int.test.ts index f995d655c4..6a03fbb2a8 100644 --- a/packages/graphql/tests/integration/issues/2474.int.test.ts +++ b/packages/graphql/tests/integration/issues/2474.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { const typeDefs = ` type ${PostalCode.name} @node { archivedAt: DateTime - number: String! @unique + number: String! address: [${Address.name}!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } @@ -48,7 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Address.name} @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) postalCode: ${PostalCode.name} @relationship(type: "HAS_POSTAL_CODE", direction: OUT) @@ -57,7 +57,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Mandate.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: ID! @id @unique # numéro + number: ID! @id # numéro createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) price: Float! @@ -66,7 +66,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Valuation.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) estate: ${Estate.name} @relationship(type: "VALUATION_FOR", direction: OUT) @@ -89,7 +89,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Estate.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) estateType: EstateType! diff --git a/packages/graphql/tests/integration/issues/2548.int.test.ts b/packages/graphql/tests/integration/issues/2548.int.test.ts index 8c624f9a4e..761e7fcead 100644 --- a/packages/graphql/tests/integration/issues/2548.int.test.ts +++ b/packages/graphql/tests/integration/issues/2548.int.test.ts @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2548", () => { { operations: [READ], where: { jwt: { roles_INCLUDES: "ADMIN" } } } ] ) { - userId: ID! @id @unique + userId: ID! @id isPublic: Boolean } `; diff --git a/packages/graphql/tests/integration/issues/2574.int.test.ts b/packages/graphql/tests/integration/issues/2574.int.test.ts index a7644f585c..dac1dd68d9 100644 --- a/packages/graphql/tests/integration/issues/2574.int.test.ts +++ b/packages/graphql/tests/integration/issues/2574.int.test.ts @@ -34,19 +34,19 @@ describe("https://github.com/neo4j/graphql/issues/2574", () => { const typeDefs = ` type ${A} @node { - uuid: ID! @id @unique + uuid: ID! @id child: ${D}! @relationship(type: "HAS_PARENT", direction: IN) } type ${B} @node { - uuid: ID! @id @unique + uuid: ID! @id child: ${D}! @relationship(type: "HAS_PARENT", direction: IN) } union C = ${A} | ${B} type ${D} @node { - uuid: ID! @id @unique + uuid: ID! @id test: String! parent: C! @relationship(type: "HAS_PARENT", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/2581.int.test.ts b/packages/graphql/tests/integration/issues/2581.int.test.ts index 901254647b..3931da7e63 100644 --- a/packages/graphql/tests/integration/issues/2581.int.test.ts +++ b/packages/graphql/tests/integration/issues/2581.int.test.ts @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { type ${Book} @node { name: String! year: Int - refID: ID @id @unique + refID: ID @id soldCopies: Int @cypher( statement: "OPTIONAL MATCH(sales:${Sales}) WHERE this.refID = sales.refID WITH count(sales) as result RETURN result as result" diff --git a/packages/graphql/tests/integration/issues/2630.int.test.ts b/packages/graphql/tests/integration/issues/2630.int.test.ts index a743235842..2373266622 100644 --- a/packages/graphql/tests/integration/issues/2630.int.test.ts +++ b/packages/graphql/tests/integration/issues/2630.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/2630", () => { } type ${Post} @node { - id: ID! @id @unique + id: ID! @id subject: ${PostSubject}! @relationship(type: "POST_FOR", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/2812.int.test.ts b/packages/graphql/tests/integration/issues/2812.int.test.ts index 65a469b45f..ba19624c60 100644 --- a/packages/graphql/tests/integration/issues/2812.int.test.ts +++ b/packages/graphql/tests/integration/issues/2812.int.test.ts @@ -39,7 +39,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { } type ${Actor} @authorization(validate: [{ where: { node: { nodeCreatedBy_EQ: "$jwt.sub" } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String nodeCreatedBy: String fieldA: String @authorization(validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "role-A" } } }]) diff --git a/packages/graphql/tests/integration/issues/283.int.test.ts b/packages/graphql/tests/integration/issues/283.int.test.ts index a0651b5f35..4e34a1ed05 100644 --- a/packages/graphql/tests/integration/issues/283.int.test.ts +++ b/packages/graphql/tests/integration/issues/283.int.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/283", () => { } type ${Post} @node { - id: ID! @id @unique + id: ID! @id title: String! datetime: DateTime @timestamp(operations: [CREATE]) } diff --git a/packages/graphql/tests/integration/issues/2871.int.test.ts b/packages/graphql/tests/integration/issues/2871.int.test.ts index ac5dbed778..46a038969f 100644 --- a/packages/graphql/tests/integration/issues/2871.int.test.ts +++ b/packages/graphql/tests/integration/issues/2871.int.test.ts @@ -96,19 +96,19 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { const typeDefs = ` type ${FirstLevel} @node { - id: ID! @id @unique + id: ID! @id secondLevel: ${SecondLevel}! @relationship(type: "HAS_SECOND_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type ${SecondLevel} @node { - id: ID! @id @unique + id: ID! @id thirdLevel: [${ThirdLevel}!]! @relationship(type: "HAS_THIRD_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type ${ThirdLevel} @node { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) } `; diff --git a/packages/graphql/tests/integration/issues/3251.int.test.ts b/packages/graphql/tests/integration/issues/3251.int.test.ts index 536acba9e0..a95492061e 100644 --- a/packages/graphql/tests/integration/issues/3251.int.test.ts +++ b/packages/graphql/tests/integration/issues/3251.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/3251", () => { } type ${Genre} @node { - name: String! @unique + name: String! movies: [${Movie}!]! @relationship(type: "HAS_GENRE", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/3428.int.test.ts b/packages/graphql/tests/integration/issues/3428.int.test.ts index d030f33581..ecd3b7b2fa 100644 --- a/packages/graphql/tests/integration/issues/3428.int.test.ts +++ b/packages/graphql/tests/integration/issues/3428.int.test.ts @@ -172,7 +172,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { test("Should not error and should only be able to perform the disconnect nested op when only the DISCONNECT nestedOperation is specified on rel to a type with a unique field", async () => { const typeDefs = `#graphql type ${Person} @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -238,70 +238,6 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { 'Unknown argument "delete" on field' ); }); - - test("Should only be able to perform the disconnect and connectOrCreate nested ops when DISCONNECT and CONNECT_OR_CREATE are the only nestedOperations specified", async () => { - const typeDefs = `#graphql - type ${Person} @node { - id: ID! @id @unique - name: String - } - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [DISCONNECT, CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeFalsy(); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); }); describe("Related to a union", () => { @@ -451,7 +387,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { test("Should not error and should only be able to perform the disconnect nested op when only the DISCONNECT nestedOperation is specified on rel to a type with a unique field", async () => { const typeDefs = `#graphql type ${PersonOne} @node { - name: String @unique + name: String } type ${PersonTwo} @node { @@ -522,74 +458,5 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { 'Unknown argument "delete" on field' ); }); - - test("Should only be able to perform the disconnect and connectOrCreate nested ops when DISCONNECT and CONNECT_OR_CREATE are the only nestedOperations specified", async () => { - const typeDefs = `#graphql - type ${PersonOne} @node { - name: String @unique - } - - type ${PersonTwo} @node { - nameTwo: String - } - - union ${Person} = ${PersonOne} | ${PersonTwo} - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [DISCONNECT, CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeFalsy(); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); }); }); diff --git a/packages/graphql/tests/integration/issues/3929.int.test.ts b/packages/graphql/tests/integration/issues/3929.int.test.ts index 42654a10f8..f3710d7b46 100644 --- a/packages/graphql/tests/integration/issues/3929.int.test.ts +++ b/packages/graphql/tests/integration/issues/3929.int.test.ts @@ -41,20 +41,20 @@ describe("https://github.com/neo4j/graphql/issues/3929", () => { } type ${User} @authorization(filter: [{ where: { node: { id_EQ: "$jwt.uid" } } }]) @node { - id: ID! @unique + id: ID! email: String! name: String } type ${Group} @authorization(validate: [{ where: { node: { creator: { id_EQ: "$jwt.uid" } } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String members: [${Person}!]! @relationship(type: "MEMBER_OF", direction: IN) creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) } type ${Person} @authorization(validate: [{ where: { node: { creator: { id_EQ: "$jwt.uid" }}}}]) @node { - id: ID! @id @unique + id: ID! @id name: String! creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN) group: ${Group}! @relationship(type: "MEMBER_OF", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/3938.int.test.ts b/packages/graphql/tests/integration/issues/3938.int.test.ts index 3a2eeb521b..8d89534056 100644 --- a/packages/graphql/tests/integration/issues/3938.int.test.ts +++ b/packages/graphql/tests/integration/issues/3938.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/3938", () => { const typeDefs = /* GraphQL */ ` type ${Group} @node { - id: ID! @id @unique + id: ID! @id name: String! invitees: [${Invitee}!]! @relationship(type: "INVITED_TO", direction: IN, aggregate: true) } @@ -50,7 +50,7 @@ describe("https://github.com/neo4j/graphql/issues/3938", () => { { operations: [CREATE], where: { node: { group: { inviteesAggregate: { count_LT: 5 } } } } } ] ) { - id: ID! @id @unique + id: ID! @id group: ${Group}! @relationship(type: "INVITED_TO", direction: OUT) email: String! status: InviteeStatus! @default(value: PENDING) diff --git a/packages/graphql/tests/integration/issues/4056.int.test.ts b/packages/graphql/tests/integration/issues/4056.int.test.ts index 0191304367..695654a370 100644 --- a/packages/graphql/tests/integration/issues/4056.int.test.ts +++ b/packages/graphql/tests/integration/issues/4056.int.test.ts @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/4056", () => { { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/4112.int.test.ts b/packages/graphql/tests/integration/issues/4112.int.test.ts index 630f9b6364..238afb1fc0 100644 --- a/packages/graphql/tests/integration/issues/4112.int.test.ts +++ b/packages/graphql/tests/integration/issues/4112.int.test.ts @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; @@ -93,7 +93,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; @@ -139,7 +139,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; diff --git a/packages/graphql/tests/integration/issues/4113.int.test.ts b/packages/graphql/tests/integration/issues/4113.int.test.ts index 2eb380a139..684a5c3bfa 100644 --- a/packages/graphql/tests/integration/issues/4113.int.test.ts +++ b/packages/graphql/tests/integration/issues/4113.int.test.ts @@ -44,21 +44,21 @@ describe("https://github.com/neo4j/graphql/issues/4113", () => { } type ${User.name} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! store: ${Store.name} @relationship(type: "WORKS_AT", direction: OUT) } type ${Store.name} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User.name}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction.name}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction.name} @node { - id: ID! @id @unique + id: ID! @id store: ${Store.name}! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [${TransactionItem.name}!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) @@ -243,21 +243,21 @@ describe("replicates the test for relationship to interface so that multiple ref } type ${User.name} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! store: ${Store.name} @relationship(type: "WORKS_AT", direction: OUT) } type ${Store.name} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User.name}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction.name}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction.name} @node { - id: ID! @id @unique + id: ID! @id store: ${Store.name}! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [TransactionItemI!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) diff --git a/packages/graphql/tests/integration/issues/4115.int.test.ts b/packages/graphql/tests/integration/issues/4115.int.test.ts index b884ee23d3..700496b7ac 100644 --- a/packages/graphql/tests/integration/issues/4115.int.test.ts +++ b/packages/graphql/tests/integration/issues/4115.int.test.ts @@ -36,12 +36,12 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { const typeDefs = ` type ${User} @node { - id: ID! @unique + id: ID! roles: [String!]! } type ${Family} @node { - id: ID! @id @unique + id: ID! @id members: [${Person}!]! @relationship(type: "MEMBER_OF", direction: IN) creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN) } @@ -59,7 +59,7 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { } ] ) { - id: ID! @id @unique + id: ID! @id creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) family: ${Family}! @relationship(type: "MEMBER_OF", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/4118.int.test.ts b/packages/graphql/tests/integration/issues/4118.int.test.ts index 30ad5ea229..7bd6e81e2e 100644 --- a/packages/graphql/tests/integration/issues/4118.int.test.ts +++ b/packages/graphql/tests/integration/issues/4118.int.test.ts @@ -65,7 +65,7 @@ describe("https://github.com/neo4j/graphql/issues/4118", () => { { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/413.int.test.ts b/packages/graphql/tests/integration/issues/413.int.test.ts index af18f5aa7c..7012603e61 100644 --- a/packages/graphql/tests/integration/issues/413.int.test.ts +++ b/packages/graphql/tests/integration/issues/413.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/413", () => { } type ${JobPlan} @node { - id: ID! @id @unique + id: ID! @id tenantID: ID! name: String! } diff --git a/packages/graphql/tests/integration/issues/4170.int.test.ts b/packages/graphql/tests/integration/issues/4170.int.test.ts index 18b15c4cda..7c1b5e7543 100644 --- a/packages/graphql/tests/integration/issues/4170.int.test.ts +++ b/packages/graphql/tests/integration/issues/4170.int.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/4214.int.test.ts b/packages/graphql/tests/integration/issues/4214.int.test.ts index 806ac2fd17..a683f304c7 100644 --- a/packages/graphql/tests/integration/issues/4214.int.test.ts +++ b/packages/graphql/tests/integration/issues/4214.int.test.ts @@ -44,21 +44,21 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { } type ${User} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! store: ${Store} @relationship(type: "WORKS_AT", direction: OUT) } type ${Store} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction} @node { - id: ID! @id @unique + id: ID! @id store: ${Store}! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [${TransactionItem}!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) diff --git a/packages/graphql/tests/integration/issues/4223.int.test.ts b/packages/graphql/tests/integration/issues/4223.int.test.ts index 873d1d17c3..ea894d4c40 100644 --- a/packages/graphql/tests/integration/issues/4223.int.test.ts +++ b/packages/graphql/tests/integration/issues/4223.int.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/4292.int.test.ts b/packages/graphql/tests/integration/issues/4292.int.test.ts index 9357bcee1d..4a77ee5683 100644 --- a/packages/graphql/tests/integration/issues/4292.int.test.ts +++ b/packages/graphql/tests/integration/issues/4292.int.test.ts @@ -44,8 +44,8 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${User.name} @node { - id: ID! @unique - email: String! @unique + id: ID! + email: String! name: String creator: [${Group.name}!]! @relationship(type: "CREATOR_OF", direction: OUT) admin: [${Admin.name}!]! @relationship(type: "IS_USER", direction: IN) @@ -55,7 +55,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${Group.name} @node { - id: ID! @id @unique + id: ID! @id name: String members: [${Person.name}!]! @relationship(type: "MEMBER_OF", direction: IN) creator: ${User.name}! @@ -95,7 +95,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } ] ) { - id: ID! @id @unique + id: ID! @id name: String! creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) @@ -132,7 +132,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${Admin.name} implements Invitee @node { - id: ID! @unique @id + id: ID! @id group: ${Group.name}! @relationship(type: "ADMIN_OF", direction: OUT) creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN) email: String! @@ -143,7 +143,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${Contributor.name} implements Invitee @node { - id: ID! @unique @id + id: ID! @id group: ${Group.name}! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN) email: String! diff --git a/packages/graphql/tests/integration/issues/440.int.test.ts b/packages/graphql/tests/integration/issues/440.int.test.ts index 17d78f97d9..2e28e32783 100644 --- a/packages/graphql/tests/integration/issues/440.int.test.ts +++ b/packages/graphql/tests/integration/issues/440.int.test.ts @@ -42,12 +42,12 @@ describe("https://github.com/neo4j/graphql/issues/440", () => { typeDefs = ` type ${Video} @node { - id: ID! @unique + id: ID! categories: [${Category}!]! @relationship(type: "IS_CATEGORIZED_AS", direction: OUT) } type ${Category} @node { - id: ID! @unique + id: ID! videos: [${Video}!]! @relationship(type: "IS_CATEGORIZED_AS", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/4429.int.test.ts b/packages/graphql/tests/integration/issues/4429.int.test.ts index 38ed9c06ef..a710d342d4 100644 --- a/packages/graphql/tests/integration/issues/4429.int.test.ts +++ b/packages/graphql/tests/integration/issues/4429.int.test.ts @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/4429", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/488.int.test.ts b/packages/graphql/tests/integration/issues/488.int.test.ts index 592c6f0db0..3a39442e96 100644 --- a/packages/graphql/tests/integration/issues/488.int.test.ts +++ b/packages/graphql/tests/integration/issues/488.int.test.ts @@ -45,17 +45,17 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { union Keyword = ${testEmoji.name} | ${testHashtag.name} | ${testText.name} type ${testEmoji.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } type ${testHashtag.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } type ${testText.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } `; diff --git a/packages/graphql/tests/integration/issues/5023.int.test.ts b/packages/graphql/tests/integration/issues/5023.int.test.ts index ad53291dbd..66e598a2d1 100644 --- a/packages/graphql/tests/integration/issues/5023.int.test.ts +++ b/packages/graphql/tests/integration/issues/5023.int.test.ts @@ -42,7 +42,7 @@ describe("https://github.com/neo4j/graphql/issues/5013", () => { id: String } type ${User} @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } diff --git a/packages/graphql/tests/integration/issues/5066.int.test.ts b/packages/graphql/tests/integration/issues/5066.int.test.ts index fb38f9d3ac..5b681ca961 100644 --- a/packages/graphql/tests/integration/issues/5066.int.test.ts +++ b/packages/graphql/tests/integration/issues/5066.int.test.ts @@ -42,7 +42,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { { where: { node: { createdBy: { id_EQ: "$jwt.sub" } } } }, ] ) { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private createdBy: ${User}! @relationship(type: "CREATED_ADMIN_GROUP", direction: IN) @settable(onCreate: true, onUpdate: false) @@ -53,10 +53,10 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, ] ) { - id: ID! @unique @settable(onCreate: true, onUpdate: false) + id: ID! @settable(onCreate: true, onUpdate: false) createdAt: DateTime! @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - username: String! @unique + username: String! blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) createdAdminGroups: [${AdminGroup}!]! @relationship(type: "CREATED_ADMIN_GROUP", direction: OUT) } @@ -66,7 +66,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { { where: { node: { from: { id_EQ: "$jwt.sub" } } } } ] ) { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) @@ -81,7 +81,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { { where: { node: { createdByConnection: { ${AdminGroup}: { node: { createdBy: { id_EQ: "$jwt.sub" } } } } } } }, ] ){ - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private createdBy: PartyCreator! @relationship(type: "CREATED_PARTY", direction: IN) @settable(onCreate: true, onUpdate: false) diff --git a/packages/graphql/tests/integration/issues/5080.int.test.ts b/packages/graphql/tests/integration/issues/5080.int.test.ts index 2ae64b77dc..e683813d8b 100644 --- a/packages/graphql/tests/integration/issues/5080.int.test.ts +++ b/packages/graphql/tests/integration/issues/5080.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { id: String } type ${User} @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } type ${Tenant} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { diff --git a/packages/graphql/tests/integration/issues/5270.int.test.ts b/packages/graphql/tests/integration/issues/5270.int.test.ts index 38f4b20c43..b625ef7314 100644 --- a/packages/graphql/tests/integration/issues/5270.int.test.ts +++ b/packages/graphql/tests/integration/issues/5270.int.test.ts @@ -38,7 +38,7 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, ] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { { where: { node: { from: { id_EQ: "$jwt.sub" } } } } ] ) { - id: ID! @id @unique + id: ID! @id from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) to: ${User}! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) } diff --git a/packages/graphql/tests/integration/issues/5378.int.test.ts b/packages/graphql/tests/integration/issues/5378.int.test.ts index 14a275842c..85f9dc977f 100644 --- a/packages/graphql/tests/integration/issues/5378.int.test.ts +++ b/packages/graphql/tests/integration/issues/5378.int.test.ts @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { type ${Space} @node @fulltext(indexes: [{ indexName: "fulltext_index_space_name_number", fields: ["Name", "Number"] }]) { - Id: ID! @id @unique + Id: ID! @id Number: String Name: String! } diff --git a/packages/graphql/tests/integration/issues/5497.int.test.ts b/packages/graphql/tests/integration/issues/5497.int.test.ts index c689aa35c6..db7a0fac8c 100644 --- a/packages/graphql/tests/integration/issues/5497.int.test.ts +++ b/packages/graphql/tests/integration/issues/5497.int.test.ts @@ -64,7 +64,7 @@ describe("https://github.com/neo4j/graphql/issues/5467", () => { } type ${File} @node { - id: ID! @unique + id: ID! category: ${Category} @relationship(type: "HAS_FILE", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/5515.int.test.ts b/packages/graphql/tests/integration/issues/5515.int.test.ts index 8e30f8ef32..1385b6aa15 100644 --- a/packages/graphql/tests/integration/issues/5515.int.test.ts +++ b/packages/graphql/tests/integration/issues/5515.int.test.ts @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { } type ${File} @node { - id: ID! @unique + id: ID! category: ${Category} @relationship(type: "HAS_FILE", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/556.int.test.ts b/packages/graphql/tests/integration/issues/556.int.test.ts index fd2fb099c7..b837c1be43 100644 --- a/packages/graphql/tests/integration/issues/556.int.test.ts +++ b/packages/graphql/tests/integration/issues/556.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/556 - Input Object type Articl } type ${Thing} @node { - id: ID! @id @unique + id: ID! @id } `; }); diff --git a/packages/graphql/tests/integration/issues/5635.int.test.ts b/packages/graphql/tests/integration/issues/5635.int.test.ts deleted file mode 100644 index 451c4bf3e8..0000000000 --- a/packages/graphql/tests/integration/issues/5635.int.test.ts +++ /dev/null @@ -1,107 +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 type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/5635", () => { - let Owner: UniqueType; - let MyNode: UniqueType; - - const testHelper = new TestHelper(); - - beforeEach(async () => { - Owner = testHelper.createUniqueType("Owner"); - MyNode = testHelper.createUniqueType("MyNode"); - - const typeDefs = /* GraphQL */ ` - type ${Owner} { - id: ID! @unique @id - owns: [${MyNode}!]! @relationship(type: "OWNS", direction: OUT) - } - - type ${MyNode} - @authorization( - validate: [ - { - operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] - where: { node: { owner: { id: "$jwt.sub" } } } - when: [AFTER] - } - ] - ) { - id: ID! @unique @id - name: String! - owner: ${Owner}! @relationship(type: "OWNS", direction: IN) - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("validation should applied correctly without causing cypher errors", async () => { - await testHelper.executeCypher(` - CREATE (c:${MyNode.name} {id: 'abc'})<-[:OWNS]-(o:${Owner.name} {id: 'abc'}) - `); - - const mutation = /* GraphQL */ ` - mutation { - ${MyNode.operations.create}(input: [{ - name: "Test", - owner: { - connectOrCreate: { - onCreate: { - node: { } - }, - where: { node: { id: "abc" } } - } - } - }]) { - ${MyNode.plural} { - id - name - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation, { - contextValue: { - token: testHelper.createBearerToken("secret", { sub: "abc" }), - }, - }); - expect(result.errors).toBeUndefined(); - expect(result.data as any).toEqual({ - [MyNode.operations.create]: { - [MyNode.plural]: [ - { - id: expect.any(String), - name: "Test", - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/5681.int.test.ts b/packages/graphql/tests/integration/issues/5681.int.test.ts index b4bd1b6700..0a6e783924 100644 --- a/packages/graphql/tests/integration/issues/5681.int.test.ts +++ b/packages/graphql/tests/integration/issues/5681.int.test.ts @@ -61,7 +61,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { operations: [UPDATE, DELETE, CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, SUBSCRIBE] jwt: { roles_INCLUDES: "overlord" } ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) diff --git a/packages/graphql/tests/integration/issues/619.int.test.ts b/packages/graphql/tests/integration/issues/619.int.test.ts deleted file mode 100644 index c3e2bbaf22..0000000000 --- a/packages/graphql/tests/integration/issues/619.int.test.ts +++ /dev/null @@ -1,78 +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 type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/619", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let FooIsARandomName: UniqueType; - let BarIsACoolName: UniqueType; - - beforeAll(async () => { - FooIsARandomName = testHelper.createUniqueType("FooIsARandomName"); - BarIsACoolName = testHelper.createUniqueType("BarIsACoolName"); - typeDefs = ` - type ${FooIsARandomName} @node { - id: ID @unique - Name: String - Age: Int - DrinksAt: ${BarIsACoolName} @relationship(type: "DRINKS_AT", direction: OUT) - } - - type ${BarIsACoolName} @node { - id: ID @unique - Adress: String - Customers: [${FooIsARandomName}!]! @relationship(type: "DRINKS_AT", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not throw 'input.map is not a function' error on one to many mutations", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${FooIsARandomName.operations.create}( - input: { - DrinksAt: { - connectOrCreate: { - where: { node: { id_EQ: "b50bd49b-9295-4749-9c0e-91d1e16df0b5" } } - onCreate: { node: { Adress: "Some Street" } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const gqlResult: any = await testHelper.executeGraphQL(mutation); - - expect(gqlResult.errors).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/issues/832.int.test.ts b/packages/graphql/tests/integration/issues/832.int.test.ts index 9eae7ac527..5ba02bdad3 100644 --- a/packages/graphql/tests/integration/issues/832.int.test.ts +++ b/packages/graphql/tests/integration/issues/832.int.test.ts @@ -38,17 +38,17 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { } type ${Person} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Place} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Interaction} @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/847.int.test.ts b/packages/graphql/tests/integration/issues/847.int.test.ts index 06e048695b..4eecd661ce 100644 --- a/packages/graphql/tests/integration/issues/847.int.test.ts +++ b/packages/graphql/tests/integration/issues/847.int.test.ts @@ -38,17 +38,17 @@ describe("https://github.com/neo4j/graphql/issues/847", () => { } type ${personType.name} implements Entity @node { - id : String! @unique + id : String! name : String! } type ${placeType.name} implements Entity @node { - id: String! @unique + id: String! location: Point! } type ${interactionType.name} @node { - id : ID! @id @unique + id : ID! @id kind : String! subjects : [Entity!]! @relationship(type: "ACTED_IN", direction: IN ) objects : [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/894.int.test.ts b/packages/graphql/tests/integration/issues/894.int.test.ts index 89c7aaf0ee..051c65359c 100644 --- a/packages/graphql/tests/integration/issues/894.int.test.ts +++ b/packages/graphql/tests/integration/issues/894.int.test.ts @@ -31,13 +31,13 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { const typeDefs = ` type ${testUser.name} @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! activeOrganization: ${testOrganization.name} @relationship(type: "ACTIVELY_MANAGING", direction: OUT) } type ${testOrganization.name} @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! } `; diff --git a/packages/graphql/tests/integration/issues/923.int.test.ts b/packages/graphql/tests/integration/issues/923.int.test.ts deleted file mode 100644 index c6cbb17256..0000000000 --- a/packages/graphql/tests/integration/issues/923.int.test.ts +++ /dev/null @@ -1,93 +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 { gql } from "graphql-tag"; -import type { Integer } from "neo4j-driver"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/923", () => { - const testHelper = new TestHelper(); - - let testBlogpost: UniqueType; - let testCategory: UniqueType; - - beforeAll(async () => { - testBlogpost = testHelper.createUniqueType("BlogPost"); - testCategory = testHelper.createUniqueType("Category"); - // driver = await neo4j.getDriver(); - - const typeDefs = gql` - type ${testBlogpost.name} @fulltext(indexes: [{ name: "BlogTitle", fields: ["title"] }]) @node { - title: String! - slug: String! @unique - } - type ${testCategory.name} @node { - name: String! @unique - blogs: [${testBlogpost.name}!]! @relationship(type: "IN_CATEGORY", direction: IN) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should query nested connection", async () => { - const query = /* GraphQL */ ` - mutation { - ${testCategory.operations.create}( - input: [ - { - blogs: { - connectOrCreate: [ - { - where: { node: { slug_EQ: "dsa" } } - onCreate: { node: { title: "mytitle", slug: "myslug" } } - } - ] - } - name: "myname" - } - ] - ) { - info { - nodesCreated - } - } - } - `; - - const result = await testHelper.executeGraphQL(query, { - contextValue: { - jwt: { - sub: "test", - }, - }, - }); - expect(result.errors).toBeUndefined(); - - const blogPostCount = await testHelper.executeCypher(` - MATCH (m:${testBlogpost.name} { slug: "myslug" }) - RETURN COUNT(m) as count - `); - expect((blogPostCount.records[0]?.toObject().count as Integer).toNumber()).toBe(1); - }); -}); diff --git a/packages/graphql/tests/integration/issues/976.int.test.ts b/packages/graphql/tests/integration/issues/976.int.test.ts deleted file mode 100644 index 3bae351e71..0000000000 --- a/packages/graphql/tests/integration/issues/976.int.test.ts +++ /dev/null @@ -1,153 +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 { type Integer } from "neo4j-driver"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/976", () => { - let testBibliographicReference: UniqueType; - let testConcept: UniqueType; - const testHelper = new TestHelper(); - - beforeAll(async () => { - testBibliographicReference = testHelper.createUniqueType("BibliographicReference"); - testConcept = testHelper.createUniqueType("Concept"); - - const typeDefs = ` - type ${testBibliographicReference.name} @node(labels: ["${testBibliographicReference.name}", "Resource"]){ - iri: ID! @unique @alias(property: "uri") - prefLabel: [String!] - isInPublication: [${testConcept.name}!]! @relationship(type: "isInPublication", direction: OUT) - } - - type ${testConcept.name} @node(labels: ["${testConcept.name}", "Resource"]){ - iri: ID! @unique @alias(property: "uri") - prefLabel: [String!]! - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should query nested connection", async () => { - const createBibRefQuery = /* GraphQL */ ` - mutation { - ${testBibliographicReference.operations.create}( - input: { - iri: "urn:myiri2" - prefLabel: "Initial label" - isInPublication: { - create: { node: { iri: "new-e", prefLabel: "stuff" } } - } - } - ) { - ${testBibliographicReference.plural} { - iri - prefLabel - isInPublication { - iri - prefLabel - } - } - } - } - `; - const updateBibRefQuery = /* GraphQL */ ` - mutation { - ${testConcept.operations.delete}(where: { iri_EQ: "new-e" }) { - nodesDeleted - } - - ${testBibliographicReference.operations.update}( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel: "Updated Label" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - ${testBibliographicReference.plural} { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - const createBibRefResult = await testHelper.executeGraphQL(createBibRefQuery); - expect(createBibRefResult.errors).toBeUndefined(); - - const bibRefRes = await testHelper.executeCypher(` - MATCH (bibRef:${testBibliographicReference.name})-[r:isInPublication]->(concept:${testConcept.name}) RETURN bibRef.uri as bibRefUri, concept.uri as conceptUri - `); - - expect(bibRefRes.records).toHaveLength(1); - expect(bibRefRes.records[0]?.toObject().bibRefUri as string).toBe("urn:myiri2"); - expect(bibRefRes.records[0]?.toObject().conceptUri as string).toBe("new-e"); - - const updateBibRefResult = await testHelper.executeGraphQL(updateBibRefQuery); - expect(updateBibRefResult.errors).toBeUndefined(); - expect(updateBibRefResult?.data).toEqual({ - [testConcept.operations.delete]: { - nodesDeleted: 1, - }, - [testBibliographicReference.operations.update]: { - [testBibliographicReference.plural]: [ - { - iri: "urn:myiri2", - prefLabel: ["Updated Label"], - isInPublication: [ - { - iri: "new-f", - prefLabel: ["pub"], - }, - ], - }, - ], - }, - }); - - const conceptCount = await testHelper.executeCypher(` - MATCH (bibRef:${testBibliographicReference.name})-[r:isInPublication]->(concept:${testConcept.name}) RETURN bibRef.uri as bibRefUri, COUNT(concept) as conceptCount - `); - - expect(conceptCount.records).toHaveLength(1); - expect(conceptCount.records[0]?.toObject().bibRefUri as string).toBe("urn:myiri2"); - expect((conceptCount.records[0]?.toObject().conceptCount as Integer).toNumber()).toBe(2); - }); -}); diff --git a/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts b/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts index 16c6073563..da9c634857 100644 --- a/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts +++ b/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts @@ -38,17 +38,17 @@ describe("Projecting interface relationships following create of multiple nodes" } type ${Person} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Place} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Interaction} @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts b/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts deleted file mode 100644 index 896f200614..0000000000 --- a/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts +++ /dev/null @@ -1,52 +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 { Neo4jGraphQL } from "../../../src/classes"; - -describe("deprecated @unique warnings", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); - - afterEach(() => { - warn.mockReset(); - }); - - test("warning on unique usage", async () => { - const typeDefs = /* GraphQL */ ` - type User @node { - id: ID! @unique - firstName: String! - } - - type Movie @node { - id: ID! @unique - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefs, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).toHaveBeenCalledWith("Future library versions will not support @unique directive."); - }); -}); diff --git a/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts b/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts index 2039aae81a..95013c4466 100644 --- a/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts +++ b/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts @@ -1227,124 +1227,6 @@ describe("Relationship properties - connect with and without `overwrite` argumen }); }); - describe("Relationships type 1:n with @unique", () => { - let movieTitle: string; - let actorName: string; - let screenTime: number; - let screenTimeUpdate: number; - - beforeEach(async () => { - typeActor = testHelper.createUniqueType("Actor"); - typeMovie = testHelper.createUniqueType("Movie"); - - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - actors: ${typeActor.name}! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${typeActor.name} @node { - name: String! - id: Int! @unique - movies: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - movieTitle = "Movie 1"; - actorName = "Actor 1"; - screenTime = 123; - screenTimeUpdate = 134; - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:ACTED_IN { screenTime: $screenTime }]->(m) - `, - { movieTitle, actorName, screenTime } - ); - }); - - // update + update + connectOrCreate - test("update with connectOrCreate is a no-op when relationship field has cardinality 1, overwrite not an option", async () => { - const actorId = 1; - - await testHelper.executeCypher( - ` - MATCH (actor:${typeActor.name} {name: $actorName}) - SET actor.id=$actorId - `, - { actorName, actorId } - ); - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!, $actorId: Int!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connectOrCreate: { - where: { node: { id_EQ: $actorId } }, - onCreate: { edge: { screenTime: $screenTime }, node: { name: $actorName, id: $actorId } }, - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - id - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName, id: $actorId}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, actorId }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime, - actorName, - actorId, - }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - actorId, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - }); - describe("Relationships of type n:n", () => { let movieTitle: string; let movieOtherTitle: string; diff --git a/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts b/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts deleted file mode 100644 index c5b568f7fc..0000000000 --- a/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts +++ /dev/null @@ -1,115 +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 type { DocumentNode } from "graphql"; -import { gql } from "graphql-tag"; -import type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("Create -> ConnectOrCreate", () => { - const testHelper = new TestHelper({ cdc: true }); - let typeDefs: DocumentNode; - let cdcEnabled: boolean; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeAll(async () => { - cdcEnabled = await testHelper.assertCDCEnabled(); - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - }); - - beforeEach(async () => { - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { subscriptions: await testHelper.getSubscriptionEngine() }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - if (!cdcEnabled) { - console.log("CDC NOT AVAILABLE - SKIPPING"); - return; - } - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", id: 5 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitleAndId = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0]?.toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0]?.toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - }); -}); diff --git a/packages/graphql/tests/integration/update.int.test.ts b/packages/graphql/tests/integration/update.int.test.ts index fd45bd0538..b27895a630 100644 --- a/packages/graphql/tests/integration/update.int.test.ts +++ b/packages/graphql/tests/integration/update.int.test.ts @@ -139,14 +139,14 @@ describe("update", () => { const typeDefs = gql` type ${Movie} implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } type ${Series} implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } diff --git a/packages/graphql/tests/schema/connect-or-create-id.test.ts b/packages/graphql/tests/schema/connect-or-create-id.test.ts deleted file mode 100644 index 98af1a6d06..0000000000 --- a/packages/graphql/tests/schema/connect-or-create-id.test.ts +++ /dev/null @@ -1,1068 +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 { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("connect or create with id", () => { - test("connect or create with id", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - id: ID! @id @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID - id_MAX_GT: ID - id_MAX_GTE: ID - id_MAX_LT: ID - id_MAX_LTE: ID - id_MIN_EQUAL: ID - id_MIN_GT: ID - id_MIN_GTE: ID - id_MIN_LT: ID - id_MIN_LTE: ID - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - id: ID! - title: String! - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input MovieUpdateInput { - title: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("connect or create with non-autogenerated id", async () => { - const typeDefs = gql` - type Post @node { - id: ID! @unique - content: String! - creator: User! @relationship(type: "HAS_POST", direction: IN) - createdAt: DateTime! - } - - type User @node { - id: ID! @id @unique - name: String! - posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreatePostsMutationResponse { - info: CreateInfo! - posts: [Post!]! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" - scalar DateTime - - type DateTimeAggregateSelection { - max: DateTime - min: DateTime - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Mutation { - createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Post { - content: String! - createdAt: DateTime! - creator(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - creatorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserCreatorAggregationSelection - creatorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostCreatorConnectionSort!], where: PostCreatorConnectionWhere): PostCreatorConnection! - id: ID! - } - - type PostAggregateSelection { - content: StringAggregateSelection! - count: Int! - createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! - } - - input PostConnectInput { - creator: PostCreatorConnectFieldInput - } - - input PostConnectOrCreateWhere { - node: PostUniqueWhere! - } - - input PostConnectWhere { - node: PostWhere! - } - - input PostCreateInput { - content: String! - createdAt: DateTime! - creator: PostCreatorFieldInput - id: ID! - } - - input PostCreatorAggregateInput { - AND: [PostCreatorAggregateInput!] - NOT: PostCreatorAggregateInput - OR: [PostCreatorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: PostCreatorNodeAggregationWhereInput - } - - input PostCreatorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: UserConnectWhere - } - - input PostCreatorConnectOrCreateFieldInput { - onCreate: PostCreatorConnectOrCreateFieldInputOnCreate! - where: UserConnectOrCreateWhere! - } - - input PostCreatorConnectOrCreateFieldInputOnCreate { - node: UserOnCreateInput! - } - - type PostCreatorConnection { - edges: [PostCreatorRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input PostCreatorConnectionSort { - node: UserSort - } - - input PostCreatorConnectionWhere { - AND: [PostCreatorConnectionWhere!] - NOT: PostCreatorConnectionWhere - OR: [PostCreatorConnectionWhere!] - node: UserWhere - } - - input PostCreatorCreateFieldInput { - node: UserCreateInput! - } - - input PostCreatorDeleteFieldInput { - delete: UserDeleteInput - where: PostCreatorConnectionWhere - } - - input PostCreatorDisconnectFieldInput { - disconnect: UserDisconnectInput - where: PostCreatorConnectionWhere - } - - input PostCreatorFieldInput { - connect: PostCreatorConnectFieldInput - connectOrCreate: PostCreatorConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: PostCreatorCreateFieldInput - } - - input PostCreatorNodeAggregationWhereInput { - AND: [PostCreatorNodeAggregationWhereInput!] - NOT: PostCreatorNodeAggregationWhereInput - OR: [PostCreatorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID - id_MAX_GT: ID - id_MAX_GTE: ID - id_MAX_LT: ID - id_MAX_LTE: ID - id_MIN_EQUAL: ID - id_MIN_GT: ID - id_MIN_GTE: ID - id_MIN_LT: ID - id_MIN_LTE: ID - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type PostCreatorRelationship { - cursor: String! - node: User! - } - - input PostCreatorUpdateConnectionInput { - node: UserUpdateInput - } - - input PostCreatorUpdateFieldInput { - connect: PostCreatorConnectFieldInput - connectOrCreate: PostCreatorConnectOrCreateFieldInput - create: PostCreatorCreateFieldInput - delete: PostCreatorDeleteFieldInput - disconnect: PostCreatorDisconnectFieldInput - update: PostCreatorUpdateConnectionInput - where: PostCreatorConnectionWhere - } - - input PostDeleteInput { - creator: PostCreatorDeleteFieldInput - } - - input PostDisconnectInput { - creator: PostCreatorDisconnectFieldInput - } - - type PostEdge { - cursor: String! - node: Post! - } - - input PostOnCreateInput { - content: String! - createdAt: DateTime! - id: ID! - } - - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - - \\"\\"\\" - Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. - \\"\\"\\" - input PostSort { - content: SortDirection - createdAt: SortDirection - id: SortDirection - } - - input PostUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input PostUpdateInput { - content: String - createdAt: DateTime - creator: PostCreatorUpdateFieldInput - id: ID - } - - type PostUserCreatorAggregationSelection { - count: Int! - node: PostUserCreatorNodeAggregateSelection - } - - type PostUserCreatorNodeAggregateSelection { - id: IDAggregateSelection! - name: StringAggregateSelection! - } - - input PostWhere { - AND: [PostWhere!] - NOT: PostWhere - OR: [PostWhere!] - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String!] - content_STARTS_WITH: String - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime!] - createdAt_LT: DateTime - createdAt_LTE: DateTime - creator: UserWhere - creatorAggregate: PostCreatorAggregateInput - creatorConnection: PostCreatorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - } - - type PostsConnection { - edges: [PostEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(where: PostWhere): PostAggregateSelection! - postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdatePostsMutationResponse { - info: UpdateInfo! - posts: [Post!]! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - id: ID! - name: String! - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): UserPostPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! - } - - type UserAggregateSelection { - count: Int! - id: IDAggregateSelection! - name: StringAggregateSelection! - } - - input UserConnectInput { - posts: [UserPostsConnectFieldInput!] - } - - input UserConnectOrCreateWhere { - node: UserUniqueWhere! - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - name: String! - posts: UserPostsFieldInput - } - - input UserDeleteInput { - posts: [UserPostsDeleteFieldInput!] - } - - input UserDisconnectInput { - posts: [UserPostsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOnCreateInput { - name: String! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - type UserPostPostsAggregationSelection { - count: Int! - node: UserPostPostsNodeAggregateSelection - } - - type UserPostPostsNodeAggregateSelection { - content: StringAggregateSelection! - createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! - } - - input UserPostsAggregateInput { - AND: [UserPostsAggregateInput!] - NOT: UserPostsAggregateInput - OR: [UserPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserPostsNodeAggregationWhereInput - } - - input UserPostsConnectFieldInput { - connect: [PostConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: PostConnectWhere - } - - input UserPostsConnectOrCreateFieldInput { - onCreate: UserPostsConnectOrCreateFieldInputOnCreate! - where: PostConnectOrCreateWhere! - } - - input UserPostsConnectOrCreateFieldInputOnCreate { - node: PostOnCreateInput! - } - - type UserPostsConnection { - edges: [UserPostsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserPostsConnectionSort { - node: PostSort - } - - input UserPostsConnectionWhere { - AND: [UserPostsConnectionWhere!] - NOT: UserPostsConnectionWhere - OR: [UserPostsConnectionWhere!] - node: PostWhere - } - - input UserPostsCreateFieldInput { - node: PostCreateInput! - } - - input UserPostsDeleteFieldInput { - delete: PostDeleteInput - where: UserPostsConnectionWhere - } - - input UserPostsDisconnectFieldInput { - disconnect: PostDisconnectInput - where: UserPostsConnectionWhere - } - - input UserPostsFieldInput { - connect: [UserPostsConnectFieldInput!] - connectOrCreate: [UserPostsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [UserPostsCreateFieldInput!] - } - - input UserPostsNodeAggregationWhereInput { - AND: [UserPostsNodeAggregationWhereInput!] - NOT: UserPostsNodeAggregationWhereInput - OR: [UserPostsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - createdAt_MAX_EQUAL: DateTime - createdAt_MAX_GT: DateTime - createdAt_MAX_GTE: DateTime - createdAt_MAX_LT: DateTime - createdAt_MAX_LTE: DateTime - createdAt_MIN_EQUAL: DateTime - createdAt_MIN_GT: DateTime - createdAt_MIN_GTE: DateTime - createdAt_MIN_LT: DateTime - createdAt_MIN_LTE: DateTime - id_MAX_EQUAL: ID - id_MAX_GT: ID - id_MAX_GTE: ID - id_MAX_LT: ID - id_MAX_LTE: ID - id_MIN_EQUAL: ID - id_MIN_GT: ID - id_MIN_GTE: ID - id_MIN_LT: ID - id_MIN_LTE: ID - } - - type UserPostsRelationship { - cursor: String! - node: Post! - } - - input UserPostsUpdateConnectionInput { - node: PostUpdateInput - } - - input UserPostsUpdateFieldInput { - connect: [UserPostsConnectFieldInput!] - connectOrCreate: [UserPostsConnectOrCreateFieldInput!] - create: [UserPostsCreateFieldInput!] - delete: [UserPostsDeleteFieldInput!] - disconnect: [UserPostsDisconnectFieldInput!] - update: UserPostsUpdateConnectionInput - where: UserPostsConnectionWhere - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - id: SortDirection - name: SortDirection - } - - input UserUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input UserUpdateInput { - name: String - posts: [UserPostsUpdateFieldInput!] - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - postsAggregate: UserPostsAggregateInput - \\"\\"\\" - Return Users where all of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_ALL: UserPostsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_NONE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SINGLE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SOME: UserPostsConnectionWhere - \\"\\"\\"Return Users where all of the related Posts match this filter\\"\\"\\" - posts_ALL: PostWhere - \\"\\"\\"Return Users where none of the related Posts match this filter\\"\\"\\" - posts_NONE: PostWhere - \\"\\"\\"Return Users where one of the related Posts match this filter\\"\\"\\" - posts_SINGLE: PostWhere - \\"\\"\\"Return Users where some of the related Posts match this filter\\"\\"\\" - posts_SOME: PostWhere - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connect-or-create-unions.test.ts b/packages/graphql/tests/schema/connect-or-create-unions.test.ts deleted file mode 100644 index 7084535d18..0000000000 --- a/packages/graphql/tests/schema/connect-or-create-unions.test.ts +++ /dev/null @@ -1,617 +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 { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("Connect Or Create", () => { - test("With Unions", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Series @node { - title: String! - isan: String! @unique - } - - union Production = Movie | Series - - type ActedIn @relationshipProperties { - screenTime: Int! - } - - type Actor @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.actedIn - \\"\\"\\" - type ActedIn { - screenTime: Int! - } - - input ActedInCreateInput { - screenTime: Int! - } - - input ActedInSort { - screenTime: SortDirection - } - - input ActedInUpdateInput { - screenTime: Int - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - } - - type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! - name: String! - } - - type ActorActedInConnection { - edges: [ActorActedInRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorActedInConnectionSort { - edge: ActedInSort - } - - input ActorActedInConnectionWhere { - Movie: ActorActedInMovieConnectionWhere - Series: ActorActedInSeriesConnectionWhere - } - - input ActorActedInCreateInput { - Movie: ActorActedInMovieFieldInput - Series: ActorActedInSeriesFieldInput - } - - input ActorActedInDeleteInput { - Movie: [ActorActedInMovieDeleteFieldInput!] - Series: [ActorActedInSeriesDeleteFieldInput!] - } - - input ActorActedInMovieConnectFieldInput { - edge: ActedInCreateInput! - where: MovieConnectWhere - } - - input ActorActedInMovieConnectOrCreateFieldInput { - onCreate: ActorActedInMovieConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorActedInMovieConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - - input ActorActedInMovieConnectionWhere { - AND: [ActorActedInMovieConnectionWhere!] - NOT: ActorActedInMovieConnectionWhere - OR: [ActorActedInMovieConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorActedInMovieCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorActedInMovieDeleteFieldInput { - where: ActorActedInMovieConnectionWhere - } - - input ActorActedInMovieDisconnectFieldInput { - where: ActorActedInMovieConnectionWhere - } - - input ActorActedInMovieFieldInput { - connect: [ActorActedInMovieConnectFieldInput!] - connectOrCreate: [ActorActedInMovieConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorActedInMovieCreateFieldInput!] - } - - input ActorActedInMovieUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorActedInMovieUpdateFieldInput { - connect: [ActorActedInMovieConnectFieldInput!] - connectOrCreate: [ActorActedInMovieConnectOrCreateFieldInput!] - create: [ActorActedInMovieCreateFieldInput!] - delete: [ActorActedInMovieDeleteFieldInput!] - disconnect: [ActorActedInMovieDisconnectFieldInput!] - update: ActorActedInMovieUpdateConnectionInput - where: ActorActedInMovieConnectionWhere - } - - type ActorActedInRelationship { - cursor: String! - node: Production! - properties: ActedIn! - } - - input ActorActedInSeriesConnectFieldInput { - edge: ActedInCreateInput! - where: SeriesConnectWhere - } - - input ActorActedInSeriesConnectOrCreateFieldInput { - onCreate: ActorActedInSeriesConnectOrCreateFieldInputOnCreate! - where: SeriesConnectOrCreateWhere! - } - - input ActorActedInSeriesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: SeriesOnCreateInput! - } - - input ActorActedInSeriesConnectionWhere { - AND: [ActorActedInSeriesConnectionWhere!] - NOT: ActorActedInSeriesConnectionWhere - OR: [ActorActedInSeriesConnectionWhere!] - edge: ActedInWhere - node: SeriesWhere - } - - input ActorActedInSeriesCreateFieldInput { - edge: ActedInCreateInput! - node: SeriesCreateInput! - } - - input ActorActedInSeriesDeleteFieldInput { - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInSeriesDisconnectFieldInput { - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInSeriesFieldInput { - connect: [ActorActedInSeriesConnectFieldInput!] - connectOrCreate: [ActorActedInSeriesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorActedInSeriesCreateFieldInput!] - } - - input ActorActedInSeriesUpdateConnectionInput { - edge: ActedInUpdateInput - node: SeriesUpdateInput - } - - input ActorActedInSeriesUpdateFieldInput { - connect: [ActorActedInSeriesConnectFieldInput!] - connectOrCreate: [ActorActedInSeriesConnectOrCreateFieldInput!] - create: [ActorActedInSeriesCreateFieldInput!] - delete: [ActorActedInSeriesDeleteFieldInput!] - disconnect: [ActorActedInSeriesDisconnectFieldInput!] - update: ActorActedInSeriesUpdateConnectionInput - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInUpdateInput { - Movie: [ActorActedInMovieUpdateFieldInput!] - Series: [ActorActedInSeriesUpdateFieldInput!] - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - actedIn: ActorActedInCreateInput - name: String! - } - - input ActorDeleteInput { - actedIn: ActorActedInDeleteInput - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - actedIn: ActorActedInUpdateInput - name: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - \\"\\"\\" - Return Actors where all of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere - \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere - \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere - \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere - \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String - title: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - deleteSeries(where: SeriesWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - union Production = Movie | Series - - input ProductionWhere { - Movie: MovieWhere - Series: SeriesWhere - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - type Series { - isan: String! - title: String! - } - - type SeriesAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input SeriesConnectOrCreateWhere { - node: SeriesUniqueWhere! - } - - input SeriesConnectWhere { - node: SeriesWhere! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - isan: String! - title: String! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - input SeriesOnCreateInput { - isan: String! - title: String! - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - isan: SortDirection - title: SortDirection - } - - input SeriesUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input SeriesUpdateInput { - isan: String - title: String - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connect-or-create.test.ts b/packages/graphql/tests/schema/connect-or-create.test.ts deleted file mode 100644 index acaf867ce8..0000000000 --- a/packages/graphql/tests/schema/connect-or-create.test.ts +++ /dev/null @@ -1,963 +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 { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("Connect Or Create", () => { - test("Connect Or Create", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String - title: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("Connect Or Create with relation properties", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int! - characterName: String - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - \\"\\"\\" - type ActedIn { - characterName: String - screentime: Int! - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - characterName_AVERAGE_LENGTH_EQUAL: Float - characterName_AVERAGE_LENGTH_GT: Float - characterName_AVERAGE_LENGTH_GTE: Float - characterName_AVERAGE_LENGTH_LT: Float - characterName_AVERAGE_LENGTH_LTE: Float - characterName_LONGEST_LENGTH_EQUAL: Int - characterName_LONGEST_LENGTH_GT: Int - characterName_LONGEST_LENGTH_GTE: Int - characterName_LONGEST_LENGTH_LT: Int - characterName_LONGEST_LENGTH_LTE: Int - characterName_SHORTEST_LENGTH_EQUAL: Int - characterName_SHORTEST_LENGTH_GT: Int - characterName_SHORTEST_LENGTH_GTE: Int - characterName_SHORTEST_LENGTH_LT: Int - characterName_SHORTEST_LENGTH_LTE: Int - screentime_AVERAGE_EQUAL: Float - screentime_AVERAGE_GT: Float - screentime_AVERAGE_GTE: Float - screentime_AVERAGE_LT: Float - screentime_AVERAGE_LTE: Float - screentime_MAX_EQUAL: Int - screentime_MAX_GT: Int - screentime_MAX_GTE: Int - screentime_MAX_LT: Int - screentime_MAX_LTE: Int - screentime_MIN_EQUAL: Int - screentime_MIN_GT: Int - screentime_MIN_GTE: Int - screentime_MIN_LT: Int - screentime_MIN_LTE: Int - screentime_SUM_EQUAL: Int - screentime_SUM_GT: Int - screentime_SUM_GTE: Int - screentime_SUM_LT: Int - screentime_SUM_LTE: Int - } - - input ActedInCreateInput { - characterName: String - screentime: Int! - } - - input ActedInSort { - characterName: SortDirection - screentime: SortDirection - } - - input ActedInUpdateInput { - characterName: String - screentime: Int - screentime_DECREMENT: Int - screentime_INCREMENT: Int - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - characterName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - characterName_CONTAINS: String - characterName_ENDS_WITH: String - characterName_EQ: String - characterName_IN: [String] - characterName_STARTS_WITH: String - screentime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screentime_EQ: Int - screentime_GT: Int - screentime_GTE: Int - screentime_IN: [Int!] - screentime_LT: Int - screentime_LTE: Int - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - characterName: StringAggregateSelection! - screentime: IntAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String - title: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connections/interfaces.test.ts b/packages/graphql/tests/schema/connections/interfaces.test.ts index 00b7300366..72c1d0833c 100644 --- a/packages/graphql/tests/schema/connections/interfaces.test.ts +++ b/packages/graphql/tests/schema/connections/interfaces.test.ts @@ -27,14 +27,14 @@ describe("Connection with interfaces", () => { const typeDefs = gql` type Movie implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } type Series implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } diff --git a/packages/graphql/tests/schema/directives/autogenerate.test.ts b/packages/graphql/tests/schema/directives/autogenerate.test.ts index 492ab92266..cf5759d131 100644 --- a/packages/graphql/tests/schema/directives/autogenerate.test.ts +++ b/packages/graphql/tests/schema/directives/autogenerate.test.ts @@ -26,7 +26,7 @@ describe("Autogenerate", () => { test("Simple", async () => { const typeDefs = gql` type Movie @node { - id: ID! @id @unique + id: ID! @id name: String! } `; diff --git a/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts b/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts index d3b730baee..ae2fe93e72 100644 --- a/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts +++ b/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts @@ -2325,7 +2325,7 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with nested operation CONNECT_OR_CREATE specified", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -2395,15 +2395,6 @@ describe("Relationship nested operations", () => { node: MovieActorsNodeAggregationWhereInput } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - node: PersonOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! @@ -2421,10 +2412,6 @@ describe("Relationship nested operations", () => { node: PersonWhere } - input MovieActorsFieldInput { - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - input MovieActorsNodeAggregationWhereInput { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput @@ -2461,18 +2448,12 @@ describe("Relationship nested operations", () => { node: Person! } - input MovieActorsUpdateFieldInput { - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] - where: MovieActorsConnectionWhere - } - type MovieAggregateSelection { count: Int! id: IDAggregateSelection! } input MovieCreateInput { - actors: MovieActorsFieldInput id: ID } @@ -2508,7 +2489,6 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] id: ID } @@ -2589,10 +2569,6 @@ describe("Relationship nested operations", () => { name: StringAggregateSelection! } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonCreateInput { name: String } @@ -2602,10 +2578,6 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOnCreateInput { - name: String - } - input PersonOptions { limit: Int offset: Int @@ -2623,11 +2595,6 @@ describe("Relationship nested operations", () => { name: SortDirection } - input PersonUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonUpdateInput { name: String } @@ -6379,12 +6346,12 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with nested operation CONNECT_OR_CREATE specified", async () => { const typeDefs = gql` type PersonOne @node { - id: ID! @id @unique + id: ID! @id name: String } type PersonTwo @node { - id: ID! @id @unique + id: ID! @id nameTwo: String } @@ -6458,20 +6425,6 @@ describe("Relationship nested operations", () => { PersonTwo: MovieActorsPersonTwoConnectionWhere } - input MovieActorsCreateInput { - PersonOne: MovieActorsPersonOneFieldInput - PersonTwo: MovieActorsPersonTwoFieldInput - } - - input MovieActorsPersonOneConnectOrCreateFieldInput { - onCreate: MovieActorsPersonOneConnectOrCreateFieldInputOnCreate! - where: PersonOneConnectOrCreateWhere! - } - - input MovieActorsPersonOneConnectOrCreateFieldInputOnCreate { - node: PersonOneOnCreateInput! - } - input MovieActorsPersonOneConnectionWhere { AND: [MovieActorsPersonOneConnectionWhere!] NOT: MovieActorsPersonOneConnectionWhere @@ -6479,24 +6432,6 @@ describe("Relationship nested operations", () => { node: PersonOneWhere } - input MovieActorsPersonOneFieldInput { - connectOrCreate: [MovieActorsPersonOneConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - - input MovieActorsPersonOneUpdateFieldInput { - connectOrCreate: [MovieActorsPersonOneConnectOrCreateFieldInput!] - where: MovieActorsPersonOneConnectionWhere - } - - input MovieActorsPersonTwoConnectOrCreateFieldInput { - onCreate: MovieActorsPersonTwoConnectOrCreateFieldInputOnCreate! - where: PersonTwoConnectOrCreateWhere! - } - - input MovieActorsPersonTwoConnectOrCreateFieldInputOnCreate { - node: PersonTwoOnCreateInput! - } - input MovieActorsPersonTwoConnectionWhere { AND: [MovieActorsPersonTwoConnectionWhere!] NOT: MovieActorsPersonTwoConnectionWhere @@ -6504,32 +6439,17 @@ describe("Relationship nested operations", () => { node: PersonTwoWhere } - input MovieActorsPersonTwoFieldInput { - connectOrCreate: [MovieActorsPersonTwoConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - - input MovieActorsPersonTwoUpdateFieldInput { - connectOrCreate: [MovieActorsPersonTwoConnectOrCreateFieldInput!] - where: MovieActorsPersonTwoConnectionWhere - } - type MovieActorsRelationship { cursor: String! node: Person! } - input MovieActorsUpdateInput { - PersonOne: [MovieActorsPersonOneUpdateFieldInput!] - PersonTwo: [MovieActorsPersonTwoUpdateFieldInput!] - } - type MovieAggregateSelection { count: Int! id: IDAggregateSelection! } input MovieCreateInput { - actors: MovieActorsCreateInput id: ID } @@ -6555,7 +6475,6 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - actors: MovieActorsUpdateInput id: ID } @@ -6634,10 +6553,6 @@ describe("Relationship nested operations", () => { name: StringAggregateSelection! } - input PersonOneConnectOrCreateWhere { - node: PersonOneUniqueWhere! - } - input PersonOneCreateInput { name: String } @@ -6647,10 +6562,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOnCreateInput { - name: String - } - input PersonOneOptions { limit: Int offset: Int @@ -6668,11 +6579,6 @@ describe("Relationship nested operations", () => { name: SortDirection } - input PersonOneUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonOneUpdateInput { name: String } @@ -6712,10 +6618,6 @@ describe("Relationship nested operations", () => { nameTwo: StringAggregateSelection! } - input PersonTwoConnectOrCreateWhere { - node: PersonTwoUniqueWhere! - } - input PersonTwoCreateInput { nameTwo: String } @@ -6725,10 +6627,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOnCreateInput { - nameTwo: String - } - input PersonTwoOptions { limit: Int offset: Int @@ -6746,11 +6644,6 @@ describe("Relationship nested operations", () => { nameTwo: SortDirection } - input PersonTwoUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonTwoUpdateInput { nameTwo: String } diff --git a/packages/graphql/tests/schema/issues/1182.test.ts b/packages/graphql/tests/schema/issues/1182.test.ts index c8d775748b..3e09c35993 100644 --- a/packages/graphql/tests/schema/issues/1182.test.ts +++ b/packages/graphql/tests/schema/issues/1182.test.ts @@ -26,13 +26,13 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { test("DateTime and Point fields are included in onCreate", async () => { const typeDefs = gql` type Movie @node { - id: ID! @id @unique + id: ID! @id title: String! actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } type Actor @node { - id: ID! @id @unique + id: ID! @id name: String! dob: DateTime! homeAddress: Point! @@ -61,10 +61,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { name: StringAggregateSelection! } - input ActorConnectOrCreateWhere { - node: ActorUniqueWhere! - } - input ActorConnectWhere { node: ActorWhere! } @@ -80,12 +76,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { node: Actor! } - input ActorOnCreateInput { - dob: DateTime! - homeAddress: PointInput! - name: String! - } - input ActorOptions { limit: Int offset: Int @@ -105,11 +95,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { name: SortDirection } - input ActorUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input ActorUpdateInput { dob: DateTime homeAddress: PointInput @@ -234,15 +219,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { where: ActorConnectWhere } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - node: ActorOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! @@ -274,7 +250,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { input MovieActorsFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieActorsCreateFieldInput!] } @@ -330,7 +305,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { input MovieActorsUpdateFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] create: [MovieActorsCreateFieldInput!] delete: [MovieActorsDeleteFieldInput!] disconnect: [MovieActorsDisconnectFieldInput!] diff --git a/packages/graphql/tests/schema/issues/200.test.ts b/packages/graphql/tests/schema/issues/200.test.ts index f4b531d49f..d3bec17a9e 100644 --- a/packages/graphql/tests/schema/issues/200.test.ts +++ b/packages/graphql/tests/schema/issues/200.test.ts @@ -26,7 +26,7 @@ describe("200", () => { test("Preserve schema array non null", async () => { const typeDefs = gql` type Category @node { - categoryId: ID! @id @unique + categoryId: ID! @id name: String! description: String! @default(value: "") exampleImageLocations: [String!] diff --git a/packages/graphql/tests/schema/issues/2377.test.ts b/packages/graphql/tests/schema/issues/2377.test.ts index afa268fcf8..f2b88926f4 100644 --- a/packages/graphql/tests/schema/issues/2377.test.ts +++ b/packages/graphql/tests/schema/issues/2377.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { } type Resource implements ResourceEntity @node { - id: ID! @unique + id: ID! name: String type: ResourceType! externalIds: [ID!] @@ -177,10 +177,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { containedBy: [ResourceContainedByConnectFieldInput!] } - input ResourceConnectOrCreateWhere { - node: ResourceUniqueWhere! - } - input ResourceConnectWhere { node: ResourceWhere! } @@ -207,15 +203,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { where: ResourceConnectWhere } - input ResourceContainedByConnectOrCreateFieldInput { - onCreate: ResourceContainedByConnectOrCreateFieldInputOnCreate! - where: ResourceConnectOrCreateWhere! - } - - input ResourceContainedByConnectOrCreateFieldInputOnCreate { - node: ResourceOnCreateInput! - } - type ResourceContainedByConnection { edges: [ResourceContainedByRelationship!]! pageInfo: PageInfo! @@ -249,7 +236,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { input ResourceContainedByFieldInput { connect: [ResourceContainedByConnectFieldInput!] - connectOrCreate: [ResourceContainedByConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ResourceContainedByCreateFieldInput!] } @@ -315,7 +301,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { input ResourceContainedByUpdateFieldInput { connect: [ResourceContainedByConnectFieldInput!] - connectOrCreate: [ResourceContainedByConnectOrCreateFieldInput!] create: [ResourceContainedByCreateFieldInput!] delete: [ResourceContainedByDeleteFieldInput!] disconnect: [ResourceContainedByDisconnectFieldInput!] @@ -424,16 +409,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { typename_IN: [ResourceEntityImplementation!] } - input ResourceOnCreateInput { - externalIds: [ID!] - id: ID! - name: String - properties: [Property!] - tags: [Tag!] - type: ResourceType! - updatedAt: DateTime! - } - input ResourceOptions { limit: Int offset: Int @@ -472,11 +447,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { ResourceC } - input ResourceUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input ResourceUpdateInput { containedBy: [ResourceContainedByUpdateFieldInput!] createdAt: DateTime diff --git a/packages/graphql/tests/schema/issues/3428.test.ts b/packages/graphql/tests/schema/issues/3428.test.ts index ee9773253e..444781f623 100644 --- a/packages/graphql/tests/schema/issues/3428.test.ts +++ b/packages/graphql/tests/schema/issues/3428.test.ts @@ -26,7 +26,7 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with no nested operation specified", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -364,11 +364,11 @@ describe("Relationship nested operations", () => { test("Single relationship to union with unique fields with no nested operation specified", async () => { const typeDefs = gql` type PersonOne @node { - name: String @unique + name: String } type PersonTwo @node { - nameTwo: String @unique + nameTwo: String } union Person = PersonOne | PersonTwo diff --git a/packages/graphql/tests/schema/issues/3439.test.ts b/packages/graphql/tests/schema/issues/3439.test.ts index dbf8a05c9b..2812048980 100644 --- a/packages/graphql/tests/schema/issues/3439.test.ts +++ b/packages/graphql/tests/schema/issues/3439.test.ts @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -128,10 +128,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -180,10 +176,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -317,11 +309,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -577,15 +564,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type MovieGenreConnection { edges: [MovieGenreRelationship!]! pageInfo: PageInfo! @@ -619,7 +597,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -664,7 +641,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: MovieGenreDeleteFieldInput disconnect: MovieGenreDisconnectFieldInput @@ -859,15 +835,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type SeriesGenreConnection { edges: [SeriesGenreRelationship!]! pageInfo: PageInfo! @@ -901,7 +868,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreCreateFieldInput } @@ -946,7 +912,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreUpdateFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput create: SeriesGenreCreateFieldInput delete: SeriesGenreDeleteFieldInput disconnect: SeriesGenreDisconnectFieldInput @@ -1099,7 +1064,7 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -1174,10 +1139,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -1226,10 +1187,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -1363,11 +1320,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -1569,15 +1521,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type MovieGenreConnection { edges: [MovieGenreRelationship!]! pageInfo: PageInfo! @@ -1611,7 +1554,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -1656,7 +1598,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: MovieGenreDeleteFieldInput disconnect: MovieGenreDisconnectFieldInput @@ -1848,15 +1789,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type SeriesGenreConnection { edges: [SeriesGenreRelationship!]! pageInfo: PageInfo! @@ -1890,7 +1822,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreCreateFieldInput } @@ -1935,7 +1866,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreUpdateFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput create: SeriesGenreCreateFieldInput delete: SeriesGenreDeleteFieldInput disconnect: SeriesGenreDisconnectFieldInput @@ -2096,7 +2026,7 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -2171,10 +2101,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -2223,10 +2149,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -2363,11 +2285,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -2486,16 +2403,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: GenreOnCreateInput! - } - type IProductGenreConnection { edges: [IProductGenreRelationship!]! pageInfo: PageInfo! @@ -2631,7 +2538,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input IProductGenreUpdateFieldInput { connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput create: IProductGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -2772,16 +2678,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: GenreOnCreateInput! - } - input MovieGenreCreateFieldInput { edge: MoviePropsCreateInput! node: GenreCreateInput! @@ -2789,7 +2685,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -2835,7 +2730,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -3090,16 +2984,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: GenreOnCreateInput! - } - input SeriesGenreCreateFieldInput { edge: SeriesPropsCreateInput node: GenreCreateInput! @@ -3107,7 +2991,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreCreateFieldInput } @@ -3153,7 +3036,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreUpdateFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput create: SeriesGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -3377,12 +3259,12 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { union UGenre = Genre | Rating type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } type Rating @node { - number: Int! @unique + number: Int! product: [IProduct!]! @relationship(type: "HAS_RATING", direction: IN) } `; @@ -3462,10 +3344,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -3514,10 +3392,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -3654,11 +3528,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -3841,16 +3710,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input IProductGenreGenreConnectOrCreateFieldInput { - onCreate: IProductGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreGenreConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: GenreOnCreateInput! - } - input IProductGenreGenreConnectionWhere { AND: [IProductGenreGenreConnectionWhere!] NOT: IProductGenreGenreConnectionWhere @@ -3881,7 +3740,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input IProductGenreGenreUpdateFieldInput { connect: IProductGenreGenreConnectFieldInput - connectOrCreate: IProductGenreGenreConnectOrCreateFieldInput create: IProductGenreGenreCreateFieldInput delete: IProductGenreGenreDeleteFieldInput disconnect: IProductGenreGenreDisconnectFieldInput @@ -3895,16 +3753,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: RatingConnectWhere } - input IProductGenreRatingConnectOrCreateFieldInput { - onCreate: IProductGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input IProductGenreRatingConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: RatingOnCreateInput! - } - input IProductGenreRatingConnectionWhere { AND: [IProductGenreRatingConnectionWhere!] NOT: IProductGenreRatingConnectionWhere @@ -3935,7 +3783,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input IProductGenreRatingUpdateFieldInput { connect: IProductGenreRatingConnectFieldInput - connectOrCreate: IProductGenreRatingConnectOrCreateFieldInput create: IProductGenreRatingCreateFieldInput delete: IProductGenreRatingDeleteFieldInput disconnect: IProductGenreRatingDisconnectFieldInput @@ -4079,16 +3926,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input MovieGenreGenreConnectOrCreateFieldInput { - onCreate: MovieGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreGenreConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: GenreOnCreateInput! - } - input MovieGenreGenreCreateFieldInput { edge: MoviePropsCreateInput! node: GenreCreateInput! @@ -4096,7 +3933,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreGenreFieldInput { connect: MovieGenreGenreConnectFieldInput - connectOrCreate: MovieGenreGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreGenreCreateFieldInput } @@ -4107,7 +3943,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreGenreUpdateFieldInput { connect: MovieGenreGenreConnectFieldInput - connectOrCreate: MovieGenreGenreConnectOrCreateFieldInput create: MovieGenreGenreCreateFieldInput delete: IProductGenreGenreDeleteFieldInput disconnect: IProductGenreGenreDisconnectFieldInput @@ -4121,16 +3956,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: RatingConnectWhere } - input MovieGenreRatingConnectOrCreateFieldInput { - onCreate: MovieGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input MovieGenreRatingConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: RatingOnCreateInput! - } - input MovieGenreRatingCreateFieldInput { edge: MoviePropsCreateInput! node: RatingCreateInput! @@ -4138,7 +3963,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreRatingFieldInput { connect: MovieGenreRatingConnectFieldInput - connectOrCreate: MovieGenreRatingConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreRatingCreateFieldInput } @@ -4149,7 +3973,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input MovieGenreRatingUpdateFieldInput { connect: MovieGenreRatingConnectFieldInput - connectOrCreate: MovieGenreRatingConnectOrCreateFieldInput create: MovieGenreRatingCreateFieldInput delete: IProductGenreRatingDeleteFieldInput disconnect: IProductGenreRatingDisconnectFieldInput @@ -4335,10 +4158,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { product: [RatingProductConnectFieldInput!] } - input RatingConnectOrCreateWhere { - node: RatingUniqueWhere! - } - input RatingConnectWhere { node: RatingWhere! } @@ -4387,10 +4206,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { name: StringAggregateSelection! } - input RatingOnCreateInput { - number: Int! - } - input RatingOptions { limit: Int offset: Int @@ -4528,11 +4343,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { number_LTE: Int } - input RatingUniqueWhere { - number: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - number_EQ: Int - } - input RatingUpdateInput { number: Int number_DECREMENT: Int @@ -4658,16 +4468,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: GenreConnectWhere } - input SeriesGenreGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreGenreConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: GenreOnCreateInput! - } - input SeriesGenreGenreCreateFieldInput { edge: SeriesPropsCreateInput node: GenreCreateInput! @@ -4675,7 +4475,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreGenreFieldInput { connect: SeriesGenreGenreConnectFieldInput - connectOrCreate: SeriesGenreGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreGenreCreateFieldInput } @@ -4686,7 +4485,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreGenreUpdateFieldInput { connect: SeriesGenreGenreConnectFieldInput - connectOrCreate: SeriesGenreGenreConnectOrCreateFieldInput create: SeriesGenreGenreCreateFieldInput delete: IProductGenreGenreDeleteFieldInput disconnect: IProductGenreGenreDisconnectFieldInput @@ -4700,16 +4498,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { where: RatingConnectWhere } - input SeriesGenreRatingConnectOrCreateFieldInput { - onCreate: SeriesGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input SeriesGenreRatingConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: RatingOnCreateInput! - } - input SeriesGenreRatingCreateFieldInput { edge: SeriesPropsCreateInput node: RatingCreateInput! @@ -4717,7 +4505,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreRatingFieldInput { connect: SeriesGenreRatingConnectFieldInput - connectOrCreate: SeriesGenreRatingConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreRatingCreateFieldInput } @@ -4728,7 +4515,6 @@ describe("https://github.com/neo4j/graphql/issues/3439", () => { input SeriesGenreRatingUpdateFieldInput { connect: SeriesGenreRatingConnectFieldInput - connectOrCreate: SeriesGenreRatingConnectOrCreateFieldInput create: SeriesGenreRatingCreateFieldInput delete: IProductGenreRatingDeleteFieldInput disconnect: IProductGenreRatingDisconnectFieldInput diff --git a/packages/graphql/tests/schema/issues/3698.test.ts b/packages/graphql/tests/schema/issues/3698.test.ts index ccb9d546b3..ef44e9b19b 100644 --- a/packages/graphql/tests/schema/issues/3698.test.ts +++ b/packages/graphql/tests/schema/issues/3698.test.ts @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -121,10 +121,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -174,10 +170,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -326,11 +318,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -541,15 +528,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type MovieGenreConnection { edges: [MovieGenreRelationship!]! pageInfo: PageInfo! @@ -583,7 +561,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -628,7 +605,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: MovieGenreDeleteFieldInput disconnect: MovieGenreDisconnectFieldInput @@ -803,7 +779,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -880,10 +856,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -933,10 +905,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -1088,11 +1056,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -1210,15 +1173,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type IProductGenreConnection { edges: [IProductGenreRelationship!]! pageInfo: PageInfo! @@ -1282,7 +1236,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input IProductGenreUpdateFieldInput { connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput create: IProductGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -1422,22 +1375,12 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - input MovieGenreCreateFieldInput { node: GenreCreateInput! } input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -1477,7 +1420,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -1660,7 +1602,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -1742,10 +1684,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -1795,10 +1733,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - input GenreOptions { limit: Int offset: Int @@ -1950,11 +1884,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name_STARTS_WITH: String } - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - input GenreUpdateInput { name: String product: [GenreProductUpdateFieldInput!] @@ -2073,15 +2002,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type IProductGenreConnection { edges: [IProductGenreRelationship!]! pageInfo: PageInfo! @@ -2145,7 +2065,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input IProductGenreUpdateFieldInput { connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput create: IProductGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -2286,22 +2205,12 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - input MovieGenreCreateFieldInput { node: GenreCreateInput! } input MovieGenreFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: MovieGenreCreateFieldInput } @@ -2341,7 +2250,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input MovieGenreUpdateFieldInput { connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput create: MovieGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput @@ -2537,22 +2445,12 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreConnectWhere } - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - input SeriesGenreCreateFieldInput { node: GenreCreateInput! } input SeriesGenreFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: SeriesGenreCreateFieldInput } @@ -2592,7 +2490,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { input SeriesGenreUpdateFieldInput { connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput create: SeriesGenreCreateFieldInput delete: IProductGenreDeleteFieldInput disconnect: IProductGenreDisconnectFieldInput diff --git a/packages/graphql/tests/schema/issues/3817.test.ts b/packages/graphql/tests/schema/issues/3817.test.ts index 54ed7caf3d..5a1e0f4289 100644 --- a/packages/graphql/tests/schema/issues/3817.test.ts +++ b/packages/graphql/tests/schema/issues/3817.test.ts @@ -22,11 +22,11 @@ import { gql } from "graphql-tag"; import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; -describe("3817", () => { +describe("ttps://github.com/neo4j/graphql/issues/3817", () => { test("3817", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id friends: [Person!]! @relationship( type: "FRIEND_OF" @@ -177,10 +177,6 @@ describe("3817", () => { friends: [PersonFriendsConnectFieldInput!] } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonConnectWhere { node: PersonWhere! } @@ -225,15 +221,6 @@ describe("3817", () => { where: PersonConnectWhere } - input PersonFriendsConnectOrCreateFieldInput { - onCreate: PersonFriendsConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input PersonFriendsConnectOrCreateFieldInputOnCreate { - node: PersonOnCreateInput! - } - type PersonFriendsConnection { edges: [PersonFriendsRelationship!]! pageInfo: PageInfo! @@ -269,7 +256,6 @@ describe("3817", () => { input PersonFriendsFieldInput { connect: [PersonFriendsConnectFieldInput!] - connectOrCreate: [PersonFriendsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [PersonFriendsCreateFieldInput!] } @@ -302,7 +288,6 @@ describe("3817", () => { input PersonFriendsUpdateFieldInput { connect: [PersonFriendsConnectFieldInput!] - connectOrCreate: [PersonFriendsConnectOrCreateFieldInput!] create: [PersonFriendsCreateFieldInput!] delete: [PersonFriendsDeleteFieldInput!] disconnect: [PersonFriendsDisconnectFieldInput!] @@ -310,13 +295,6 @@ describe("3817", () => { where: PersonFriendsConnectionWhere } - input PersonOnCreateInput { - \\"\\"\\" - Appears because this input type would be empty otherwise because this type is composed of just generated and/or relationship properties. See https://neo4j.com/docs/graphql-manual/current/troubleshooting/faqs/ - \\"\\"\\" - _emptyInput: Boolean - } - input PersonOptions { limit: Int offset: Int @@ -347,11 +325,6 @@ describe("3817", () => { id: SortDirection } - input PersonUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonUpdateInput { friends: [PersonFriendsUpdateFieldInput!] } diff --git a/packages/graphql/tests/schema/issues/4511.test.ts b/packages/graphql/tests/schema/issues/4511.test.ts index a5bd14cf9b..47497a21b6 100644 --- a/packages/graphql/tests/schema/issues/4511.test.ts +++ b/packages/graphql/tests/schema/issues/4511.test.ts @@ -28,13 +28,13 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { const typeDefs = gql` type Movie implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: Creature! @relationship(type: "DIRECTED", direction: IN) } type Series implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: Creature! @relationship(type: "DIRECTED", direction: IN) } interface Production { diff --git a/packages/graphql/tests/schema/issues/556.test.ts b/packages/graphql/tests/schema/issues/556.test.ts index db0875e5e4..b7e1669944 100644 --- a/packages/graphql/tests/schema/issues/556.test.ts +++ b/packages/graphql/tests/schema/issues/556.test.ts @@ -33,7 +33,7 @@ describe("https://github.com/neo4j/graphql/issues/556", () => { } type Article @node { - id: ID! @id @unique + id: ID! @id blocks: [Block!]! @relationship(type: "HAS_BLOCK", direction: OUT, properties: "HasBlock") images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } @@ -47,16 +47,16 @@ describe("https://github.com/neo4j/graphql/issues/556", () => { } type TextBlock implements Block @node { - id: ID @id @unique + id: ID @id text: String } type DividerBlock implements Block @node { - id: ID @id @unique + id: ID @id } type ImageBlock implements Block @node { - id: ID @id @unique + id: ID @id images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } diff --git a/packages/graphql/tests/schema/issues/872.test.ts b/packages/graphql/tests/schema/issues/872.test.ts index 5b3a4389a7..dfff228a73 100644 --- a/packages/graphql/tests/schema/issues/872.test.ts +++ b/packages/graphql/tests/schema/issues/872.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { const typeDefs = gql` type Movie @node { title: String! - id: ID! @id @unique + id: ID! @id } type Actor @node { @@ -113,15 +113,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { where: MovieConnectWhere } - input Actor2MoviesConnectOrCreateFieldInput { - onCreate: Actor2MoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input Actor2MoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - type Actor2MoviesConnection { edges: [Actor2MoviesRelationship!]! pageInfo: PageInfo! @@ -153,7 +144,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input Actor2MoviesFieldInput { connect: [Actor2MoviesConnectFieldInput!] - connectOrCreate: [Actor2MoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [Actor2MoviesCreateFieldInput!] } @@ -199,7 +189,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input Actor2MoviesUpdateFieldInput { connect: [Actor2MoviesConnectFieldInput!] - connectOrCreate: [Actor2MoviesConnectOrCreateFieldInput!] create: [Actor2MoviesCreateFieldInput!] delete: [Actor2MoviesDeleteFieldInput!] disconnect: [Actor2MoviesDisconnectFieldInput!] @@ -321,15 +310,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { where: MovieConnectWhere } - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - type ActorMoviesConnection { edges: [ActorMoviesRelationship!]! pageInfo: PageInfo! @@ -361,7 +341,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input ActorMoviesFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ActorMoviesCreateFieldInput!] } @@ -407,7 +386,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input ActorMoviesUpdateFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] create: [ActorMoviesCreateFieldInput!] delete: [ActorMoviesDeleteFieldInput!] disconnect: [ActorMoviesDisconnectFieldInput!] @@ -526,10 +504,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { title: StringAggregateSelection! } - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - input MovieConnectWhere { node: MovieWhere! } @@ -543,10 +517,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { node: Movie! } - input MovieOnCreateInput { - title: String! - } - input MovieOptions { limit: Int offset: Int @@ -564,11 +534,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { title: SortDirection } - input MovieUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input MovieUpdateInput { title: String } diff --git a/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts b/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts deleted file mode 100644 index c4a5b1cc76..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts +++ /dev/null @@ -1,416 +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 { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Connect Or Create", () => { - test("Connect Or Create", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { excludeDeprecatedFields: { connectOrCreate: true } }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - isan: String - title: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/rfcs/rfc-003.test.ts b/packages/graphql/tests/schema/rfcs/rfc-003.test.ts index 916b646a9e..c87ce28f44 100644 --- a/packages/graphql/tests/schema/rfcs/rfc-003.test.ts +++ b/packages/graphql/tests/schema/rfcs/rfc-003.test.ts @@ -17,8 +17,8 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import { GraphQLError, GraphQLSchema } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; describe("schema/rfc/003", () => { @@ -34,15 +34,15 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } type SecondTarget @node { - id: ID @id @unique + id: ID @id } type ThirdTarget @node { - id: ID @id @unique + id: ID @id } `; @@ -58,7 +58,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -74,7 +74,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -89,7 +89,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -109,14 +109,14 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target!]! @relationship(type: "HAS_TARGET", direction: OUT) target1: Target! @relationship(type: "HAS_TARGET", direction: OUT) target2: Target @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -132,12 +132,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target!] @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -156,12 +156,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target]! @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -180,12 +180,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target] @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; diff --git a/packages/graphql/tests/schema/subscriptions.test.ts b/packages/graphql/tests/schema/subscriptions.test.ts index be465c6cf3..aad18c8a7b 100644 --- a/packages/graphql/tests/schema/subscriptions.test.ts +++ b/packages/graphql/tests/schema/subscriptions.test.ts @@ -4808,14 +4808,14 @@ describe("Subscriptions", () => { const typeDefs = gql` type Movie implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: Creature! @relationship(type: "DIRECTED", direction: IN) } type Series implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: Creature! @relationship(type: "DIRECTED", direction: IN) } diff --git a/packages/graphql/tests/schema/union-interface-relationship.test.ts b/packages/graphql/tests/schema/union-interface-relationship.test.ts index fd8f010c5e..142f13a8cc 100644 --- a/packages/graphql/tests/schema/union-interface-relationship.test.ts +++ b/packages/graphql/tests/schema/union-interface-relationship.test.ts @@ -29,12 +29,12 @@ describe("Union Interface Relationships", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type Actor @node { name: String! - id: Int @unique + id: Int movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) } @@ -53,8 +53,8 @@ describe("Union Interface Relationships", () => { type Person implements Reviewer @node { name: String! reputation: Int! - id: Int @unique - reviewerId: Int @unique + id: Int + reviewerId: Int movies: [Movie!]! @relationship(type: "REVIEWED", direction: OUT, properties: "Review") } @@ -160,10 +160,6 @@ describe("Union Interface Relationships", () => { movies: [ActorMoviesConnectFieldInput!] } - input ActorConnectOrCreateWhere { - node: ActorUniqueWhere! - } - input ActorConnectWhere { node: ActorWhere! } @@ -226,16 +222,6 @@ describe("Union Interface Relationships", () => { where: MovieConnectWhere } - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - type ActorMoviesConnection { edges: [ActorMoviesRelationship!]! pageInfo: PageInfo! @@ -272,7 +258,6 @@ describe("Union Interface Relationships", () => { input ActorMoviesFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ActorMoviesCreateFieldInput!] } @@ -330,7 +315,6 @@ describe("Union Interface Relationships", () => { input ActorMoviesUpdateFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] create: [ActorMoviesCreateFieldInput!] delete: [ActorMoviesDeleteFieldInput!] disconnect: [ActorMoviesDisconnectFieldInput!] @@ -338,11 +322,6 @@ describe("Union Interface Relationships", () => { where: ActorMoviesConnectionWhere } - input ActorOnCreateInput { - id: Int - name: String! - } - input ActorOptions { limit: Int offset: Int @@ -360,11 +339,6 @@ describe("Union Interface Relationships", () => { name: SortDirection } - input ActorUniqueWhere { - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - } - input ActorUpdateInput { id: Int id_DECREMENT: Int @@ -644,16 +618,6 @@ describe("Union Interface Relationships", () => { where: ActorConnectWhere } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: ActorOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! @@ -690,7 +654,6 @@ describe("Union Interface Relationships", () => { input MovieActorsFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieActorsCreateFieldInput!] } @@ -748,7 +711,6 @@ describe("Union Interface Relationships", () => { input MovieActorsUpdateFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] create: [MovieActorsCreateFieldInput!] delete: [MovieActorsDeleteFieldInput!] disconnect: [MovieActorsDisconnectFieldInput!] @@ -768,10 +730,6 @@ describe("Union Interface Relationships", () => { reviewers: [MovieReviewersConnectFieldInput!] } - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - input MovieConnectWhere { node: MovieWhere! } @@ -796,16 +754,6 @@ describe("Union Interface Relationships", () => { where: ActorConnectWhere } - input MovieDirectorsActorConnectOrCreateFieldInput { - onCreate: MovieDirectorsActorConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieDirectorsActorConnectOrCreateFieldInputOnCreate { - edge: DirectedCreateInput! - node: ActorOnCreateInput! - } - input MovieDirectorsActorConnectionWhere { AND: [MovieDirectorsActorConnectionWhere!] NOT: MovieDirectorsActorConnectionWhere @@ -831,7 +779,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsActorFieldInput { connect: [MovieDirectorsActorConnectFieldInput!] - connectOrCreate: [MovieDirectorsActorConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieDirectorsActorCreateFieldInput!] } @@ -842,7 +789,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsActorUpdateFieldInput { connect: [MovieDirectorsActorConnectFieldInput!] - connectOrCreate: [MovieDirectorsActorConnectOrCreateFieldInput!] create: [MovieDirectorsActorCreateFieldInput!] delete: [MovieDirectorsActorDeleteFieldInput!] disconnect: [MovieDirectorsActorDisconnectFieldInput!] @@ -891,16 +837,6 @@ describe("Union Interface Relationships", () => { where: PersonConnectWhere } - input MovieDirectorsPersonConnectOrCreateFieldInput { - onCreate: MovieDirectorsPersonConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input MovieDirectorsPersonConnectOrCreateFieldInputOnCreate { - edge: DirectedCreateInput! - node: PersonOnCreateInput! - } - input MovieDirectorsPersonConnectionWhere { AND: [MovieDirectorsPersonConnectionWhere!] NOT: MovieDirectorsPersonConnectionWhere @@ -926,7 +862,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsPersonFieldInput { connect: [MovieDirectorsPersonConnectFieldInput!] - connectOrCreate: [MovieDirectorsPersonConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieDirectorsPersonCreateFieldInput!] } @@ -937,7 +872,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsPersonUpdateFieldInput { connect: [MovieDirectorsPersonConnectFieldInput!] - connectOrCreate: [MovieDirectorsPersonConnectOrCreateFieldInput!] create: [MovieDirectorsPersonCreateFieldInput!] delete: [MovieDirectorsPersonDeleteFieldInput!] disconnect: [MovieDirectorsPersonDisconnectFieldInput!] @@ -967,11 +901,6 @@ describe("Union Interface Relationships", () => { node: Movie! } - input MovieOnCreateInput { - imdbId: Int - title: String! - } - input MovieOptions { limit: Int offset: Int @@ -1126,11 +1055,6 @@ describe("Union Interface Relationships", () => { title: SortDirection } - input MovieUniqueWhere { - imdbId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbId_EQ: Int - } - input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] directors: MovieDirectorsUpdateInput @@ -1291,10 +1215,6 @@ describe("Union Interface Relationships", () => { movies: [PersonMoviesConnectFieldInput!] } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonConnectWhere { node: PersonWhere! } @@ -1359,16 +1279,6 @@ describe("Union Interface Relationships", () => { where: MovieConnectWhere } - input PersonMoviesConnectOrCreateFieldInput { - onCreate: PersonMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input PersonMoviesConnectOrCreateFieldInputOnCreate { - edge: ReviewCreateInput! - node: MovieOnCreateInput! - } - type PersonMoviesConnection { edges: [PersonMoviesRelationship!]! pageInfo: PageInfo! @@ -1405,7 +1315,6 @@ describe("Union Interface Relationships", () => { input PersonMoviesFieldInput { connect: [PersonMoviesConnectFieldInput!] - connectOrCreate: [PersonMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [PersonMoviesCreateFieldInput!] } @@ -1463,7 +1372,6 @@ describe("Union Interface Relationships", () => { input PersonMoviesUpdateFieldInput { connect: [PersonMoviesConnectFieldInput!] - connectOrCreate: [PersonMoviesConnectOrCreateFieldInput!] create: [PersonMoviesCreateFieldInput!] delete: [PersonMoviesDeleteFieldInput!] disconnect: [PersonMoviesDisconnectFieldInput!] @@ -1471,13 +1379,6 @@ describe("Union Interface Relationships", () => { where: PersonMoviesConnectionWhere } - input PersonOnCreateInput { - id: Int - name: String! - reputation: Int! - reviewerId: Int - } - input PersonOptions { limit: Int offset: Int @@ -1497,13 +1398,6 @@ describe("Union Interface Relationships", () => { reviewerId: SortDirection } - input PersonUniqueWhere { - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reviewerId_EQ: Int - } - input PersonUpdateInput { id: Int id_DECREMENT: Int diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts deleted file mode 100644 index 9e3d9a705d..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts +++ /dev/null @@ -1,141 +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 { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Connect or create with @alias", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type BibliographicReference @node(labels: ["BibliographicReference", "Resource"]) { - iri: ID! @unique @alias(property: "_uri") - prefLabel: [String] - isInPublication: [Concept!]! @relationship(type: "isInPublication", direction: OUT) - } - - type Concept @node(labels: ["Concept", "Resource"]) { - iri: ID! @unique @alias(property: "$_uri") - prefLabel: [String]! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("where with multiple filters and params", async () => { - const query = /* GraphQL */ ` - mutation { - updateBibliographicReferences( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel: "Updated Label:My BRS with Resource" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - bibliographicReferences { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:BibliographicReference:Resource) - WHERE this._uri = $param0 - SET this.prefLabel = $this_update_prefLabel - WITH this - CALL { - WITH this - MERGE (this_isInPublication0_connectOrCreate0:Concept:Resource { \`$_uri\`: $this_isInPublication0_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication0_connectOrCreate0.\`$_uri\` = $this_isInPublication0_connectOrCreate_param1, - this_isInPublication0_connectOrCreate0.prefLabel = $this_isInPublication0_connectOrCreate_param2 - MERGE (this)-[this_isInPublication0_connectOrCreate_this0:isInPublication]->(this_isInPublication0_connectOrCreate0) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_isInPublication1_connectOrCreate0:Concept:Resource { \`$_uri\`: $this_isInPublication1_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication1_connectOrCreate0.\`$_uri\` = $this_isInPublication1_connectOrCreate_param1, - this_isInPublication1_connectOrCreate0.prefLabel = $this_isInPublication1_connectOrCreate_param2 - MERGE (this)-[this_isInPublication1_connectOrCreate_this0:isInPublication]->(this_isInPublication1_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:isInPublication]->(update_this1:Concept:Resource) - WHERE update_this1.\`$_uri\` IN $update_param0 - WITH update_this1 { .prefLabel, iri: update_this1.\`$_uri\` } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .prefLabel, iri: this._uri, isInPublication: update_var2 }) AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"update_param0\\": [ - \\"new-f\\", - \\"new-e\\" - ], - \\"param0\\": \\"urn:myiri2\\", - \\"this_update_prefLabel\\": [ - \\"Updated Label:My BRS with Resource\\" - ], - \\"this_isInPublication0_connectOrCreate_param0\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param1\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"this_isInPublication1_connectOrCreate_param0\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param1\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts deleted file mode 100644 index 229351903f..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts +++ /dev/null @@ -1,658 +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 { Neo4jGraphQL } from "../../../../src"; -import { createBearerToken } from "../../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("connectOrCreate", () => { - const secret = "secret"; - let neoSchema: Neo4jGraphQL; - - function createTypedef(operations: string): string { - return /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type Movie @node { - title: String - genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type Genre @authorization(validate: [{ operations: ${operations}, where: { jwt: { roles_INCLUDES: "admin" } } }]) @node { - name: String @unique - } - `; - } - - describe("Create -> nested connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: [ - { where: { node: { name_EQ: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ] - ) { - movies { - title - } - } - } - `; - - test("Create with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Update -> nested connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - update: { - title: "Cool Movie" - genres: { - connectOrCreate: [ - { where: { node: { name: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ) { - movies { - title - } - } - } - `; - - test("Update with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Update -> connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - update: { - title: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - movies { - title - } - } - } - `; - - test("Update with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and allow in auth", async () => { - const typeDefs = ` - type Movie @node { - title: String - genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type Genre @node { - name: String @unique - } - - extend type Genre @authorization(validate: [{ when: [BEFORE], operations: [CREATE], where: { node: { name: "$jwt.sub" } } }]) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", { - sub: "test", - }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts deleted file mode 100644 index ca308d4996..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts +++ /dev/null @@ -1,247 +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 { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Create or connect with unions", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! - isan: String! @unique - } - - type Series @node { - title: String! - isan: String! @unique - } - - union Production = Movie | Series - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type Actor @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - actedIn: { - Movie: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0000-03B6-0000-O-0000-0006-P" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "0000-0000-03B6-0000-O-0000-0006-P" } - } - } - } - Series: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0001-ECC5-0000-8-0000-0001-B" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "0000-0001-ECC5-0000-8-0000-0001-B" - } - } - } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_actedIn_Movie_connectOrCreate0:Movie { isan: $this0_actedIn_Movie_connectOrCreate_param0 }) - ON CREATE SET - this0_actedIn_Movie_connectOrCreate0.title = $this0_actedIn_Movie_connectOrCreate_param1, - this0_actedIn_Movie_connectOrCreate0.isan = $this0_actedIn_Movie_connectOrCreate_param2 - MERGE (this0)-[this0_actedIn_Movie_connectOrCreate_this0:ACTED_IN]->(this0_actedIn_Movie_connectOrCreate0) - ON CREATE SET - this0_actedIn_Movie_connectOrCreate_this0.screentime = $this0_actedIn_Movie_connectOrCreate_param3 - RETURN count(*) AS _ - } - WITH this0 - CALL { - WITH this0 - MERGE (this0_actedIn_Series_connectOrCreate0:Series { isan: $this0_actedIn_Series_connectOrCreate_param0 }) - ON CREATE SET - this0_actedIn_Series_connectOrCreate0.title = $this0_actedIn_Series_connectOrCreate_param1, - this0_actedIn_Series_connectOrCreate0.isan = $this0_actedIn_Series_connectOrCreate_param2 - MERGE (this0)-[this0_actedIn_Series_connectOrCreate_this0:ACTED_IN]->(this0_actedIn_Series_connectOrCreate0) - ON CREATE SET - this0_actedIn_Series_connectOrCreate_this0.screentime = $this0_actedIn_Series_connectOrCreate_param3 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_actedIn_Movie_connectOrCreate_param0\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this0_actedIn_Movie_connectOrCreate_param1\\": \\"Forrest Gump\\", - \\"this0_actedIn_Movie_connectOrCreate_param2\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this0_actedIn_Movie_connectOrCreate_param3\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"this0_actedIn_Series_connectOrCreate_param0\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this0_actedIn_Series_connectOrCreate_param1\\": \\"Band of Brothers\\", - \\"this0_actedIn_Series_connectOrCreate_param2\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this0_actedIn_Series_connectOrCreate_param3\\": { - \\"low\\": 126, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name: "Tom Hanks" - actedIn: { - Movie: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0000-03B6-0000-O-0000-0006-P" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "0000-0000-03B6-0000-O-0000-0006-P" } - } - } - } - Series: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0001-ECC5-0000-8-0000-0001-B" } } - onCreate: { - edge: { screentime: 126 } - node: { title: "Band of Brothers", isan: "0000-0001-ECC5-0000-8-0000-0001-B" } - } - } - } - } - } - where: { name_EQ: "Tom Hanks evil twin" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name - WITH this - CALL { - WITH this - MERGE (this_actedIn_Movie0_connectOrCreate0:Movie { isan: $this_actedIn_Movie0_connectOrCreate_param0 }) - ON CREATE SET - this_actedIn_Movie0_connectOrCreate0.title = $this_actedIn_Movie0_connectOrCreate_param1, - this_actedIn_Movie0_connectOrCreate0.isan = $this_actedIn_Movie0_connectOrCreate_param2 - MERGE (this)-[this_actedIn_Movie0_connectOrCreate_this0:ACTED_IN]->(this_actedIn_Movie0_connectOrCreate0) - ON CREATE SET - this_actedIn_Movie0_connectOrCreate_this0.screentime = $this_actedIn_Movie0_connectOrCreate_param3 - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_actedIn_Series0_connectOrCreate0:Series { isan: $this_actedIn_Series0_connectOrCreate_param0 }) - ON CREATE SET - this_actedIn_Series0_connectOrCreate0.title = $this_actedIn_Series0_connectOrCreate_param1, - this_actedIn_Series0_connectOrCreate0.isan = $this_actedIn_Series0_connectOrCreate_param2 - MERGE (this)-[this_actedIn_Series0_connectOrCreate_this0:ACTED_IN]->(this_actedIn_Series0_connectOrCreate0) - ON CREATE SET - this_actedIn_Series0_connectOrCreate_this0.screentime = $this_actedIn_Series0_connectOrCreate_param3 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks evil twin\\", - \\"this_update_name\\": \\"Tom Hanks\\", - \\"this_actedIn_Movie0_connectOrCreate_param0\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this_actedIn_Movie0_connectOrCreate_param1\\": \\"Forrest Gump\\", - \\"this_actedIn_Movie0_connectOrCreate_param2\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this_actedIn_Movie0_connectOrCreate_param3\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"this_actedIn_Series0_connectOrCreate_param0\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this_actedIn_Series0_connectOrCreate_param1\\": \\"Band of Brothers\\", - \\"this_actedIn_Series0_connectOrCreate_param2\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this_actedIn_Series0_connectOrCreate_param3\\": { - \\"low\\": 126, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts deleted file mode 100644 index 7135e56b6f..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts +++ /dev/null @@ -1,599 +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 { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Create or Connect", () => { - describe("Simple", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Autogenerated field on created node", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.createdAt = datetime(), - this0_movies_connectOrCreate0.id = randomUUID(), - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect operation - with @id in where", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "movieId" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { id: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.createdAt = datetime(), - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"movieId\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.createdAt = datetime(), - this_movies0_connectOrCreate0.id = randomUUID(), - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation - with @id in where", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "movieId" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { id: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.createdAt = datetime(), - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"movieId\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Autogenerated field on created relationship", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - id: ID! @id - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime @timestamp(operations: [UPDATE]) - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.createdAt = datetime(), - this0_movies_connectOrCreate_this0.id = randomUUID(), - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.createdAt = datetime(), - this_movies0_connectOrCreate_this0.id = randomUUID(), - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/directives/autogenerate.test.ts b/packages/graphql/tests/tck/directives/autogenerate.test.ts index 773a0fb904..e27227f7a2 100644 --- a/packages/graphql/tests/tck/directives/autogenerate.test.ts +++ b/packages/graphql/tests/tck/directives/autogenerate.test.ts @@ -27,7 +27,7 @@ describe("Cypher autogenerate directive", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Movie @node { - id: ID! @id @unique + id: ID! @id name: String! } `; diff --git a/packages/graphql/tests/tck/fragments.test.ts b/packages/graphql/tests/tck/fragments.test.ts index 3b65e42a28..809344c67f 100644 --- a/packages/graphql/tests/tck/fragments.test.ts +++ b/packages/graphql/tests/tck/fragments.test.ts @@ -31,7 +31,7 @@ describe("Cypher Fragment", () => { } type User implements Entity @node { - id: ID! @id @unique + id: ID! @id username: String! owns: [OwnableType!]! @relationship(type: "OWNS", direction: OUT) } @@ -44,12 +44,12 @@ describe("Cypher Fragment", () => { } type Tile implements Ownable @node { - id: ID! @id @unique + id: ID! @id owner: User! @relationship(type: "OWNS", direction: IN) } type Character implements Ownable @node { - id: ID! @id @unique + id: ID! @id owner: User! @relationship(type: "OWNS", direction: IN) } `; diff --git a/packages/graphql/tests/tck/global-node.test.ts b/packages/graphql/tests/tck/global-node.test.ts index d89ce10e07..635d21223b 100644 --- a/packages/graphql/tests/tck/global-node.test.ts +++ b/packages/graphql/tests/tck/global-node.test.ts @@ -25,12 +25,12 @@ describe("Global nodes", () => { test("it should fetch the correct node and fields", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - name: ID! @id @unique @relayId + name: ID! @id @relayId movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } `; @@ -65,12 +65,12 @@ describe("Global nodes", () => { test("it should project the correct node and fields when id is the idField", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Movie!]! @relationship(type: "ACTED_IN", direction: IN) } `; @@ -108,12 +108,12 @@ describe("Global nodes", () => { test("it should project the correct selectionSet when id is used as a where argument", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Movie!]! @relationship(type: "ACTED_IN", direction: IN) } `; diff --git a/packages/graphql/tests/tck/issues/1115.test.ts b/packages/graphql/tests/tck/issues/1115.test.ts deleted file mode 100644 index febad4d19e..0000000000 --- a/packages/graphql/tests/tck/issues/1115.test.ts +++ /dev/null @@ -1,126 +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 { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1115", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type Parent @node { - children: [Child!]! @relationship(type: "HAS", direction: IN) - } - - type Child @node { - tcId: String @unique - } - - extend type Child - @authorization( - validate: [ - { - operations: [READ, CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { jwt: { roles_INCLUDES: "upstream" } } - } - { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } - ] - ) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - test("multiple connectOrCreate operations with auth", async () => { - const query = /* GraphQL */ ` - mutation UpdateParents { - updateParents( - update: { - children: { - connectOrCreate: [ - { where: { node: { tcId_EQ: "123" } }, onCreate: { node: { tcId: "123" } } } - { where: { node: { tcId_EQ: "456" } }, onCreate: { node: { tcId: "456" } } } - ] - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - contextValues: { token: createBearerToken("secret") }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Parent) - WITH this - CALL { - WITH this - MERGE (this_children0_connectOrCreate0:Child { tcId: $this_children0_connectOrCreate_param0 }) - ON CREATE SET - this_children0_connectOrCreate0.tcId = $this_children0_connectOrCreate_param1 - MERGE (this)<-[this_children0_connectOrCreate_this0:HAS]-(this_children0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_children0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_children0_connectOrCreate1:Child { tcId: $this_children0_connectOrCreate_param5 }) - ON CREATE SET - this_children0_connectOrCreate1.tcId = $this_children0_connectOrCreate_param6 - MERGE (this)<-[this_children0_connectOrCreate_this1:HAS]-(this_children0_connectOrCreate1) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_children0_connectOrCreate_param7 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_children0_connectOrCreate_param0\\": \\"123\\", - \\"this_children0_connectOrCreate_param1\\": \\"123\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_children0_connectOrCreate_param4\\": \\"upstream\\", - \\"this_children0_connectOrCreate_param5\\": \\"456\\", - \\"this_children0_connectOrCreate_param6\\": \\"456\\", - \\"this_children0_connectOrCreate_param7\\": \\"upstream\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1131.test.ts b/packages/graphql/tests/tck/issues/1131.test.ts deleted file mode 100644 index 12a71aff90..0000000000 --- a/packages/graphql/tests/tck/issues/1131.test.ts +++ /dev/null @@ -1,141 +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 { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1131", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type BibliographicReference @node(labels: ["BibliographicReference", "Resource"]) { - iri: ID! @unique @alias(property: "uri") - prefLabel: [String] - isInPublication: [Concept!]! @relationship(type: "isInPublication", direction: OUT) - } - - type Concept @node(labels: ["Concept", "Resource"]) { - iri: ID! @unique @alias(property: "uri") - prefLabel: [String]! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("where with multiple filters and params", async () => { - const query = /* GraphQL */ ` - mutation { - updateBibliographicReferences( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel: "Updated Label:My BRS with Resource" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - bibliographicReferences { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:BibliographicReference:Resource) - WHERE this.uri = $param0 - SET this.prefLabel = $this_update_prefLabel - WITH this - CALL { - WITH this - MERGE (this_isInPublication0_connectOrCreate0:Concept:Resource { uri: $this_isInPublication0_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication0_connectOrCreate0.uri = $this_isInPublication0_connectOrCreate_param1, - this_isInPublication0_connectOrCreate0.prefLabel = $this_isInPublication0_connectOrCreate_param2 - MERGE (this)-[this_isInPublication0_connectOrCreate_this0:isInPublication]->(this_isInPublication0_connectOrCreate0) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_isInPublication1_connectOrCreate0:Concept:Resource { uri: $this_isInPublication1_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication1_connectOrCreate0.uri = $this_isInPublication1_connectOrCreate_param1, - this_isInPublication1_connectOrCreate0.prefLabel = $this_isInPublication1_connectOrCreate_param2 - MERGE (this)-[this_isInPublication1_connectOrCreate_this0:isInPublication]->(this_isInPublication1_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:isInPublication]->(update_this1:Concept:Resource) - WHERE update_this1.uri IN $update_param0 - WITH update_this1 { .prefLabel, iri: update_this1.uri } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .prefLabel, iri: this.uri, isInPublication: update_var2 }) AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"update_param0\\": [ - \\"new-f\\", - \\"new-e\\" - ], - \\"param0\\": \\"urn:myiri2\\", - \\"this_update_prefLabel\\": [ - \\"Updated Label:My BRS with Resource\\" - ], - \\"this_isInPublication0_connectOrCreate_param0\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param1\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"this_isInPublication1_connectOrCreate_param0\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param1\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1150.test.ts b/packages/graphql/tests/tck/issues/1150.test.ts index 111dce734f..c3fcf615a3 100644 --- a/packages/graphql/tests/tck/issues/1150.test.ts +++ b/packages/graphql/tests/tck/issues/1150.test.ts @@ -31,19 +31,19 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { } type Battery @node { - id: ID! @unique + id: ID! current: Boolean! } extend type Battery @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) type CombustionEngine @node { - id: ID! @unique + id: ID! current: Boolean! } type Drive @node { - id: ID! @unique + id: ID! current: Boolean! driveCompositions: [DriveComposition!]! @relationship(type: "CONSISTS_OF", properties: "RelationProps", direction: OUT) @@ -52,7 +52,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { union DriveComponent = Battery | CombustionEngine type DriveComposition @node { - id: ID! @unique + id: ID! current: Boolean! driveComponent: [DriveComponent!]! @relationship(type: "HAS", properties: "RelationProps", direction: OUT) diff --git a/packages/graphql/tests/tck/issues/1182.test.ts b/packages/graphql/tests/tck/issues/1182.test.ts deleted file mode 100644 index 33330e8a4c..0000000000 --- a/packages/graphql/tests/tck/issues/1182.test.ts +++ /dev/null @@ -1,127 +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 { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1182", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID! @id @unique - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type Actor @node { - id: ID! @id @unique - name: String! - dob: DateTime! - homeAddress: Point! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("DateTime and Point values get set as expected", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { - title: "Forrest Gump" - actors: { - connectOrCreate: { - where: { node: { id_EQ: 1 } } - onCreate: { - node: { - name: "Tom Hanks" - dob: "1970-01-01T00:00:00.000Z" - homeAddress: { longitude: 1, latitude: 2 } - } - } - } - } - } - ] - ) { - movies { - title - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = randomUUID() - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_actors_connectOrCreate0:Actor { id: $this0_actors_connectOrCreate_param0 }) - ON CREATE SET - this0_actors_connectOrCreate0.name = $this0_actors_connectOrCreate_param1, - this0_actors_connectOrCreate0.dob = $this0_actors_connectOrCreate_param2, - this0_actors_connectOrCreate0.homeAddress = $this0_actors_connectOrCreate_param3 - MERGE (this0)<-[this0_actors_connectOrCreate_this0:ACTED_IN]-(this0_actors_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Forrest Gump\\", - \\"this0_actors_connectOrCreate_param0\\": \\"1\\", - \\"this0_actors_connectOrCreate_param1\\": \\"Tom Hanks\\", - \\"this0_actors_connectOrCreate_param2\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"this0_actors_connectOrCreate_param3\\": { - \\"longitude\\": 1, - \\"latitude\\": 2 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1221.test.ts b/packages/graphql/tests/tck/issues/1221.test.ts index f563874f0c..ab80f31788 100644 --- a/packages/graphql/tests/tck/issues/1221.test.ts +++ b/packages/graphql/tests/tck/issues/1221.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { test("should apply where filter for deep relations, two relations deep", async () => { typeDefs = /* GraphQL */ ` type Series @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [MasterData!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -42,7 +42,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { } type MasterData @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } @@ -129,13 +129,13 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { test("should apply where filter for deep relations, three relations deep", async () => { typeDefs = /* GraphQL */ ` type Main @node { - id: ID! @unique + id: ID! current: Boolean! main: [Series!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) } type Series @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [MasterData!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -150,7 +150,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { } type MasterData @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/1430.test.ts b/packages/graphql/tests/tck/issues/1430.test.ts index e574e3d01f..ea9dd755ce 100644 --- a/packages/graphql/tests/tck/issues/1430.test.ts +++ b/packages/graphql/tests/tck/issues/1430.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1430", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type ABCE @node { - id: ID @id @unique + id: ID @id name: String interface: InterfaceMom @relationship(type: "HAS_INTERFACE", direction: OUT) } @@ -38,13 +38,13 @@ describe("https://github.com/neo4j/graphql/issues/1430", () => { } type ChildOne implements InterfaceMom @node { - id: ID @id @unique + id: ID @id name: String feathur: String } type ChildTwo implements InterfaceMom @node { - id: ID @id @unique + id: ID @id name: String sth: String } diff --git a/packages/graphql/tests/tck/issues/1535.test.ts b/packages/graphql/tests/tck/issues/1535.test.ts index bf798273e6..6f22205d00 100644 --- a/packages/graphql/tests/tck/issues/1535.test.ts +++ b/packages/graphql/tests/tck/issues/1535.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Tenant @node { - id: ID! @id @unique + id: ID! @id name: String! events: [Event!]! @relationship(type: "HOSTED_BY", direction: IN) fooBars: [FooBar!]! @relationship(type: "HAS_FOOBARS", direction: OUT) @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type Screening implements Event @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! } @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type FooBar @node { - id: ID! @id @unique + id: ID! @id name: String! } `; diff --git a/packages/graphql/tests/tck/issues/1536.test.ts b/packages/graphql/tests/tck/issues/1536.test.ts index 3f101b3193..7dccdd3108 100644 --- a/packages/graphql/tests/tck/issues/1536.test.ts +++ b/packages/graphql/tests/tck/issues/1536.test.ts @@ -27,12 +27,12 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type SomeNode @node { - id: ID! @id @unique + id: ID! @id other: OtherNode! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type OtherNode @node { - id: ID! @id @unique + id: ID! @id interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } @@ -41,7 +41,7 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { } type MyImplementation implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } `; diff --git a/packages/graphql/tests/tck/issues/1628.test.ts b/packages/graphql/tests/tck/issues/1628.test.ts index fbf86e23a2..72b5be637b 100644 --- a/packages/graphql/tests/tck/issues/1628.test.ts +++ b/packages/graphql/tests/tck/issues/1628.test.ts @@ -30,7 +30,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { """ IRI """ - iri: ID! @unique @alias(property: "uri") + iri: ID! @alias(property: "uri") dcterms__title: [dcterms_title!]! @relationship(type: "dcterms__title", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/1751.test.ts b/packages/graphql/tests/tck/issues/1751.test.ts index 5dd86c736a..56fb14559d 100644 --- a/packages/graphql/tests/tck/issues/1751.test.ts +++ b/packages/graphql/tests/tck/issues/1751.test.ts @@ -27,14 +27,14 @@ describe("https://github.com/neo4j/graphql/issues/1751", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Organization @node { - organizationId: ID! @id @unique + organizationId: ID! @id title: String createdAt: DateTime! admins: [Admin!]! @relationship(type: "HAS_ADMINISTRATOR", direction: OUT) } type Admin @node { - adminId: ID! @id @unique + adminId: ID! @id createdAt: DateTime! isSuperAdmin: Boolean organizations: [Organization!]! @relationship(type: "HAS_ADMINISTRATOR", direction: IN) diff --git a/packages/graphql/tests/tck/issues/1756.test.ts b/packages/graphql/tests/tck/issues/1756.test.ts deleted file mode 100644 index 1f58eeb5b5..0000000000 --- a/packages/graphql/tests/tck/issues/1756.test.ts +++ /dev/null @@ -1,124 +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 { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1756", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - interface INode { - id: ID - } - - type Product implements INode @node { - id: ID @populatedBy(operations: [CREATE], callback: "nanoid") - name: String! - genre: [Genre!]! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Genre implements INode @node { - id: ID @populatedBy(operations: [CREATE], callback: "nanoid") - value: String! @unique - } - `; - - const nanoid = () => { - return `callback_value`; - }; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { populatedBy: { callbacks: { nanoid } } }, - }); - }); - - test("should define the ID using the callback function", async () => { - const query = /* GraphQL */ ` - mutation { - createProducts( - input: { - name: "TestProduct" - genre: { - connectOrCreate: [ - { where: { node: { value_EQ: "Action" } }, onCreate: { node: { value: "Action" } } } - ] - } - } - ) { - products { - id - name - genre { - id - value - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Product) - SET this0.id = $resolvedCallbacks.this0_id_nanoid - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_genre_connectOrCreate0:Genre { value: $this0_genre_connectOrCreate_param0 }) - ON CREATE SET - this0_genre_connectOrCreate0.value = $this0_genre_connectOrCreate_param1, - this0_genre_connectOrCreate0.id = $resolvedCallbacks.this0_genre_connectOrCreate0_id_nanoid - MERGE (this0)-[this0_genre_connectOrCreate_this0:HAS_GENRE]->(this0_genre_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_GENRE]->(create_this1:Genre) - WITH create_this1 { .id, .value } AS create_this1 - RETURN collect(create_this1) AS create_var2 - } - RETURN this0 { .id, .name, genre: create_var2 } AS create_var3 - } - RETURN [create_var3] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"TestProduct\\", - \\"this0_genre_connectOrCreate_param0\\": \\"Action\\", - \\"this0_genre_connectOrCreate_param1\\": \\"Action\\", - \\"resolvedCallbacks\\": { - \\"this0_id_nanoid\\": \\"callback_value\\", - \\"this0_genre_connectOrCreate0_id_nanoid\\": \\"callback_value\\" - } - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1760.test.ts b/packages/graphql/tests/tck/issues/1760.test.ts index 45a08d1ea1..54b21c1515 100644 --- a/packages/graphql/tests/tck/issues/1760.test.ts +++ b/packages/graphql/tests/tck/issues/1760.test.ts @@ -41,7 +41,7 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { markets: [Market!]! @relationship(type: "HAS_MARKETS", direction: OUT) - id: ID! @unique + id: ID! relatedId: ID @cypher(statement: "MATCH (this)<-[:HAS_BASE]-(n:BaseObject) RETURN n.id as res", columnName: "res") baseObject: BaseObject! @relationship(type: "HAS_BASE", direction: IN) @@ -61,7 +61,7 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { @node @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @unique + id: ID! nameDetails: NameDetails @relationship(type: "HAS_NAME", direction: OUT) } @@ -69,7 +69,7 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { @node @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @id @unique + id: ID! @id } `; diff --git a/packages/graphql/tests/tck/issues/1783.test.ts b/packages/graphql/tests/tck/issues/1783.test.ts index 0b37de1ceb..1df8719134 100644 --- a/packages/graphql/tests/tck/issues/1783.test.ts +++ b/packages/graphql/tests/tck/issues/1783.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Series @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [MasterData!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { } type MasterData @node { - id: ID! @unique + id: ID! current: Boolean! nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/1848.test.ts b/packages/graphql/tests/tck/issues/1848.test.ts index 6523ae6cd5..18429b7f4c 100644 --- a/packages/graphql/tests/tck/issues/1848.test.ts +++ b/packages/graphql/tests/tck/issues/1848.test.ts @@ -27,17 +27,17 @@ describe("https://github.com/neo4j/graphql/issues/1848", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type ContentPiece @node(labels: ["ContentPiece", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type Project @node(labels: ["Project", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type Community @node(labels: ["Community", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int hasContentPieces: [ContentPiece!]! @relationship(type: "COMMUNITY_CONTENTPIECE_HASCONTENTPIECES", direction: OUT) diff --git a/packages/graphql/tests/tck/issues/1933.test.ts b/packages/graphql/tests/tck/issues/1933.test.ts index b47941853f..3e15ca970d 100644 --- a/packages/graphql/tests/tck/issues/1933.test.ts +++ b/packages/graphql/tests/tck/issues/1933.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Employee @node { - employeeId: ID! @unique + employeeId: ID! firstName: String! @settable(onCreate: false, onUpdate: false) lastName: String @settable(onCreate: false, onUpdate: false) projects: [Project!]! @@ -39,7 +39,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } type Project @node { - projectId: ID! @unique + projectId: ID! name: String! @settable(onCreate: false, onUpdate: false) description: String employees: [Employee!]! diff --git a/packages/graphql/tests/tck/issues/2022.test.ts b/packages/graphql/tests/tck/issues/2022.test.ts index 618b2bbb55..232f019026 100644 --- a/packages/graphql/tests/tck/issues/2022.test.ts +++ b/packages/graphql/tests/tck/issues/2022.test.ts @@ -27,14 +27,14 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type ArtPiece @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! auction: AuctionItem! @relationship(type: "SOLD_AT_AUCTION_AS", direction: OUT) owner: Organization! @relationship(type: "OWNED_BY", direction: OUT) } type AuctionItem @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") auctionName: String! lotNumber: Int! @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { } type Organization @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! artCollection: [ArtPiece!]! @relationship(type: "OWNED_BY", direction: IN) diff --git a/packages/graphql/tests/tck/issues/2100.test.ts b/packages/graphql/tests/tck/issues/2100.test.ts index d9cc8d04d9..16e61e0c08 100644 --- a/packages/graphql/tests/tck/issues/2100.test.ts +++ b/packages/graphql/tests/tck/issues/2100.test.ts @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { } type Bacenta implements Church @node { - id: ID @id @unique + id: ID @id name: String! serviceLogs: [ServiceLog!]! @relationship(type: "HAS_HISTORY", direction: OUT) bussing(limit: Int!): [BussingRecord!]! diff --git a/packages/graphql/tests/tck/issues/2189.test.ts b/packages/graphql/tests/tck/issues/2189.test.ts index 0ff354e024..54ff9101ce 100644 --- a/packages/graphql/tests/tck/issues/2189.test.ts +++ b/packages/graphql/tests/tck/issues/2189.test.ts @@ -26,7 +26,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { beforeEach(() => { const typeDefs = ` type Test_Item @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { ) } type Test_Feedback @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean diff --git a/packages/graphql/tests/tck/issues/2249.test.ts b/packages/graphql/tests/tck/issues/2249.test.ts index a110cb03a4..10a9ad751d 100644 --- a/packages/graphql/tests/tck/issues/2249.test.ts +++ b/packages/graphql/tests/tck/issues/2249.test.ts @@ -38,8 +38,8 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { type Person implements Reviewer @node { name: String! reputation: Int! - id: Int @unique - reviewerId: Int @unique + id: Int + reviewerId: Int } type Influencer implements Reviewer @node { diff --git a/packages/graphql/tests/tck/issues/2396.test.ts b/packages/graphql/tests/tck/issues/2396.test.ts index 6897fefa6f..005580f7da 100644 --- a/packages/graphql/tests/tck/issues/2396.test.ts +++ b/packages/graphql/tests/tck/issues/2396.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { typeDefs = /* GraphQL */ ` type PostalCode @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! address: [Address!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } @@ -40,11 +40,11 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type Address @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - address: String! @unique + address: String! streetNumber: String route: String! @coalesce(value: "") postalCode: PostalCode! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) @@ -61,7 +61,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type Mandate @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -74,7 +74,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type Valuation @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -100,7 +100,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type Estate @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) diff --git a/packages/graphql/tests/tck/issues/2437.test.ts b/packages/graphql/tests/tck/issues/2437.test.ts index e06a472ed7..f4a4fd3a47 100644 --- a/packages/graphql/tests/tck/issues/2437.test.ts +++ b/packages/graphql/tests/tck/issues/2437.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { } type Agent @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime valuations: [Valuation!]! @relationship(type: "IS_VALUATION_AGENT", direction: OUT) @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { ) type Valuation @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime agent: Agent! @relationship(type: "IS_VALUATION_AGENT", direction: IN) diff --git a/packages/graphql/tests/tck/issues/2581.test.ts b/packages/graphql/tests/tck/issues/2581.test.ts index 105dabc857..b2c768c9eb 100644 --- a/packages/graphql/tests/tck/issues/2581.test.ts +++ b/packages/graphql/tests/tck/issues/2581.test.ts @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { type Book @node { name: String! year: Int - refID: ID @id @unique + refID: ID @id soldCopies: Int @cypher( statement: "OPTIONAL MATCH(sales:Sales) WHERE this.refID = sales.refID WITH count(sales) as result RETURN result as result" diff --git a/packages/graphql/tests/tck/issues/2812.test.ts b/packages/graphql/tests/tck/issues/2812.test.ts index c2d125bc95..f5a09ca9c3 100644 --- a/packages/graphql/tests/tck/issues/2812.test.ts +++ b/packages/graphql/tests/tck/issues/2812.test.ts @@ -31,7 +31,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { type Actor @authorization(validate: [{ when: [BEFORE], where: { node: { nodeCreatedBy_EQ: "$jwt.sub" } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String nodeCreatedBy: String fieldA: String diff --git a/packages/graphql/tests/tck/issues/2871.test.ts b/packages/graphql/tests/tck/issues/2871.test.ts index a43c8fbd95..9a18ef6c9d 100644 --- a/packages/graphql/tests/tck/issues/2871.test.ts +++ b/packages/graphql/tests/tck/issues/2871.test.ts @@ -25,19 +25,19 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { const typeDefs = /* GraphQL */ ` type FirstLevel @node { - id: ID! @id @unique + id: ID! @id secondLevel: SecondLevel! @relationship(type: "HAS_SECOND_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type SecondLevel @node { - id: ID! @id @unique + id: ID! @id thirdLevel: [ThirdLevel!]! @relationship(type: "HAS_THIRD_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type ThirdLevel @node { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) } `; diff --git a/packages/graphql/tests/tck/issues/3251.test.ts b/packages/graphql/tests/tck/issues/3251.test.ts index f0efc5d4df..8bb267dc67 100644 --- a/packages/graphql/tests/tck/issues/3251.test.ts +++ b/packages/graphql/tests/tck/issues/3251.test.ts @@ -31,7 +31,7 @@ describe("https://github.com/neo4j/graphql/issues/3251", () => { } type Genre @node { - name: String! @unique + name: String! movies: [Movie!]! @relationship(type: "HAS_GENRE", direction: IN) } `; diff --git a/packages/graphql/tests/tck/issues/4095.test.ts b/packages/graphql/tests/tck/issues/4095.test.ts index c1a526b25e..c1bc82ec35 100644 --- a/packages/graphql/tests/tck/issues/4095.test.ts +++ b/packages/graphql/tests/tck/issues/4095.test.ts @@ -29,17 +29,17 @@ describe("https://github.com/neo4j/graphql/issues/4095", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) creator: User! @relationship(type: "CREATOR_OF", direction: IN) } type Person @authorization(filter: [{ where: { node: { creator: { id_EQ: "$jwt.uid" } } } }]) @node { - id: ID! @id @unique + id: ID! @id creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) family: Family! @relationship(type: "MEMBER_OF", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4115.test.ts b/packages/graphql/tests/tck/issues/4115.test.ts index 590004722c..41f344b19b 100644 --- a/packages/graphql/tests/tck/issues/4115.test.ts +++ b/packages/graphql/tests/tck/issues/4115.test.ts @@ -29,12 +29,12 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! roles: [String!]! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) creator: User! @relationship(type: "CREATOR_OF", direction: IN) } @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { } ] ) { - id: ID! @id @unique + id: ID! @id creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) family: Family! @relationship(type: "MEMBER_OF", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4116.test.ts b/packages/graphql/tests/tck/issues/4116.test.ts index 685647f78e..0ac3c963e8 100644 --- a/packages/graphql/tests/tck/issues/4116.test.ts +++ b/packages/graphql/tests/tck/issues/4116.test.ts @@ -29,12 +29,12 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! roles: [String!]! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) creator: User! @relationship(type: "CREATOR_OF", direction: IN) } @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { @authorization( filter: [{ where: { node: { family: { creator: { roles_INCLUDES: "plan:paid" } } } } }] ) { - id: ID! @id @unique + id: ID! @id creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) family: Family! @relationship(type: "MEMBER_OF", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4118.test.ts b/packages/graphql/tests/tck/issues/4118.test.ts index 01bfa29f5c..46ace9feaa 100644 --- a/packages/graphql/tests/tck/issues/4118.test.ts +++ b/packages/graphql/tests/tck/issues/4118.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4170.test.ts b/packages/graphql/tests/tck/issues/4170.test.ts index 1681a647ff..b1b1ed2281 100644 --- a/packages/graphql/tests/tck/issues/4170.test.ts +++ b/packages/graphql/tests/tck/issues/4170.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { roles: [String] } type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4214.test.ts b/packages/graphql/tests/tck/issues/4214.test.ts index 7d71b4fede..2adfad14f8 100644 --- a/packages/graphql/tests/tck/issues/4214.test.ts +++ b/packages/graphql/tests/tck/issues/4214.test.ts @@ -34,21 +34,21 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { } type User @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! store: Store @relationship(type: "WORKS_AT", direction: OUT) } type Store @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [User!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [Transaction!]! @relationship(type: "TRANSACTION", direction: IN) } type Transaction @node { - id: ID! @id @unique + id: ID! @id store: Store! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [TransactionItem!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) diff --git a/packages/graphql/tests/tck/issues/4223.test.ts b/packages/graphql/tests/tck/issues/4223.test.ts index db8e824bab..3c639f5993 100644 --- a/packages/graphql/tests/tck/issues/4223.test.ts +++ b/packages/graphql/tests/tck/issues/4223.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { roles: [String] } type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/4292.test.ts b/packages/graphql/tests/tck/issues/4292.test.ts index a579669055..08079ac838 100644 --- a/packages/graphql/tests/tck/issues/4292.test.ts +++ b/packages/graphql/tests/tck/issues/4292.test.ts @@ -25,8 +25,8 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { test("authorization subqueries should be wrapped in a Cypher.CALL", async () => { const typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique - email: String! @unique + id: ID! + email: String! name: String creator: [Group!]! @relationship(type: "CREATOR_OF", direction: OUT) admin: [Admin!]! @relationship(type: "IS_USER", direction: IN) @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type Group @node { - id: ID! @id @unique + id: ID! @id name: String members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) creator: User! @@ -78,7 +78,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } ] ) { - id: ID! @id @unique + id: ID! @id name: String! creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) @@ -115,7 +115,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type Admin implements Invitee @node { - id: ID! @unique @id + id: ID! @id group: Group! @relationship(type: "ADMIN_OF", direction: OUT) creator: User! @relationship(type: "CREATOR_OF", direction: IN) email: String! @@ -126,7 +126,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type Contributor implements Invitee @node { - id: ID! @unique @id + id: ID! @id group: Group! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) creator: User! @relationship(type: "CREATOR_OF", direction: IN) email: String! diff --git a/packages/graphql/tests/tck/issues/4429.test.ts b/packages/graphql/tests/tck/issues/4429.test.ts index 73d498d7cb..131a6d3338 100644 --- a/packages/graphql/tests/tck/issues/4429.test.ts +++ b/packages/graphql/tests/tck/issues/4429.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/4429", () => { roles: [String] } type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } diff --git a/packages/graphql/tests/tck/issues/488.test.ts b/packages/graphql/tests/tck/issues/488.test.ts index dc87541b8f..d503f027c9 100644 --- a/packages/graphql/tests/tck/issues/488.test.ts +++ b/packages/graphql/tests/tck/issues/488.test.ts @@ -34,17 +34,17 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { union Keyword = Emoji | Hashtag | Text type Emoji @node { - id: ID! @id @unique + id: ID! @id type: String! } type Hashtag @node { - id: ID! @id @unique + id: ID! @id type: String! } type Text @node { - id: ID! @id @unique + id: ID! @id type: String! } `; diff --git a/packages/graphql/tests/tck/issues/5023.test.ts b/packages/graphql/tests/tck/issues/5023.test.ts index eb1b536a75..126b99035f 100644 --- a/packages/graphql/tests/tck/issues/5023.test.ts +++ b/packages/graphql/tests/tck/issues/5023.test.ts @@ -31,7 +31,7 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { id: String } type User @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } diff --git a/packages/graphql/tests/tck/issues/5066.test.ts b/packages/graphql/tests/tck/issues/5066.test.ts index 215d70cc54..df7e4bffef 100644 --- a/packages/graphql/tests/tck/issues/5066.test.ts +++ b/packages/graphql/tests/tck/issues/5066.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { @node(labels: ["AdminGroup"]) @mutation(operations: []) @authorization(filter: [{ where: { node: { createdBy: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private createdBy: User! @@ -46,10 +46,10 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { @authorization( filter: [{ where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }] ) { - id: ID! @unique @settable(onCreate: true, onUpdate: false) + id: ID! @settable(onCreate: true, onUpdate: false) createdAt: DateTime! @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - username: String! @unique + username: String! blockedUsers: [UserBlockedUser!]! @relationship(type: "HAS_BLOCKED", direction: OUT) createdAdminGroups: [AdminGroup!]! @relationship(type: "CREATED_ADMIN_GROUP", direction: OUT) } @@ -59,7 +59,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { @query(read: false, aggregate: false) @mutation(operations: []) @authorization(filter: [{ where: { node: { from: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private from: User! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) @@ -83,7 +83,7 @@ describe("https://github.com/neo4j/graphql/issues/5066", () => { } ] ) { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) @private updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private createdBy: PartyCreator! diff --git a/packages/graphql/tests/tck/issues/5080.test.ts b/packages/graphql/tests/tck/issues/5080.test.ts index 5028cb872b..b1f3068e88 100644 --- a/packages/graphql/tests/tck/issues/5080.test.ts +++ b/packages/graphql/tests/tck/issues/5080.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { id: String } type User @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } diff --git a/packages/graphql/tests/tck/issues/5270.test.ts b/packages/graphql/tests/tck/issues/5270.test.ts index 20328bec43..a4ca383647 100644 --- a/packages/graphql/tests/tck/issues/5270.test.ts +++ b/packages/graphql/tests/tck/issues/5270.test.ts @@ -33,14 +33,14 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { @authorization( filter: [{ where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [UserBlockedUser!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } type UserBlockedUser @node(labels: ["UserBlockedUser"]) @authorization(filter: [{ where: { node: { from: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique + id: ID! @id from: User! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) to: User! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) } diff --git a/packages/graphql/tests/tck/issues/5497.test.ts b/packages/graphql/tests/tck/issues/5497.test.ts index f772679136..3e18605c33 100644 --- a/packages/graphql/tests/tck/issues/5497.test.ts +++ b/packages/graphql/tests/tck/issues/5497.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/5497", () => { } type File @node { - id: ID! @unique + id: ID! category: Category @relationship(type: "HAS_FILE", direction: IN) } `; diff --git a/packages/graphql/tests/tck/issues/5515.test.ts b/packages/graphql/tests/tck/issues/5515.test.ts index f46f4b3921..8f10f4f518 100644 --- a/packages/graphql/tests/tck/issues/5515.test.ts +++ b/packages/graphql/tests/tck/issues/5515.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { } type File @node { - id: ID! @unique + id: ID! category: Category @relationship(type: "HAS_FILE", direction: IN) } `; diff --git a/packages/graphql/tests/tck/issues/5635.test.ts b/packages/graphql/tests/tck/issues/5635.test.ts deleted file mode 100644 index b16170f28f..0000000000 --- a/packages/graphql/tests/tck/issues/5635.test.ts +++ /dev/null @@ -1,121 +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 { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/5635", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Owner { - id: ID! @unique @id - owns: [MyNode!]! @relationship(type: "OWNS", direction: OUT) - } - - type MyNode - @authorization( - validate: [ - { - operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] - where: { node: { owner: { id: "$jwt.sub" } } } - when: [AFTER] - } - ] - ) { - id: ID! @unique @id - name: String! - owner: Owner! @relationship(type: "OWNS", direction: IN) - } - `; - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { key: "secret" }, - }, - }); - }); - - test("validation should applied correctly without causing cypher errors", async () => { - const mutation = /* GraphQL */ ` - mutation { - createMyNodes( - input: [ - { - name: "Test" - owner: { connectOrCreate: { onCreate: { node: {} }, where: { node: { id: "abc" } } } } - } - ] - ) { - myNodes { - id - name - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:MyNode) - SET this0.id = randomUUID() - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_owner_connectOrCreate0:Owner { id: $this0_owner_connectOrCreate_param0 }) - MERGE (this0)<-[this0_owner_connectOrCreate_this0:OWNS]-(this0_owner_connectOrCreate0) - WITH * - OPTIONAL MATCH (this0)<-[:OWNS]-(this0_owner_connectOrCreate_this1:Owner) - WITH *, count(this0_owner_connectOrCreate_this1) AS ownerCount - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (ownerCount <> 0 AND ($jwt.sub IS NOT NULL AND this0_owner_connectOrCreate_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_owner_Owner_unique:OWNS]-(:Owner) - WITH count(this0_owner_Owner_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMyNode.owner required exactly once', [0]) - RETURN c AS this0_owner_Owner_unique_ignored - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .id, .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Test\\", - \\"this0_owner_connectOrCreate_param0\\": \\"abc\\", - \\"isAuthenticated\\": false, - \\"jwt\\": {}, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/601.test.ts b/packages/graphql/tests/tck/issues/601.test.ts index dbae17d807..3debab91f0 100644 --- a/packages/graphql/tests/tck/issues/601.test.ts +++ b/packages/graphql/tests/tck/issues/601.test.ts @@ -37,7 +37,7 @@ describe("#601", () => { } type Document @mutation(operations: []) @node { - id: ID! @id @unique + id: ID! @id stakeholder: Stakeholder! @relationship(type: "REQUIRES", direction: OUT) customerContact: CustomerContact! diff --git a/packages/graphql/tests/tck/issues/832.test.ts b/packages/graphql/tests/tck/issues/832.test.ts index 61d3c3ec67..841485e2ec 100644 --- a/packages/graphql/tests/tck/issues/832.test.ts +++ b/packages/graphql/tests/tck/issues/832.test.ts @@ -31,17 +31,17 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { } type Person implements Entity @node { - id: String! @unique + id: String! name: String! } type Place implements Entity @node { - id: String! @unique + id: String! location: Point! } type Interaction @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/tck/issues/847.test.ts b/packages/graphql/tests/tck/issues/847.test.ts index 5bc89d2de4..a9bf93b340 100644 --- a/packages/graphql/tests/tck/issues/847.test.ts +++ b/packages/graphql/tests/tck/issues/847.test.ts @@ -28,17 +28,17 @@ describe("https://github.com/neo4j/graphql/issues/847", () => { } type Person implements Entity @node { - id: String! @unique + id: String! name: String! } type Place implements Entity @node { - id: String! @unique + id: String! location: Point! } type Interaction @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/tck/issues/894.test.ts b/packages/graphql/tests/tck/issues/894.test.ts index a78f233ec9..69fb204d87 100644 --- a/packages/graphql/tests/tck/issues/894.test.ts +++ b/packages/graphql/tests/tck/issues/894.test.ts @@ -27,13 +27,13 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! activeOrganization: Organization @relationship(type: "ACTIVELY_MANAGING", direction: OUT) } type Organization @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! } `; diff --git a/packages/graphql/tests/tck/issues/901.test.ts b/packages/graphql/tests/tck/issues/901.test.ts index e41c43775c..6a1bcfed55 100644 --- a/packages/graphql/tests/tck/issues/901.test.ts +++ b/packages/graphql/tests/tck/issues/901.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/901", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Series @node { - id: ID! @id @unique + id: ID! @id name: String! brand: Series @relationship(type: "HAS_BRAND", direction: OUT, properties: "Properties") manufacturer: Series @relationship(type: "HAS_MANUFACTURER", direction: OUT, properties: "Properties") diff --git a/packages/graphql/tests/tck/math.test.ts b/packages/graphql/tests/tck/math.test.ts index 4cbb46dd89..42e4ef46b9 100644 --- a/packages/graphql/tests/tck/math.test.ts +++ b/packages/graphql/tests/tck/math.test.ts @@ -36,7 +36,7 @@ describe("Math operators", () => { } type Movie @node { - id: ID! @id @unique + id: ID! @id title: String! viewers: Int revenue: Float diff --git a/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts index de4ff84407..95034fced4 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts @@ -32,7 +32,7 @@ describe("Batch Create, Auth", () => { } type Actor @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") @@ -242,295 +242,4 @@ describe("Batch Create, Auth", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const token = createBearerToken("secret", { sub: "1" }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this3_actors_connect0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this4.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this11.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this18.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this25.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this32.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [], - \\"sub\\": \\"1\\" - }, - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"authorization_0_after_param2\\": \\"admin\\", - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts index 9e1d895c35..7806568eda 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts @@ -27,7 +27,7 @@ describe("Batch Create, Scalar types", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Actor @node { - id: ID! @id @unique + id: ID! @id name: String born: Date createdAt: DateTime @timestamp(operations: [CREATE]) @@ -379,281 +379,4 @@ describe("Batch Create, Scalar types", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.createdAt = datetime() - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.createdAt = datetime() - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.createdAt = datetime() - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.createdAt = datetime() - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.createdAt = datetime() - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.createdAt = datetime() - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.createdAt = datetime() - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.createdAt = datetime(), - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/operations/batch/batch-create.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create.test.ts index 0d1f7aa0c3..53df69a459 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create.test.ts @@ -27,7 +27,7 @@ describe("Batch Create", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Actor @node { - id: ID! @id @unique + id: ID! @id name: String website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") @@ -464,273 +464,4 @@ describe("Batch Create", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts b/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts index 855493aa5d..30b2983539 100644 --- a/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts +++ b/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts @@ -205,14 +205,14 @@ describe("Undirected relationships", () => { test("nested undirected relationship", async () => { typeDefs = /* GraphQL */ ` type Foo @node { - id: ID @unique + id: ID Name: String Age: Int DrinksAt: Bar @relationship(type: "DRINKS_AT", direction: OUT) } type Bar @node { - id: ID @unique + id: ID Adress: String Customers: [Foo!]! @relationship(type: "DRINKS_AT", direction: IN) }