neo4j-driver-core
Version:
Internals of neo4j-driver
247 lines (246 loc) • 10.8 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 __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RecordObjectMapping = exports.defaultNameMapping = exports.rulesRegistry = void 0;
exports.as = as;
exports.valueAs = valueAs;
exports.optionalParameterConversion = optionalParameterConversion;
exports.validateAndCleanParameters = validateAndCleanParameters;
exports.getRules = getRules;
var error_1 = require("./error");
var mapping_nameconventions_1 = require("./mapping.nameconventions");
exports.rulesRegistry = {};
var defaultNameMapping = function (name) { return name; };
exports.defaultNameMapping = defaultNameMapping;
function register(constructor, rules) {
exports.rulesRegistry[constructor.name] = rules;
}
function clearMappingRegistry() {
exports.rulesRegistry = {};
}
function translateIdentifiers(translationFunction) {
exports.defaultNameMapping = translationFunction;
}
function getCaseTranslator(databaseConvention, codeConvention) {
var keys = Object.keys(mapping_nameconventions_1.nameConventions);
if (!keys.includes(databaseConvention)) {
throw (0, error_1.newError)("Naming convention ".concat(databaseConvention, " is not recognized, \n please provide a recognized name convention or manually provide a translation function."));
}
if (!keys.includes(codeConvention)) {
throw (0, error_1.newError)("Naming convention ".concat(codeConvention, " is not recognized, \n please provide a recognized name convention or manually provide a translation function."));
}
// @ts-expect-error
return function (name) { return mapping_nameconventions_1.nameConventions[databaseConvention].encode(mapping_nameconventions_1.nameConventions[codeConvention].tokenize(name)); };
}
exports.RecordObjectMapping = Object.freeze({
/**
* Clears all registered type mappings from the record object mapping registry.
*/
clearMappingRegistry: clearMappingRegistry,
/**
* Creates a translation function from record key names to object property names, for use with the {@link translateIdentifiers} function
*
* Recognized naming conventions are "camelCase", "PascalCase", "snake_case", "kebab-case", "SCREAMING_SNAKE_CASE"
*
* @param {string} databaseConvention The naming convention in use in database result Records
* @param {string} codeConvention The naming convention in use in JavaScript object properties
* @returns {function} translation function
*/
getCaseTranslator: getCaseTranslator,
/**
* Registers a set of {@link Rules} to be used by {@link hydrated} for the provided class when no other rules are specified. This registry exists in global memory, not the driver instance.
*
* @example
* // The following code:
* const summary = await driver.executeQuery('CREATE (p:Person{ name: $name }) RETURN p', { name: 'Person1'}, {
* resultTransformer: neo4j.resultTransformers.hydrated(Person, personClassRules)
* })
*
* can instead be written:
* neo4j.RecordObjectMapping.register(Person, personClassRules)
*
* const summary = await driver.executeQuery('CREATE (p:Person{ name: $name }) RETURN p', { name: 'Person1'}, {
* resultTransformer: neo4j.resultTransformers.hydrated(Person)
* })
*
* @param {GenericConstructor} constructor The constructor function of the class to set rules for. The constructor must be callable with all arguments undefined.
* @param {Rules} rules The rules to set for the provided class
*/
register: register,
/**
* Sets a default name translation from record keys to object properties.
* If providing a function, provide a function that maps FROM your object properties names TO record key names.
*
* NOTE: The keys of objects inside a record will only be translated if using the asObject rule with it, not by default.
*
* The function getCaseTranslator can be used to provide a prewritten translation function between some common naming conventions.
*
* @example
* //if the keys on records from the database are in ALLCAPS
* RecordObjectMapping.translateIdentifiers((name) => name.toUpperCase())
*
* //if you utilize PacalCase in the database and camelCase in JavaScript code.
* RecordObjectMapping.translateIdentifiers(mapping.getCaseTranslator("PascalCase", "camelCase"))
*
* //if a type has one odd mapping you can override the translation with the rule
* const personRules = {
* firstName: neo4j.rule.asString(),
* bornAt: neo4j.rule.asNumber({ optional: true })
* weird_name-property: neo4j.rule.asString({from: 'homeTown'})
* }
* //These rules can then be used by providing them to a hydratedResultsMapper
* record.as<Person>(personRules)
* //or by registering them to the mapping registry
* RecordObjectMapping.register(Person, personRules)
*
* @param {function} translationFunction A function translating the names of your JS object property names to record key names
*/
translateIdentifiers: translateIdentifiers
});
function as(gettable, constructorOrRules, rules) {
var e_1, _a, e_2, _b;
var GenericConstructor = typeof constructorOrRules === 'function' ? constructorOrRules : Object;
var theRules = getRules(constructorOrRules, rules);
var visitedKeys = [];
var obj = new GenericConstructor();
try {
for (var _c = __values(Object.entries(theRules !== null && theRules !== void 0 ? theRules : {})), _d = _c.next(); !_d.done; _d = _c.next()) {
var _e = __read(_d.value, 2), key = _e[0], rule = _e[1];
visitedKeys.push(key);
_apply(gettable, obj, key, rule);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
try {
for (var _f = __values(Object.getOwnPropertyNames(obj)), _g = _f.next(); !_g.done; _g = _f.next()) {
var key = _g.value;
if (!visitedKeys.includes(key)) {
_apply(gettable, obj, key, theRules === null || theRules === void 0 ? void 0 : theRules[key]);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
}
finally { if (e_2) throw e_2.error; }
}
return obj;
}
function _apply(gettable, obj, key, rule) {
var _a;
var mappedKey = (0, exports.defaultNameMapping)(key);
var value;
try {
value = gettable.get((_a = rule === null || rule === void 0 ? void 0 : rule.from) !== null && _a !== void 0 ? _a : mappedKey);
}
catch (e) {
if ((rule === null || rule === void 0 ? void 0 : rule.optional) === true && e instanceof error_1.Neo4jError) {
return;
}
throw e;
}
var field = "".concat(obj.constructor.name, "#").concat(key);
// @ts-expect-error
obj[key] = valueAs(value, field, rule);
}
function valueAs(value, field, rule) {
if ((rule === null || rule === void 0 ? void 0 : rule.optional) === true && value == null) {
return value;
}
if (typeof (rule === null || rule === void 0 ? void 0 : rule.validate) === 'function') {
rule.validate(value, field);
}
return ((rule === null || rule === void 0 ? void 0 : rule.convert) != null) ? rule.convert(value, field) : value;
}
function optionalParameterConversion(value, rule) {
if (rule.optional === true && value == null) {
return value;
}
return (value != null && rule.parameterConversion != null) ? rule.parameterConversion(value) : value;
}
function validateAndCleanParameters(params, suppliedRules) {
var _a;
var cleanedParams = {};
var parameterRules = getRules(Object.getPrototypeOf(params).constructor, suppliedRules);
if (parameterRules != null) {
for (var key in parameterRules) {
var param = params[key];
var mappedKey = (_a = parameterRules[key].from) !== null && _a !== void 0 ? _a : (0, exports.defaultNameMapping)(key);
if (param != null && parameterRules[key].parameterConversion != null) {
param = parameterRules[key].parameterConversion(param);
}
if (param == null) {
if (parameterRules[key].optional !== true) {
throw (0, error_1.newError)("Mapped Parameter object did not include required parameter with key ".concat(key, ", \n check provided parameters and parameter rules."));
}
}
else if (parameterRules[key].validate != null) {
parameterRules[key].validate(param, key);
}
cleanedParams[mappedKey] = param;
}
return cleanedParams;
}
else {
return params;
}
}
function getRules(constructorOrRules, rules) {
var rulesDefined = typeof constructorOrRules === 'object' ? constructorOrRules : rules;
if (rulesDefined != null) {
return rulesDefined;
}
return typeof constructorOrRules !== 'object' ? exports.rulesRegistry[constructorOrRules.name] : undefined;
}