@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
254 lines • 13.5 kB
JavaScript
;
/*
* 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