UNPKG

@rest-api/react-models

Version:

[![npm version](https://img.shields.io/npm/v/@rest-api/react-models)](https://www.npmjs.com/package/@rest-api/react-models) [![codecov](https://codecov.io/gh/hector7/rest-api-react-models/branch/master/graph/badge.svg)](https://codecov.io/gh/hector7/rest-

392 lines (391 loc) 17.2 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Schema = exports.fieldIsSchema = void 0; const BasicRestModel_1 = __importDefault(require("./restmodels/basic/BasicRestModel")); const BasicIdRestModel_1 = __importDefault(require("./restmodels/basic/BasicIdRestModel")); function fieldIsExtendedFormat(property) { return property !== null && property.hasOwnProperty('type'); } function isBasicModel(property) { return property.constructor === BasicIdRestModel_1.default || property.constructor === BasicRestModel_1.default; } function fieldIsSchema(property) { return property && typeof property === 'object' && property.constructor === Schema; } exports.fieldIsSchema = fieldIsSchema; class Schema { constructor(schema, subSchema) { this.updateSchema = schema => { const ownKeys = this.keys; const targetKeys = Object.keys(schema); const matchingKeys = ownKeys.filter(k => targetKeys.indexOf(k) >= 0); return new Schema(Object.assign(Object.assign({}, this._schema), matchingKeys .map(k => ({ k, v: schema[k] })) .reduce((o, next) => { o[next.k] = next.v; return o; }, {})), new Schema(schema).deleteFields(...matchingKeys)); }; this.deleteFields = (...fields) => { let schema = Object.assign({}, this._schema); fields.forEach(f => { const _a = schema, _b = f, toBeDelete = _a[_b], other = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]); schema = other; }); if (this._subSchema) return new Schema(schema, this._subSchema.deleteFields(...fields)); return new Schema(schema); }; this._schema = schema; this._subSchema = subSchema; } get RealType() { throw ('This getter is only for typing reasons'); } get PopulatedType() { throw ('This getter is only for typing reasons'); } get FullPopulatedType() { throw ('This getter is only for typing reasons'); } static getSchema(schema) { return new Schema(schema); } _fieldIsAnIdModel(property) { return isBasicModel(property); } fieldIsExtendedFormatWithModel(property) { if (fieldIsExtendedFormat(property)) { return this._fieldIsAnIdModel(property.type); } return false; } fieldIsSchema(property) { return fieldIsSchema(property); } isItemFromProperty(item, property, nullable, idOnly = false) { if (this._fieldIsAnIdModel(property)) { if (idOnly) return this.isItemFromProperty(item, property.idType, nullable); return property.model.schema.validate(item); } if (this.fieldIsSchema(property)) return property.validate(item); if (item === null) { return nullable; } return (item.constructor === property); } getErrorFromItemProperty(item, property, nullable, idOnly = false) { if (this._fieldIsAnIdModel(property)) { if (idOnly) return this.getErrorFromItemProperty(item, property.idType, nullable); return property.model.schema.getValidateError(item); } if (this.fieldIsSchema(property)) return property.getValidateError(item); if (item === null && !nullable) { return `Received null, not marked as nullable.`; } if (item.constructor !== property) { return `Received: \n ${JSON.stringify(item, null, 2)}\nExpected:\n ${property.name}`; } throw new Error('Called getErrorFromItemProperty and not errors found.'); } get keys() { if (this._subSchema) return Object.keys(this._schema).concat(this._subSchema.keys); return Object.keys(this._schema); } /** * * @param f field to update * @param fx function in order to convert the field */ updateField(f, fx) { const schema = new Schema({}, this); schema.functionToConvert = { key: f, fx }; return schema; } /** @internal */ _useUpdatedSteps(state, item) { let r = item; if (this._subSchema) { r = this._subSchema._useUpdatedSteps(state, item); } if (this.functionToConvert !== undefined) { r[this.functionToConvert.key] = this.functionToConvert.fx(r[this.functionToConvert.key]); } return r; } /** @internal */ _isPopulated(state, item) { let key; for (key in this._schema) { if (item !== null && item.hasOwnProperty(key)) { // check arrays and required property. const gField = this._schema[key]; const isArray = fieldIsExtendedFormat(gField) ? Array.isArray(gField.type) : Array.isArray(gField); const field = Array.isArray(gField) ? gField[0] : gField; const type = fieldIsExtendedFormat(field) ? field.type : field; const idOnly = this.fieldIsExtendedFormatWithModel(field) ? field.idOnly === true : false; const nullable = fieldIsExtendedFormat(field) ? field.nullable === true : false; if (this._fieldIsAnIdModel(type)) { if (idOnly) { if (isArray) { const array = item[key]; for (let i = 0; i < array.length; i++) { if (!nullable || (nullable && array[i] !== null)) { const getByIdResult = type._reducer.getById(state, array[i]); if (!getByIdResult) return false; } } } else { if ((!nullable || (nullable && item[key] !== null)) && !type._reducer.getById(state, item[key])) return false; } } } else if (this.fieldIsSchema(type)) { if (isArray) { const array = item[key]; for (let i = 0; i < array.length; i++) { const getByIdResult = type._isPopulated(state, array[i]); if (!getByIdResult) return false; } } else { if (!type._isPopulated(state, item[key])) return false; } } } } return true; } /** @internal */ _convertToPopulated(state, item) { let key; let result = Object.assign({}, item); for (key in this._schema) { if (item !== null && item.hasOwnProperty(key)) { // check arrays and required property. const gField = this._schema[key]; const isArray = fieldIsExtendedFormat(gField) ? Array.isArray(gField.type) : Array.isArray(gField); const field = Array.isArray(gField) ? gField[0] : gField; const type = fieldIsExtendedFormat(field) ? field.type : field; const idOnly = this.fieldIsExtendedFormatWithModel(field) ? field.idOnly === true : false; const nullable = fieldIsExtendedFormat(field) ? field.nullable === true : false; if (this._fieldIsAnIdModel(type)) { if (idOnly) { if (isArray) { result[key] = item[key].map((object) => { if (!nullable || (nullable && object !== null)) { const getByIdResult = type._reducer.getById(state, object); return getByIdResult ? type.model.schema._convertToPopulated(state, getByIdResult) : { [type._id]: object }; } else { return object; } }); } else { if (!nullable || (nullable && item[key] !== null)) { const getByIdResult = type._reducer.getById(state, item[key]); result[key] = getByIdResult ? type.model.schema._convertToPopulated(state, getByIdResult) : { [type._id]: item[key] }; } } } } else if (this.fieldIsSchema(type)) { if (isArray) { result[key] = item[key].map((object) => { return type._convertToPopulated(state, object); }); } else { result[key] = type._convertToPopulated(state, item[key]); } } } } return result; } _getModelValuesToPopulate(item, callback) { let key; for (key in this._schema) { if (item !== null && item.hasOwnProperty(key)) { // check arrays and required property. const gField = this._schema[key]; const isArray = fieldIsExtendedFormat(gField) ? Array.isArray(gField.type) : Array.isArray(gField); const field = Array.isArray(gField) ? gField[0] : gField; const type = fieldIsExtendedFormat(field) ? field.type : field; const idOnly = this.fieldIsExtendedFormatWithModel(field) ? field.idOnly === true : false; const nullable = fieldIsExtendedFormat(field) ? field.nullable === true : false; if (this._fieldIsAnIdModel(type)) { if (idOnly) { if (isArray) { item[key].forEach((object, idx) => { if (!nullable || (nullable && object !== null)) { callback(type, object); } }); } else if (!nullable || (nullable && item[key] !== null)) { callback(type, item[key]); } } } else if (this.fieldIsSchema(type)) { if (isArray) { item[key].forEach((object, idx) => { type._getModelValuesToPopulate(object, callback); }); } else { type._getModelValuesToPopulate(item[key], callback); } } } } } validate(str) { if (process.env.NODE_ENV === 'production') return true; if (typeof str !== 'object') { return false; } let key; for (key in this._schema) { // check arrays and required property. const gField = this._schema[key]; const isArray = fieldIsExtendedFormat(gField) ? Array.isArray(gField.type) : Array.isArray(gField); const field = Array.isArray(gField) ? gField[0] : gField; const type = fieldIsExtendedFormat(field) ? field.type : field; const required = fieldIsExtendedFormat(gField) && gField.required === true; const idOnly = this.fieldIsExtendedFormatWithModel(field) ? field.idOnly === true : false; const nullable = fieldIsExtendedFormat(field) ? field.nullable === true : false; if (required && (str === null || !str.hasOwnProperty(key))) return false; if (str !== null && str.hasOwnProperty(key)) { if (isArray) { if (str[key] === null) { if (!nullable) { return false; } } else { if (!Array.isArray(str[key])) { return false; } for (let i = 0; i < str[key].length; i++) { if (!this.isItemFromProperty(str[key][i], type, nullable, idOnly)) return false; } } } else { if (!this.isItemFromProperty(str[key], type, nullable, idOnly)) { return false; } } } } if (this._subSchema) return this._subSchema.validate(str); return true; } getValidateError(str) { if (typeof str !== 'object') { return `Expected Object, found: ${JSON.stringify(str, null, 2)}`; } let key; for (key in this._schema) { // check arrays and required property. const gField = this._schema[key]; const isArray = fieldIsExtendedFormat(gField) ? Array.isArray(gField.type) : Array.isArray(gField); const field = Array.isArray(gField) ? gField[0] : gField; const type = fieldIsExtendedFormat(field) ? field.type : field; const required = fieldIsExtendedFormat(gField) && gField.required === true; const idOnly = this.fieldIsExtendedFormatWithModel(field) ? field.idOnly === true : false; const nullable = fieldIsExtendedFormat(field) ? field.nullable === true : false; if (required && (str === null || !str.hasOwnProperty(key))) return { [key]: 'Marked as required, not found' }; if (str !== null && str.hasOwnProperty(key)) { if (isArray) { if (str[key] === null) { if (!nullable) { return { [key]: 'Received null, not marked as nullable' }; } } else { if (!Array.isArray(str[key])) { return { [key]: 'Is not an array' }; } for (let i = 0; i < str[key].length; i++) { if (!this.isItemFromProperty(str[key][i], type, nullable, idOnly)) return { [key]: this.getErrorFromItemProperty(str[key][i], type, nullable, idOnly) }; } } } else { if (!this.isItemFromProperty(str[key], type, nullable, idOnly)) { return { [key]: this.getErrorFromItemProperty(str[key], type, nullable, idOnly) }; } } } } if (this._subSchema) return this._subSchema.getValidateError(str); throw new Error('Called getValidateError and not errors found. Check first errors with validate function.'); } getValidateErrorPretty(str) { let error = this.getValidateError(str); let keys = []; while (typeof error !== 'string') { const k = Object.keys(error)[0]; keys.push(k); error = error[k]; } return `Validate error on path "${keys.join('.')}"\n${error}`; } getValidateArrayError(str) { if (Array.isArray(str)) { for (let i = 0; i < str.length; i++) { if (!this.validate(str[i])) return this.getValidateErrorPretty(str[i]); } throw new Error('Called getValidateArrayError and not errors found. Check first errors with validateArray function.'); } return `Expected Array, found: ${JSON.stringify(str, null, 2)}`; } validateArray(str) { if (process.env.NODE_ENV === 'production') return true; if (Array.isArray(str)) { return str.every(r => this.validate(r)); } return false; } } exports.Schema = Schema; exports.default = Schema.getSchema;