UNPKG

@neo4j/graphql

Version:

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

254 lines 13.5 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 }); const get_relationship_type_1 = require("../utils/get-relationship-type"); const map_to_db_property_1 = __importDefault(require("../utils/map-to-db-property")); const check_authentication_1 = require("./authorization/check-authentication"); const create_authorization_after_and_params_1 = require("./authorization/compatibility/create-authorization-after-and-params"); const create_connect_and_params_1 = __importDefault(require("./create-connect-and-params")); const create_set_relationship_properties_1 = require("./create-set-relationship-properties"); const assert_non_ambiguous_update_1 = require("./utils/assert-non-ambiguous-update"); const callback_utils_1 = require("./utils/callback-utils"); function createCreateAndParams({ input, varName, node, context, callbackBucket, withVars, topLevelNodeVariable, authorizationPrefix = [0], }) { (0, assert_non_ambiguous_update_1.assertNonAmbiguousUpdate)(node, input); (0, check_authentication_1.checkAuthentication)({ context, node, targetOperations: ["CREATE"] }); function reducer(res, [key, value], reducerIndex) { const varNameKey = `${varName}_${key}`; const relationField = node.relationFields.find((x) => key === x.fieldName); const primitiveField = node.primitiveFields.find((x) => key === x.fieldName); const pointField = node.pointFields.find((x) => key === x.fieldName); const temporalField = node.temporalFields.find((x) => key === x.fieldName); const dbFieldName = (0, map_to_db_property_1.default)(node, key); if (primitiveField) { (0, check_authentication_1.checkAuthentication)({ context, node, targetOperations: ["CREATE"], field: primitiveField.fieldName }); } if (relationField) { const refNodes = []; if (relationField.union) { Object.keys(value).forEach((unionTypeName) => { refNodes.push(context.nodes.find((x) => x.name === unionTypeName)); }); } else if (relationField.interface) { relationField.interface?.implementations?.forEach((implementationName) => { refNodes.push(context.nodes.find((x) => x.name === implementationName)); }); } else { refNodes.push(context.nodes.find((x) => x.name === relationField.typeMeta.name)); } refNodes.forEach((refNode, refNodeIndex) => { const v = relationField.union ? value[refNode.name] : value; const unionTypeName = relationField.union || relationField.interface ? refNode.name : ""; if (v.create) { const isInterfaceAnArray = relationField.interface?.typeMeta.array; const createNodeInputIsOfTypeRefNode = !!v.create.node?.[refNode.name]; const createNodeInputKeys = createNodeInputIsOfTypeRefNode ? Object.keys(v.create.node || []) : []; const isCreatingMultipleNodesForOneToOneRel = !isInterfaceAnArray && createNodeInputKeys.length > 1; if (isCreatingMultipleNodesForOneToOneRel) { throw new Error(`Relationship field "${relationField.connectionPrefix}.${relationField.interface?.dbPropertyName || relationField.interface?.fieldName}" cannot have more than one node linked`); } const creates = relationField.typeMeta.array ? v.create : [v.create]; creates.forEach((create, createIndex) => { if (relationField.interface && !create.node[refNode.name]) { return; } res.creates.push(`\nWITH *`); const baseName = `${varNameKey}${relationField.union ? "_" : ""}${unionTypeName}${createIndex}`; const nodeName = `${baseName}_node`; const propertiesName = `${baseName}_relationship`; const { create: nestedCreate, params, authorizationPredicates, authorizationSubqueries, } = createCreateAndParams({ input: relationField.interface ? create.node[refNode.name] : create.node, context, callbackBucket, node: refNode, varName: nodeName, withVars: [...withVars, nodeName], topLevelNodeVariable, authorizationPrefix: [...authorizationPrefix, reducerIndex, createIndex, refNodeIndex], }); res.creates.push(nestedCreate); res.params = { ...res.params, ...params }; const inStr = relationField.direction === "IN" ? "<-" : "-"; const outStr = relationField.direction === "OUT" ? "->" : "-"; const fieldType = (0, get_relationship_type_1.getRelationshipType)(relationField, context.features); const relationVarName = relationField.properties ? propertiesName : ""; const relTypeStr = `[${relationVarName}:${fieldType}]`; res.creates.push(`MERGE (${varName})${inStr}${relTypeStr}${outStr}(${nodeName})`); if (relationField.properties) { const relationship = context.relationships.find((x) => x.properties === relationField.properties); const setA = (0, create_set_relationship_properties_1.createSetRelationshipProperties)({ properties: create.edge ?? {}, varName: propertiesName, withVars, relationship, operation: "CREATE", callbackBucket, parameterPrefix: propertiesName, parameterNotation: "_", }); if (setA) { res.creates.push(setA[0]); res.params = { ...res.params, ...setA[1] }; } } if (authorizationPredicates.length) { if (authorizationSubqueries.length) { res.meta.authorizationSubqueries.push(...authorizationSubqueries); } res.meta.authorizationPredicates.push(...authorizationPredicates); } }); } if (!relationField.interface && v.connect) { const connectAndParams = (0, create_connect_and_params_1.default)({ withVars, value: v.connect, varName: `${varNameKey}${relationField.union ? "_" : ""}${unionTypeName}_connect`, parentVar: varName, relationField, context, callbackBucket, refNodes: [refNode], labelOverride: unionTypeName, parentNode: node, source: "CREATE", indexPrefix: makeAuthorizationParamsPrefix(authorizationPrefix), }); res.creates.push(connectAndParams[0]); res.params = { ...res.params, ...connectAndParams[1] }; } }); if (relationField.interface && value.connect) { const connectAndParams = (0, create_connect_and_params_1.default)({ withVars, value: value.connect, varName: `${varNameKey}${relationField.union ? "_" : ""}_connect`, parentVar: varName, relationField, context, callbackBucket, refNodes, labelOverride: "", parentNode: node, source: "CREATE", }); res.creates.push(connectAndParams[0]); res.params = { ...res.params, ...connectAndParams[1] }; } return res; } const authorizationAndParams = (0, create_authorization_after_and_params_1.createAuthorizationAfterAndParamsField)({ context, nodes: [ { variable: varName, node, fieldName: primitiveField?.fieldName, }, ], operations: ["CREATE"], indexPrefix: makeAuthorizationParamsPrefix(authorizationPrefix), }); if (authorizationAndParams) { const { cypher, params: authParams, subqueries } = authorizationAndParams; if (subqueries) { res.meta.authorizationSubqueries.push(subqueries); } res.meta.authorizationPredicates.push(cypher); res.params = { ...res.params, ...authParams }; } if (pointField) { if (pointField.typeMeta.array) { res.creates.push(`SET ${varName}.${dbFieldName} = [p in $${varNameKey} | point(p)]`); } else { res.creates.push(`SET ${varName}.${dbFieldName} = point($${varNameKey})`); } res.params[varNameKey] = value; return res; } if (temporalField && ["DateTime", "Time"].includes(temporalField.typeMeta.name)) { if (temporalField.typeMeta.array) { res.creates.push(`SET ${varName}.${dbFieldName} = [t in $${varNameKey} | ${temporalField.typeMeta.name.toLowerCase()}(t)]`); } else { res.creates.push(`SET ${varName}.${dbFieldName} = ${temporalField.typeMeta.name.toLowerCase()}($${varNameKey})`); } res.params[varNameKey] = value; return res; } res.creates.push(`SET ${varName}.${dbFieldName} = $${varNameKey}`); res.params[varNameKey] = value; return res; } const labels = node.getLabelString(context); const initial = [`CREATE (${varName}${labels})`]; const timestampedFields = node.temporalFields.filter((x) => ["DateTime", "Time"].includes(x.typeMeta.name) && x.timestamps?.includes("CREATE")); timestampedFields.forEach((field) => { // DateTime -> datetime(); Time -> time() initial.push(`SET ${varName}.${field.dbPropertyName} = ${field.typeMeta.name.toLowerCase()}()`); }); [...node.primitiveFields, ...node.temporalFields].forEach((field) => (0, callback_utils_1.addCallbackAndSetParam)(field, varName, input, callbackBucket, initial, "CREATE")); const autogeneratedIdFields = node.primitiveFields.filter((x) => x.autogenerate); autogeneratedIdFields.forEach((f) => { initial.push(`SET ${varName}.${f.dbPropertyName} = randomUUID()`); }); // eslint-disable-next-line prefer-const let { creates, params, meta } = Object.entries(input).reduce(reducer, { creates: initial, params: {}, meta: { authorizationPredicates: [], authorizationSubqueries: [], }, }); const { authorizationPredicates, authorizationSubqueries } = meta; const authorizationAndParams = (0, create_authorization_after_and_params_1.createAuthorizationAfterAndParams)({ context, nodes: [ { variable: varName, node, }, ], operations: ["CREATE"], indexPrefix: makeAuthorizationParamsPrefix(authorizationPrefix), }); if (authorizationAndParams) { const { cypher, params: authParams, subqueries } = authorizationAndParams; if (subqueries) { authorizationSubqueries.push(subqueries); } authorizationPredicates.push(cypher); params = { ...params, ...authParams }; } return { create: creates.join("\n"), params, authorizationPredicates, authorizationSubqueries }; } function makeAuthorizationParamsPrefix(authorizationPrefix) { return `${authorizationPrefix.join("_")}_`; } exports.default = createCreateAndParams; //# sourceMappingURL=create-create-and-params.js.map