mvom
Version:
Multivalue Object Mapper
206 lines (178 loc) • 5.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _lodash = require("lodash");
var _errors = require("../errors");
var _BaseSchemaType = _interopRequireDefault(require("./BaseSchemaType"));
// #region Types
// #endregion
/** Abstract Scalar Schema Type */
class BaseScalarType extends _BaseSchemaType.default {
/** Data definition which this schema type was constructed from */
/** 0-indexed Array path */
/** Multivalue dictionary id */
/** Required validation value for the schema type */
/** Indicates whether data should be encrypted/decrypted */
/** Encrypt function to call on sensitive data before writing to the database */
/** Decrypt function to call on sensitive data encrypted in the database */
/** Data transformer */
constructor(definition, {
encrypt,
decrypt
} = {}) {
super();
const {
path,
dictionary = null,
required = false,
encrypted = false
} = definition;
if (encrypted) {
if (typeof encrypt !== 'function') {
throw new _errors.InvalidParameterError({
message: 'Encrypt function required to process encrypted fields',
parameterName: 'encrypt'
});
}
if (typeof decrypt !== 'function') {
throw new _errors.InvalidParameterError({
message: 'Decrypt function required to process encrypted fields',
parameterName: 'decrypt'
});
}
}
this.definition = definition;
this.dictionary = dictionary;
this.path = this.normalizeMvPath(path);
this.required = required;
this.encrypted = encrypted;
this.encrypt = encrypt;
this.decrypt = decrypt;
}
/** Get value from mv data */
get(record) {
const value = this.getFromMvData(record);
return this.transformFromDb(value);
}
/** Transform into multivalue format and set specified value into mv record */
set(originalRecord, value) {
return this.setIntoMvData(originalRecord, this.transformToDb(value));
}
/** Transform from mv data to externally formatted data */
transformFromDb(value) {
return this.dataTransformer.transformFromDb(value);
}
/** Transform from externally formatted data to mv data */
transformToDb(value) {
return this.dataTransformer.transformToDb(value);
}
/** Transform query constants to the format schema */
transformToQuery(value) {
return this.dataTransformer.transformToQuery(value);
}
/** Validate the scalar type */
validate(value) {
const validators = this.validators.concat(this.createRequiredValidator(), this.createTypeValidator());
return validators.reduce((acc, {
validationFn,
message
}) => {
const isValid = validationFn(value);
if (!isValid) {
acc.push(message);
}
return acc;
}, []);
}
/** Get data from the specified keypath */
getFromMvData(record) {
const value = this.getFromMvArray(this.path, record);
return this.decryptData(value);
}
/** Set specified value into mv record */
setIntoMvData(originalRecord, setValue) {
const encryptedSetValue = this.encryptData(setValue);
return (0, _lodash.set)((0, _lodash.cloneDeep)(originalRecord), this.path, encryptedSetValue);
}
/** Required validator */
validateRequired = value => !this.required || value != null;
/** Type validator */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validateType = value => true;
/** Create validation object for required validation */
createRequiredValidator() {
const message = 'Property is required';
return {
validationFn: this.validateRequired,
message
};
}
/** Create validation object for type validation */
createTypeValidator() {
const message = 'Property cannot be cast into the defined type';
return {
validationFn: this.validateType,
message
};
}
/**
* Convert a 1-index string array path definition (e.g. '1.1.1') to a 0-index array path definition (e.g. [0, 0, 0])
* @throws {@link InvalidParameterError} Path definition must be a string of integers split by periods
*/
normalizeMvPath(path) {
return (0, _lodash.toPath)(path).slice(0, 3).map(val => {
const numVal = +val;
if (!Number.isInteger(numVal) || numVal < 1) {
throw new _errors.InvalidParameterError({
message: 'Path definition must be a string of integers split by periods',
parameterName: 'path'
});
}
return numVal - 1;
});
}
/** Encrypt a transformed property */
encryptData(data) {
if (!this.encrypted) {
return data;
}
if (Array.isArray(data)) {
return data.map(value => {
if (Array.isArray(value)) {
return value.map(innerValue => this.encryptSingle(innerValue));
}
return this.encryptSingle(value);
});
}
return this.encryptSingle(data);
}
/** Encrypt a single value */
encryptSingle(value) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return value == null ? value : this.encrypt(value);
}
/** Decrypt a multivalue attribute */
decryptData(data) {
if (!this.encrypted) {
return data;
}
if (Array.isArray(data)) {
return data.map(value => {
if (Array.isArray(value)) {
return value.map(innerValue => this.decryptSingle(innerValue));
}
return this.decryptSingle(value);
});
}
return this.decryptSingle(data);
}
/** Decrypt a single value */
decryptSingle(value) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return typeof value !== 'string' ? value : this.decrypt(value);
}
}
var _default = exports.default = BaseScalarType;