@sprucelabs/schema
Version:
Static and dynamic binding plus runtime validation and transformation to ensure your app is sound. 🤓
220 lines (219 loc) • 8.85 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const AbstractEntity_1 = __importDefault(require("./AbstractEntity"));
const SpruceError_1 = __importDefault(require("./errors/SpruceError"));
const FieldFactory_1 = __importDefault(require("./factories/FieldFactory"));
const cloneDeepPreservingInstances_1 = __importDefault(require("./utilities/cloneDeepPreservingInstances"));
const normalizeFieldValue_1 = __importStar(require("./utilities/normalizeFieldValue"));
class StaticSchemaEntityImpl extends AbstractEntity_1.default {
constructor(schema, values) {
super(schema);
this.values = {};
this.schema = schema;
this.fields = {};
this.buildFields();
const v = {
...this.values,
...values,
};
this.values = (0, cloneDeepPreservingInstances_1.default)(v);
}
buildFields() {
const fieldDefinitions = this.schema.fields;
if (!fieldDefinitions) {
throw new Error(`SchemaEntity requires fields. If you want to use dynamicFieldSignature, try DynamicSchemaEntity.`);
}
Object.keys(fieldDefinitions).forEach((name) => {
const definition = fieldDefinitions[name];
const field = FieldFactory_1.default.Field(name, definition);
this.fields[name] = field;
if (definition.value) {
this.set(name, definition.value);
}
});
}
normalizeValue(forField, value, options) {
const field = this.fields[forField];
const overrideOptions = {
...(options ?? {}),
...(options?.byField?.[forField] ?? {}),
};
return (0, normalizeFieldValue_1.default)(this.schemaId, this.name, {}, field, value, overrideOptions);
}
get(fieldName, options = {}) {
const value = this.values[fieldName] !== undefined
? this.values[fieldName]
: undefined;
return this.normalizeValue(fieldName, value, options);
}
set(fieldName, value, options = {}) {
const localValue = this.normalizeValue(fieldName, value, options);
this.values[fieldName] = localValue;
return this;
}
isValid(options = {}) {
try {
this.validate(options);
return true;
}
catch {
return false;
}
}
pluckExtraFields(values, schema) {
const extraFields = [];
if (schema.fields) {
const passedFields = Object.keys(values);
const expectedFields = Object.keys(schema.fields);
passedFields.forEach((passed) => {
if (expectedFields.indexOf(passed) === -1) {
extraFields.push(passed);
}
});
}
return extraFields;
}
validate(options = {}) {
const errors = [];
const extraFields = this.pluckExtraFields(this.values, this.schema);
if (extraFields.length > 0) {
extraFields.forEach((name) => {
errors.push({
name,
code: 'UNEXPECTED_PARAMETER',
friendlyMessage: `\`${name}\` does not exist.`,
});
});
}
this.getNamedFields(options).forEach((namedField) => {
const { name, field } = namedField;
let valueAsArray = (0, normalizeFieldValue_1.normalizeValueToArray)(this.values[name]);
if (field.isRequired &&
field.isArray &&
(!this.values[name] ||
valueAsArray.length < (field.minArrayLength ?? 1))) {
errors.push({
code: !this.values[name]
? 'MISSING_PARAMETER'
: 'INVALID_PARAMETER',
name,
friendlyMessage: !this.values[name]
? `${field.label ? `'${field.label}'` : 'This'} is required!`
: `${field.label ? `'${field.label}'` : 'You'} must ${field.label ? 'have' : 'select'} at least ${field.minArrayLength} value${field.minArrayLength === 1 ? '' : 's'}. I found ${valueAsArray.length}!`,
});
}
else {
if ((!field.isArray || (field.minArrayLength ?? 0) > 0) &&
valueAsArray.length === 0) {
valueAsArray = [undefined];
}
for (const value of valueAsArray) {
const fieldErrors = field.validate(value, {
schemasById: {},
});
if (fieldErrors.length > 0) {
errors.push(...fieldErrors);
}
}
}
});
if (errors.length > 0) {
throw new SpruceError_1.default({
code: 'VALIDATION_FAILED',
schemaId: this.schemaId,
schemaName: this.name,
errors,
});
}
}
getDefaultValues(options = {}) {
const values = {};
this.getNamedFields().forEach((namedField) => {
const { name, field } = namedField;
if (typeof field.definition.defaultValue !== 'undefined') {
// @ts-ignore
values[name] = this.normalizeValue(name, field.definition.defaultValue, options);
}
});
return values;
}
getValues(options) {
const values = {};
let { fields = Object.keys(this.fields), shouldIncludePrivateFields: includePrivateFields = true, shouldIncludeNullAndUndefinedFields = true, excludeFields, } = options || {};
this.getNamedFields().forEach((namedField) => {
const { name, field } = namedField;
const shouldSkipBecauseNotSet = !shouldIncludeNullAndUndefinedFields && !(name in this.values);
const shouldSkipBecauseUndefinedOrNull = !shouldIncludeNullAndUndefinedFields &&
(this.values[name] === undefined || this.values[name] === null);
const shouldSkipBecauseExcluded = excludeFields?.includes(name);
if (shouldSkipBecauseNotSet ||
shouldSkipBecauseUndefinedOrNull ||
shouldSkipBecauseExcluded) {
return;
}
if (fields.indexOf(name) > -1 &&
(includePrivateFields || !field.isPrivate)) {
const value = this.get(name, options);
values[name] = value;
}
});
return values;
}
setValues(values) {
this.getNamedFields().forEach((namedField) => {
const { name } = namedField;
const value = values[name];
if (typeof value !== 'undefined') {
this.set(name, value);
}
});
return this;
}
getNamedFields(options = {}) {
const namedFields = [];
const { fields = Object.keys(this.fields) } = options;
fields.forEach((name) => {
const field = this.fields[name];
namedFields.push({ name, field });
});
return namedFields;
}
}
StaticSchemaEntityImpl.enableDuplicateCheckWhenTracking = true;
exports.default = StaticSchemaEntityImpl;