461 lines
17 KiB
JavaScript
461 lines
17 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
|
|
|
|
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
|
|
|
|
var _typeof2 = require('babel-runtime/helpers/typeof');
|
|
|
|
var _typeof3 = _interopRequireDefault(_typeof2);
|
|
|
|
var _keys = require('babel-runtime/core-js/object/keys');
|
|
|
|
var _keys2 = _interopRequireDefault(_keys);
|
|
|
|
var _stringify = require('babel-runtime/core-js/json/stringify');
|
|
|
|
var _stringify2 = _interopRequireDefault(_stringify);
|
|
|
|
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
|
|
|
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
|
|
|
var _createClass2 = require('babel-runtime/helpers/createClass');
|
|
|
|
var _createClass3 = _interopRequireDefault(_createClass2);
|
|
|
|
var _integer = require('../../integer');
|
|
|
|
var _graphTypes = require('../../graph-types');
|
|
|
|
var _error = require('../../error');
|
|
|
|
var _spatialTypes = require('../../spatial-types');
|
|
|
|
var _temporalTypes = require('../../temporal-types');
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
var CREDENTIALS_EXPIRED_CODE = 'Neo.ClientError.Security.CredentialsExpired'; /**
|
|
* Copyright (c) 2002-2018 "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 HttpResponseConverter = function () {
|
|
function HttpResponseConverter() {
|
|
(0, _classCallCheck3.default)(this, HttpResponseConverter);
|
|
}
|
|
|
|
(0, _createClass3.default)(HttpResponseConverter, [{
|
|
key: 'encodeStatementParameters',
|
|
value: function encodeStatementParameters(parameters) {
|
|
return encodeQueryParameters(parameters);
|
|
}
|
|
|
|
/**
|
|
* Attempts to extract error from transactional cypher endpoint response and convert it to {@link Neo4jError}.
|
|
* @param {object} response the response.
|
|
* @return {Neo4jError|null} new driver friendly error, if exists.
|
|
*/
|
|
|
|
}, {
|
|
key: 'extractError',
|
|
value: function extractError(response) {
|
|
var errors = response.errors;
|
|
if (errors) {
|
|
var error = errors[0];
|
|
if (error) {
|
|
// endpoint returns 'Neo.ClientError.Security.Forbidden' code and 'password_change' that points to another endpoint
|
|
// this is different from code returned via Bolt and less descriptive
|
|
// make code same as in Bolt, if password change is required
|
|
var code = response.password_change ? CREDENTIALS_EXPIRED_CODE : error.code;
|
|
var message = error.message;
|
|
return new _error.Neo4jError(message, code);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Extracts transaction id from the db/data/transaction endpoint response.
|
|
* @param {object} response the response.
|
|
* @return {number} the transaction id.
|
|
*/
|
|
|
|
}, {
|
|
key: 'extractTransactionId',
|
|
value: function extractTransactionId(response) {
|
|
var commitUrl = response.commit;
|
|
if (commitUrl) {
|
|
// extract id 42 from commit url like 'http://localhost:7474/db/data/transaction/42/commit'
|
|
var url = commitUrl.replace('/commit', '');
|
|
var transactionIdString = url.substring(url.lastIndexOf('/') + 1);
|
|
var transactionId = parseInt(transactionIdString, 10);
|
|
if (transactionId || transactionId === 0) {
|
|
return transactionId;
|
|
}
|
|
}
|
|
throw new _error.Neo4jError('Unable to extract transaction id from the response JSON: ' + (0, _stringify2.default)(response));
|
|
}
|
|
|
|
/**
|
|
* Extracts record metadata (array of column names) from transactional cypher endpoint response.
|
|
* @param {object} response the response.
|
|
* @return {object} new metadata object.
|
|
*/
|
|
|
|
}, {
|
|
key: 'extractRecordMetadata',
|
|
value: function extractRecordMetadata(response) {
|
|
var result = extractResult(response);
|
|
var fields = result ? result.columns : [];
|
|
return { fields: fields };
|
|
}
|
|
|
|
/**
|
|
* Extracts raw records (each raw record is just an array of value) from transactional cypher endpoint response.
|
|
* @param {object} response the response.
|
|
* @return {object[][]} raw records from the response.
|
|
*/
|
|
|
|
}, {
|
|
key: 'extractRawRecords',
|
|
value: function extractRawRecords(response) {
|
|
var result = extractResult(response);
|
|
if (result) {
|
|
var data = result.data;
|
|
if (data) {
|
|
return data.map(function (element) {
|
|
return extractRawRecord(element);
|
|
});
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Extracts metadata for a completed statement.
|
|
* @param {object} response the response.
|
|
* @return {object} metadata as object.
|
|
*/
|
|
|
|
}, {
|
|
key: 'extractStatementMetadata',
|
|
value: function extractStatementMetadata(response) {
|
|
var result = extractResult(response);
|
|
if (result) {
|
|
var stats = result.stats;
|
|
if (stats) {
|
|
var convertedStats = (0, _keys2.default)(stats).reduce(function (newStats, key) {
|
|
if (key === 'contains_updates') {
|
|
// skip because such key does not exist in bolt
|
|
return newStats;
|
|
}
|
|
|
|
// fix key name for future parsing by StatementStatistics class
|
|
var newKey = (key === 'relationship_deleted' ? 'relationships_deleted' : key).replace('_', '-');
|
|
newStats[newKey] = stats[key];
|
|
return newStats;
|
|
}, {});
|
|
|
|
return { stats: convertedStats };
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
}]);
|
|
return HttpResponseConverter;
|
|
}();
|
|
|
|
exports.default = HttpResponseConverter;
|
|
|
|
|
|
function encodeQueryParameters(parameters) {
|
|
if (parameters && (typeof parameters === 'undefined' ? 'undefined' : (0, _typeof3.default)(parameters)) === 'object') {
|
|
return (0, _keys2.default)(parameters).reduce(function (result, key) {
|
|
result[key] = encodeQueryParameter(parameters[key]);
|
|
return result;
|
|
}, {});
|
|
}
|
|
return parameters;
|
|
}
|
|
|
|
function encodeQueryParameter(value) {
|
|
if (value instanceof _graphTypes.Node) {
|
|
throw new _error.Neo4jError('It is not allowed to pass nodes in query parameters', _error.PROTOCOL_ERROR);
|
|
} else if (value instanceof _graphTypes.Relationship) {
|
|
throw new _error.Neo4jError('It is not allowed to pass relationships in query parameters', _error.PROTOCOL_ERROR);
|
|
} else if (value instanceof _graphTypes.Path) {
|
|
throw new _error.Neo4jError('It is not allowed to pass paths in query parameters', _error.PROTOCOL_ERROR);
|
|
} else if ((0, _spatialTypes.isPoint)(value)) {
|
|
throw newUnsupportedParameterError('points');
|
|
} else if ((0, _temporalTypes.isDate)(value)) {
|
|
throw newUnsupportedParameterError('dates');
|
|
} else if ((0, _temporalTypes.isDateTime)(value)) {
|
|
throw newUnsupportedParameterError('date-time');
|
|
} else if ((0, _temporalTypes.isDuration)(value)) {
|
|
throw newUnsupportedParameterError('durations');
|
|
} else if ((0, _temporalTypes.isLocalDateTime)(value)) {
|
|
throw newUnsupportedParameterError('local date-time');
|
|
} else if ((0, _temporalTypes.isLocalTime)(value)) {
|
|
throw newUnsupportedParameterError('local time');
|
|
} else if ((0, _temporalTypes.isTime)(value)) {
|
|
throw newUnsupportedParameterError('time');
|
|
} else if ((0, _integer.isInt)(value)) {
|
|
return value.toNumber();
|
|
} else if (Array.isArray(value)) {
|
|
return value.map(function (element) {
|
|
return encodeQueryParameter(element);
|
|
});
|
|
} else if ((typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) === 'object') {
|
|
return encodeQueryParameters(value);
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
function newUnsupportedParameterError(name) {
|
|
return new _error.Neo4jError('It is not allowed to pass ' + name + ' in query parameters when using HTTP endpoint. ' + ('Please consider using Cypher functions to create ' + name + ' so that query parameters are plain objects.'), _error.PROTOCOL_ERROR);
|
|
}
|
|
|
|
function extractResult(response) {
|
|
var results = response.results;
|
|
if (results) {
|
|
var result = results[0];
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function extractRawRecord(data) {
|
|
var row = data.row;
|
|
|
|
var nodesById = indexNodesById(data);
|
|
var relationshipsById = indexRelationshipsById(data);
|
|
|
|
if (row) {
|
|
return row.map(function (ignore, index) {
|
|
return extractRawRecordElement(index, data, nodesById, relationshipsById);
|
|
});
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function indexNodesById(data) {
|
|
var graph = data.graph;
|
|
if (graph) {
|
|
var nodes = graph.nodes;
|
|
if (nodes) {
|
|
return nodes.reduce(function (result, node) {
|
|
|
|
var identity = convertNumber(node.id);
|
|
var labels = node.labels;
|
|
var properties = convertPrimitiveValue(node.properties);
|
|
result[node.id] = new _graphTypes.Node(identity, labels, properties);
|
|
|
|
return result;
|
|
}, {});
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
function indexRelationshipsById(data) {
|
|
var graph = data.graph;
|
|
if (graph) {
|
|
var relationships = graph.relationships;
|
|
if (relationships) {
|
|
return relationships.reduce(function (result, relationship) {
|
|
|
|
var identity = convertNumber(relationship.id);
|
|
var startNode = convertNumber(relationship.startNode);
|
|
var endNode = convertNumber(relationship.endNode);
|
|
var type = relationship.type;
|
|
var properties = convertPrimitiveValue(relationship.properties);
|
|
result[relationship.id] = new _graphTypes.Relationship(identity, startNode, endNode, type, properties);
|
|
|
|
return result;
|
|
}, {});
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
function extractRawRecordElement(index, data, nodesById, relationshipsById) {
|
|
var element = data.row ? data.row[index] : null;
|
|
var elementMetadata = data.meta ? data.meta[index] : null;
|
|
|
|
if (elementMetadata) {
|
|
// element is either a graph, spatial or temporal type
|
|
return convertComplexValue(element, elementMetadata, nodesById, relationshipsById);
|
|
} else {
|
|
// element is a primitive, like number, string, array or object
|
|
return convertPrimitiveValue(element);
|
|
}
|
|
}
|
|
|
|
function convertComplexValue(element, elementMetadata, nodesById, relationshipsById) {
|
|
if (isNodeMetadata(elementMetadata)) {
|
|
return nodesById[elementMetadata.id];
|
|
} else if (isRelationshipMetadata(elementMetadata)) {
|
|
return relationshipsById[elementMetadata.id];
|
|
} else if (isPathMetadata(elementMetadata)) {
|
|
return convertPath(elementMetadata, nodesById, relationshipsById);
|
|
} else if (isPointMetadata(elementMetadata)) {
|
|
return convertPoint(element);
|
|
} else {
|
|
return element;
|
|
}
|
|
}
|
|
|
|
function convertPath(metadata, nodesById, relationshipsById) {
|
|
var startNode = null;
|
|
var relationship = null;
|
|
var pathSegments = [];
|
|
|
|
for (var i = 0; i < metadata.length; i++) {
|
|
var element = metadata[i];
|
|
var elementId = element.id;
|
|
var elementType = element.type;
|
|
|
|
var nodeExpected = startNode === null && relationship === null || startNode !== null && relationship !== null;
|
|
if (nodeExpected && elementType !== 'node') {
|
|
throw new _error.Neo4jError('Unable to parse path, node expected but got: ' + (0, _stringify2.default)(element) + ' in ' + (0, _stringify2.default)(metadata));
|
|
}
|
|
if (!nodeExpected && elementType === 'node') {
|
|
throw new _error.Neo4jError('Unable to parse path, relationship expected but got: ' + (0, _stringify2.default)(element) + ' in ' + (0, _stringify2.default)(metadata));
|
|
}
|
|
|
|
if (nodeExpected) {
|
|
var node = nodesById[elementId];
|
|
if (startNode === null) {
|
|
startNode = node;
|
|
} else if (startNode !== null && relationship !== null) {
|
|
var pathSegment = new _graphTypes.PathSegment(startNode, relationship, node);
|
|
pathSegments.push(pathSegment);
|
|
startNode = node;
|
|
relationship = null;
|
|
} else {
|
|
throw new _error.Neo4jError('Unable to parse path, illegal node configuration: ' + (0, _stringify2.default)(metadata));
|
|
}
|
|
} else {
|
|
if (relationship === null) {
|
|
relationship = relationshipsById[elementId];
|
|
} else {
|
|
throw new _error.Neo4jError('Unable to parse path, illegal relationship configuration: ' + (0, _stringify2.default)(metadata));
|
|
}
|
|
}
|
|
}
|
|
|
|
var lastPathSegment = pathSegments[pathSegments.length - 1];
|
|
if (lastPathSegment && lastPathSegment.end !== startNode || relationship !== null) {
|
|
throw new _error.Neo4jError('Unable to parse path: ' + (0, _stringify2.default)(metadata));
|
|
}
|
|
|
|
return createPath(pathSegments);
|
|
}
|
|
|
|
function createPath(pathSegments) {
|
|
var pathStartNode = pathSegments[0].start;
|
|
var pathEndNode = pathSegments[pathSegments.length - 1].end;
|
|
return new _graphTypes.Path(pathStartNode, pathEndNode, pathSegments);
|
|
}
|
|
|
|
function convertPoint(element) {
|
|
var type = element.type;
|
|
if (type !== 'Point') {
|
|
throw new _error.Neo4jError('Unexpected Point type received: ' + (0, _stringify2.default)(element));
|
|
}
|
|
|
|
var coordinates = element.coordinates;
|
|
if (!Array.isArray(coordinates) && (coordinates.length !== 2 || coordinates.length !== 3)) {
|
|
throw new _error.Neo4jError('Unexpected Point coordinates received: ' + (0, _stringify2.default)(element));
|
|
}
|
|
|
|
var srid = convertCrsToId(element);
|
|
|
|
return new (Function.prototype.bind.apply(_spatialTypes.Point, [null].concat([srid], (0, _toConsumableArray3.default)(coordinates))))();
|
|
}
|
|
|
|
function convertCrsToId(element) {
|
|
var crs = element.crs;
|
|
if (!crs || !crs.name) {
|
|
throw new _error.Neo4jError('Unexpected Point crs received: ' + (0, _stringify2.default)(element));
|
|
}
|
|
var name = crs.name.toLowerCase();
|
|
|
|
if (name === 'wgs-84') {
|
|
return 4326;
|
|
} else if (name === 'wgs-84-3d') {
|
|
return 4979;
|
|
} else if (name === 'cartesian') {
|
|
return 7203;
|
|
} else if (name === 'cartesian-3d') {
|
|
return 9157;
|
|
} else {
|
|
throw new _error.Neo4jError('Unexpected Point crs received: ' + (0, _stringify2.default)(element));
|
|
}
|
|
}
|
|
|
|
function convertPrimitiveValue(element) {
|
|
if (element == null || element === undefined) {
|
|
return null;
|
|
} else if (typeof element === 'number') {
|
|
return convertNumber(element);
|
|
} else if (Array.isArray(element)) {
|
|
return element.map(function (element) {
|
|
return convertPrimitiveValue(element);
|
|
});
|
|
} else if ((typeof element === 'undefined' ? 'undefined' : (0, _typeof3.default)(element)) === 'object') {
|
|
return (0, _keys2.default)(element).reduce(function (result, key) {
|
|
result[key] = convertPrimitiveValue(element[key]);
|
|
return result;
|
|
}, {});
|
|
} else {
|
|
return element;
|
|
}
|
|
}
|
|
|
|
function convertNumber(value) {
|
|
return typeof value === 'number' ? value : Number(value);
|
|
}
|
|
|
|
function isNodeMetadata(metadata) {
|
|
return isMetadataForType('node', metadata);
|
|
}
|
|
|
|
function isRelationshipMetadata(metadata) {
|
|
return isMetadataForType('relationship', metadata);
|
|
}
|
|
|
|
function isPointMetadata(metadata) {
|
|
return isMetadataForType('point', metadata);
|
|
}
|
|
|
|
function isMetadataForType(name, metadata) {
|
|
return !Array.isArray(metadata) && (typeof metadata === 'undefined' ? 'undefined' : (0, _typeof3.default)(metadata)) === 'object' && metadata.type === name;
|
|
}
|
|
|
|
function isPathMetadata(metadata) {
|
|
return Array.isArray(metadata);
|
|
} |