UNPKG

@neo4j/graphql

Version:

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

166 lines 6.82 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.Executor = void 0; const debug_1 = __importDefault(require("debug")); const neo4j_driver_1 = require("neo4j-driver"); const constants_1 = require("../constants"); const debug_cypher_and_params_1 = require("../debug/debug-cypher-and-params"); const is_in_array_1 = require("../utils/is-in-array"); const Error_1 = require("./Error"); const debug = (0, debug_1.default)(constants_1.DEBUG_EXECUTE); const SUPPORTED_CYPHER_VERSION = "5"; function isDriverLike(executionContext) { return typeof executionContext.session === "function"; } function isSessionLike(executionContext) { return typeof executionContext.beginTransaction === "function"; } class Executor { constructor({ executionContext, cypherQueryOptions, sessionConfig, cypherParams = {}, transactionMetadata = {}, }) { this.executionContext = executionContext; this.cypherQueryOptions = cypherQueryOptions; this.sessionConfig = sessionConfig; this.cypherParams = cypherParams; this.transactionMetadata = transactionMetadata; } async execute(query, parameters, sessionMode, info) { const params = { ...parameters, ...this.cypherParams }; try { if (isDriverLike(this.executionContext)) { return await this.driverRun({ query, parameters: params, driver: this.executionContext, sessionMode, info, }); } if (isSessionLike(this.executionContext)) { return await this.sessionRun({ query, parameters: params, sessionMode, session: this.executionContext, }); } return await this.transactionRun(query, params, this.executionContext); } catch (error) { throw this.formatError(error); } } formatError(error) { if (error instanceof neo4j_driver_1.Neo4jError) { if (error.message.includes(`Caused by: java.lang.RuntimeException: ${constants_1.AUTH_FORBIDDEN_ERROR}`)) { return new Error_1.Neo4jGraphQLForbiddenError("Forbidden"); } if (error.message.includes(`Caused by: java.lang.RuntimeException: ${constants_1.AUTH_UNAUTHENTICATED_ERROR}`)) { return new Error_1.Neo4jGraphQLAuthenticationError("Unauthenticated"); } if (error.message.includes(`Caused by: java.lang.RuntimeException: ${constants_1.RELATIONSHIP_REQUIREMENT_PREFIX}`)) { const [, message] = error.message.split(constants_1.RELATIONSHIP_REQUIREMENT_PREFIX); return new Error_1.Neo4jGraphQLRelationshipValidationError(message || ""); } if (error.code === "Neo.ClientError.Schema.ConstraintValidationFailed") { return new Error_1.Neo4jGraphQLConstraintValidationError("Constraint validation failed"); } } debug("%s", error); return error; } addCypherOptionsToQuery(query) { const cypherVersion = this.getCypherVersionStatement(); const cypherQueryOptions = this.getCypherQueryOptionsStatement(); return `${cypherVersion}${cypherQueryOptions}${query}`; } getCypherVersionStatement() { const addVersionPrefixDefault = true; if (this.cypherQueryOptions?.addVersionPrefix ?? addVersionPrefixDefault) { return `CYPHER ${SUPPORTED_CYPHER_VERSION}\n`; } return ""; } getCypherQueryOptionsStatement() { const ignoredCypherQueryOptions = ["addVersionPrefix"]; const cypherQueryOptions = Object.entries(this.cypherQueryOptions ?? []).filter(([key, _value]) => { return !(0, is_in_array_1.isInArray)(ignoredCypherQueryOptions, key); }); if (cypherQueryOptions.length) { return `CYPHER ${cypherQueryOptions .map(([key, value]) => { return `${key}=${value}`; }) .join(" ")}\n`; } return ""; } getTransactionConfig() { return { metadata: { app: constants_1.APP_ID, ...this.transactionMetadata, type: "user-transpiled", }, }; } async driverRun({ query, parameters, driver, sessionMode, info, }) { const session = driver.session({ // Always specify a default database to avoid requests for routing table database: "neo4j", ...this.sessionConfig, bookmarkManager: driver.executeQueryBookmarkManager, defaultAccessMode: sessionMode, }); try { const result = await this.sessionRun({ query, parameters, session, sessionMode }); return result; } finally { await session.close(); } } async sessionRun({ query, parameters, session, sessionMode, }) { let result; switch (sessionMode) { case "READ": result = await session.executeRead((tx) => { return this.transactionRun(query, parameters, tx); }, this.getTransactionConfig()); break; case "WRITE": result = await session.executeWrite((tx) => { return this.transactionRun(query, parameters, tx); }, this.getTransactionConfig()); break; } return result; } transactionRun(query, parameters, transaction) { const queryToRun = this.addCypherOptionsToQuery(query); (0, debug_cypher_and_params_1.debugCypherAndParams)(debug, queryToRun, parameters); return transaction.run(queryToRun, parameters); } } exports.Executor = Executor; //# sourceMappingURL=Executor.js.map