Home Reference Source

lib6/mapping.rulesfactories.js

/**
 * 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.
 */
import { valueAs } from './mapping.highlevel';
import { isNode, isPath, isRelationship, isUnboundRelationship } from './graph-types';
import { isPoint } from './spatial-types';
import { isDate, isDateTime, isDuration, isLocalDateTime, isLocalTime, isTime } from './temporal-types';
import Vector from './vector';
/**
 * @property {function(rule: ?Rule)} asString Create a {@link Rule} that validates the value is a String.
 *
 * @property {function(rule: ?Rule & { acceptBigInt?: boolean })} asNumber Create a {@link Rule} that validates the value is a Number.
 *
 * @property {function(rule: ?Rule & { acceptNumber?: boolean })} AsBigInt Create a {@link Rule} that validates the value is a BigInt.
 *
 * @property {function(rule: ?Rule)} asNode Create a {@link Rule} that validates the value is a {@link Node}.
 *
 * @property {function(rule: ?Rule)} asRelationship Create a {@link Rule} that validates the value is a {@link Relationship}.
 *
 * @property {function(rule: ?Rule)} asPath Create a {@link Rule} that validates the value is a {@link Path}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asDuration Create a {@link Rule} that validates the value is a {@link Duration}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asLocalTime Create a {@link Rule} that validates the value is a {@link LocalTime}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asLocalDateTime Create a {@link Rule} that validates the value is a {@link LocalDateTime}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asTime Create a {@link Rule} that validates the value is a {@link Time}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asDateTime Create a {@link Rule} that validates the value is a {@link DateTime}.
 *
 * @property {function(rule: ?Rule & { stringify?: boolean })} asDate Create a {@link Rule} that validates the value is a {@link Date}.
 *
 * @property {function(rule: ?Rule)} asPoint Create a {@link Rule} that validates the value is a {@link Point}.
 *
 * @property {function(rule: ?Rule & { apply?: Rule })} asList Create a {@link Rule} that validates the value is a List.
 *
 * @property {function(rule: ?Rule & { asTypedList: boolean })} asVector Create a {@link Rule} that validates the value is a List.
 *
 * @experimental Part of the Record Object Mapping preview feature
 */
export const rule = Object.freeze({
    /**
     * Create a {@link Rule} that validates the value is a Boolean.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asBoolean(rule) {
        return Object.assign({ validate: (value, field) => {
                if (typeof value !== 'boolean') {
                    throw new TypeError(`${field} should be a boolean but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a String.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asString(rule) {
        return Object.assign({ validate: (value, field) => {
                if (typeof value !== 'string') {
                    throw new TypeError(`${field} should be a string but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Number}.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule & { acceptBigInt?: boolean }} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asNumber(rule) {
        return Object.assign({ validate: (value, field) => {
                if (typeof value === 'object' && value.low !== undefined && value.high !== undefined && Object.keys(value).length === 2) {
                    throw new TypeError('Number returned as Object. To use asNumber mapping, set disableLosslessIntegers or useBigInt in driver config object');
                }
                if (typeof value !== 'number' && ((rule === null || rule === void 0 ? void 0 : rule.acceptBigInt) !== true || typeof value !== 'bigint')) {
                    throw new TypeError(`${field} should be a number but received ${typeof value}`);
                }
            }, convert: (value) => {
                if (typeof value === 'bigint') {
                    return Number(value);
                }
                return value;
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link BigInt}.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule & { acceptNumber?: boolean }} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asBigInt(rule) {
        return Object.assign({ validate: (value, field) => {
                if (typeof value !== 'bigint' && ((rule === null || rule === void 0 ? void 0 : rule.acceptNumber) !== true || typeof value !== 'number')) {
                    throw new TypeError(`${field} should be a bigint but received ${typeof value}`);
                }
            }, convert: (value) => {
                if (typeof value === 'number') {
                    return BigInt(value);
                }
                return value;
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Node}.
     *
     * @example
     * const actingJobsRules: Rules = {
     *  // Converts the person node to a Person object in accordance with provided rules
     *  person: neo4j.rule.asNode({
     *    convert: (node: Node) => node.as(Person, personRules)
     *  }),
     *  // Returns the movie node as a Node
     *  movie: neo4j.rule.asNode({}),
     * }
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asNode(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isNode(value)) {
                    throw new TypeError(`${field} should be a Node but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Relationship}.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule.
     * @returns {Rule} A new rule for the value
     */
    asRelationship(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isRelationship(value)) {
                    throw new TypeError(`${field} should be a Relationship but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is an {@link UnboundRelationship}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asUnboundRelationship(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isUnboundRelationship(value)) {
                    throw new TypeError(`${field} should be a UnboundRelationship but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Path}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asPath(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isPath(value)) {
                    throw new TypeError(`${field} should be a Path but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Point}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asPoint(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isPoint(value)) {
                    throw new TypeError(`${field} should be a Point but received ${typeof value}`);
                }
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Duration}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asDuration(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isDuration(value)) {
                    throw new TypeError(`${field} should be a Duration but received ${typeof value}`);
                }
            }, convert: (value) => (rule === null || rule === void 0 ? void 0 : rule.stringify) === true ? value.toString() : value }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link LocalTime}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asLocalTime(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isLocalTime(value)) {
                    throw new TypeError(`${field} should be a LocalTime but received ${typeof value}`);
                }
            }, convert: (value) => (rule === null || rule === void 0 ? void 0 : rule.stringify) === true ? value.toString() : value }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Time}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asTime(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isTime(value)) {
                    throw new TypeError(`${field} should be a Time but received ${typeof value}`);
                }
            }, convert: (value) => (rule === null || rule === void 0 ? void 0 : rule.stringify) === true ? value.toString() : value }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link Date}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asDate(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isDate(value)) {
                    throw new TypeError(`${field} should be a Date but received ${typeof value}`);
                }
            }, convert: (value) => convertStdDate(value, rule) }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link LocalDateTime}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asLocalDateTime(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isLocalDateTime(value)) {
                    throw new TypeError(`${field} should be a LocalDateTime but received ${typeof value}`);
                }
            }, convert: (value) => convertStdDate(value, rule) }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a {@link DateTime}
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asDateTime(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!isDateTime(value)) {
                    throw new TypeError(`${field} should be a DateTime but received ${typeof value}`);
                }
            }, convert: (value) => convertStdDate(value, rule) }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a List. Optionally taking a rule for hydrating the contained values.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule & { apply?: Rule }} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asList(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!Array.isArray(value)) {
                    throw new TypeError(`${field} should be a list but received ${typeof value}`);
                }
            }, convert: (list, field) => {
                if ((rule === null || rule === void 0 ? void 0 : rule.apply) != null) {
                    return list.map((value, index) => valueAs(value, `${field}[${index}]`, rule.apply));
                }
                return list;
            } }, rule);
    },
    /**
     * Create a {@link Rule} that validates the value is a Vector.
     *
     * @experimental Part of the Record Object Mapping preview feature
     * @param {Rule & { asTypedList?: boolean }} rule Configurations for the rule
     * @returns {Rule} A new rule for the value
     */
    asVector(rule) {
        return Object.assign({ validate: (value, field) => {
                if (!(value instanceof Vector)) {
                    throw new TypeError(`${field} should be a vector but received ${typeof value}`);
                }
            }, convert: (value) => {
                if ((rule === null || rule === void 0 ? void 0 : rule.asTypedList) === true) {
                    return value._typedArray;
                }
                return value;
            } }, rule);
    }
});
function convertStdDate(value, rule) {
    if (rule != null) {
        if (rule.stringify === true) {
            return value.toString();
        }
        else if (rule.toStandardDate === true) {
            return value.toStandardDate();
        }
    }
    return value;
}