UNPKG

@neo4j/graphql

Version:

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations

214 lines 9.73 kB
"use strict"; /* * 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. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConnectFactory = void 0; const cypher_builder_1 = __importDefault(require("@neo4j/cypher-builder")); const utils_1 = require("../../../../utils/utils"); const MutationOperationField_1 = require("../../ast/input-fields/MutationOperationField"); const ParamInputField_1 = require("../../ast/input-fields/ParamInputField"); const CompositeConnectOperation_1 = require("../../ast/operations/composite/CompositeConnectOperation"); const CompositeConnectPartial_1 = require("../../ast/operations/composite/CompositeConnectPartial"); const ConnectOperation_1 = require("../../ast/operations/ConnectOperation"); const NodeSelectionPattern_1 = require("../../ast/selection/SelectionPattern/NodeSelectionPattern"); const is_concrete_entity_1 = require("../../utils/is-concrete-entity"); const is_interface_entity_1 = require("../../utils/is-interface-entity"); const is_union_entity_1 = require("../../utils/is-union-entity"); const raise_attribute_ambiguity_1 = require("../../utils/raise-attribute-ambiguity"); class ConnectFactory { constructor(queryASTFactory) { this.queryASTFactory = queryASTFactory; } createConnectOperation(entity, relationship, input, context, callbackBucket) { const connectOP = new ConnectOperation_1.ConnectOperation({ target: entity, selectionPattern: new NodeSelectionPattern_1.NodeSelectionPattern({ target: entity, }), relationship, }); this.hydrateConnectOperation({ target: entity, relationship, input, connect: connectOP, context, callbackBucket, }); return connectOP; } createCompositeConnectOperation(entity, relationship, input, context, callbackBucket) { const partials = []; for (const concreteEntity of entity.concreteEntities) { const partial = this.createCompositeConnectPartial(concreteEntity, relationship, input, context, callbackBucket); partials.push(partial); } return new CompositeConnectOperation_1.CompositeConnectOperation({ partials, target: entity, }); } createCompositeConnectPartial(entity, relationship, input, context, callbackBucket) { const connectOP = new CompositeConnectPartial_1.CompositeConnectPartial({ target: entity, selectionPattern: new NodeSelectionPattern_1.NodeSelectionPattern({ target: entity, }), relationship, }); this.hydrateConnectOperation({ target: entity, relationship, input, connect: connectOP, context, callbackBucket, }); return connectOP; } hydrateConnectOperation({ target, relationship, input, connect, context, callbackBucket, }) { this.addEntityAuthorization({ entity: target, context, operation: connect, }); if ((0, is_concrete_entity_1.isConcreteEntity)(relationship.source)) { this.addSourceEntityAuthorization({ entity: relationship.source, context, operation: connect, }); } (0, utils_1.asArray)(input).forEach((inputItem) => { const { whereArg, connectArg } = this.parseConnectArgs(inputItem); const nodeFilters = []; if (whereArg.node) { if ((0, is_concrete_entity_1.isConcreteEntity)(relationship.target) || (0, is_union_entity_1.isUnionEntity)(relationship.target)) { nodeFilters.push(...this.queryASTFactory.filterFactory.createNodeFilters(target, whereArg.node)); } else if ((0, is_interface_entity_1.isInterfaceEntity)(relationship.target)) { nodeFilters.push(...this.queryASTFactory.filterFactory.createInterfaceNodeFilters({ entity: relationship.target, targetEntity: target, whereFields: whereArg.node, relationship, })); } } connect.addFilters(...nodeFilters); (0, utils_1.asArray)(connectArg).forEach((nestedConnectInputFields) => { Object.entries(nestedConnectInputFields).forEach(([key, value]) => { const nestedRelationship = target.relationships.get(key); if (!nestedRelationship) { throw new Error("Expected relationship on connect operation. Please contact support"); } const nestedEntity = nestedRelationship.target; (0, utils_1.asArray)(value).forEach((nestedConnectInputItem) => { const nestedConnectOperation = this.queryASTFactory.operationsFactory.createConnectOperation(nestedEntity, nestedRelationship, nestedConnectInputItem, context, callbackBucket); const mutationOperationField = new MutationOperationField_1.MutationOperationField(nestedConnectOperation, key); connect.addField(mutationOperationField, "node"); }); }); }); const targetInputEdge = this.getInputEdge(inputItem, relationship); /* Create the attributes for the edge */ (0, raise_attribute_ambiguity_1.raiseAttributeAmbiguity)(Object.keys(targetInputEdge), relationship); for (const key of Object.keys(targetInputEdge)) { const attribute = relationship.attributes.get(key); if (attribute) { const attachedTo = "relationship"; const paramInputField = new ParamInputField_1.ParamInputField({ attachedTo, attribute, inputValue: targetInputEdge[key], }); connect.addField(paramInputField, attachedTo); } } }); this.addPopulatedByFieldToConnect({ connect, input, callbackBucket, relationship, }); } addPopulatedByFieldToConnect({ connect, input, callbackBucket, relationship, }) { // Using create here because the relationship is created on connect relationship?.getPopulatedByFields("CREATE").forEach((attribute) => { const attachedTo = "relationship"; // the param value it's irrelevant as it will be overwritten by the callback function const relCallbackParam = new cypher_builder_1.default.Param(""); const relField = new ParamInputField_1.ParamInputField({ attribute, attachedTo, inputValue: relCallbackParam, }); connect.addField(relField, "relationship"); const callbackFunctionName = attribute.annotations.populatedBy?.callback; if (!callbackFunctionName) { throw new Error(`PopulatedBy callback not found for attribute ${attribute.name}`); } callbackBucket.addCallback({ functionName: callbackFunctionName, param: relCallbackParam, parent: input.edge, type: attribute.type, operation: "UPDATE", }); }); } addEntityAuthorization({ entity, context, operation, }) { const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["CREATE_RELATIONSHIP"], context, afterValidation: true, }); operation.addAuthFilters(...authFilters); } addSourceEntityAuthorization({ entity, context, operation, }) { const authFilters = this.queryASTFactory.authorizationFactory.getAuthFilters({ entity, operations: ["CREATE_RELATIONSHIP"], context, afterValidation: true, }); operation.addSourceAuthFilters(...authFilters); } getInputEdge(inputItem, relationship) { const edge = inputItem.edge ?? {}; // Deals with composite relationships if (relationship.propertiesTypeName && edge[relationship.propertiesTypeName]) { return edge[relationship.propertiesTypeName]; } return edge; } parseConnectArgs(args) { const rawWhere = args.where ?? {}; const whereArg = { node: rawWhere.node, edge: {} }; const connectArg = args.connect ?? {}; return { whereArg, connectArg }; } } exports.ConnectFactory = ConnectFactory; //# sourceMappingURL=ConnectFactory.js.map