UNPKG

electrodb-temp

Version:

A library to more easily create and interact with multiple entities and heretical relationships in dynamodb

414 lines (393 loc) 9.19 kB
const e = require("./errors"); const { KeyCasing } = require("./types"); const Validator = require("jsonschema").Validator; Validator.prototype.customFormats.isFunction = function (input) { return typeof input === "function"; }; Validator.prototype.customFormats.isFunctionOrString = function (input) { return typeof input === "function" || typeof input === "string"; }; Validator.prototype.customFormats.isFunctionOrRegexp = function (input) { return typeof input === "function" || input instanceof RegExp; }; let v = new Validator(); const Attribute = { id: "/Attribute", type: ["object", "string", "array"], required: ["type"], properties: { type: { // todo: only specific values type: ["string", "array"], // enum: ["string", "number", "boolean", "enum"], }, field: { type: "string", }, hidden: { type: "boolean", }, watch: { type: ["array", "string"], items: { type: "string", }, }, label: { type: "string", }, readOnly: { type: "boolean", }, required: { type: "boolean", }, cast: { type: "string", enum: ["string", "number"], }, default: { type: "any", }, validate: { type: "any", format: "isFunctionOrRegexp", }, get: { type: "any", format: "isFunction", }, set: { type: "any", format: "isFunction", }, padding: { type: "object", required: ["length", "char"], properties: { length: { type: "number", }, char: { type: "string", }, }, }, }, }; const Index = { id: "/Index", type: "object", properties: { pk: { type: "object", required: true, properties: { field: { type: "string", required: true, }, facets: { type: ["array", "string"], items: { type: "string", }, required: false, }, composite: { type: ["array"], items: { type: "string", }, required: false, }, template: { type: "string", required: false, }, casing: { type: "string", enum: ["upper", "lower", "none", "default"], required: false, }, cast: { type: "string", enum: ["string", "number"], required: false, }, scope: { type: "string", required: false, }, }, }, sk: { type: "object", required: ["field"], properties: { field: { type: "string", required: true, }, facets: { type: ["array", "string"], required: false, items: { type: "string", }, }, composite: { type: ["array"], required: false, items: { type: "string", }, }, template: { type: "string", required: false, }, casing: { type: "string", enum: ["upper", "lower", "none", "default"], required: false, }, cast: { type: "string", enum: ["string", "number"], required: false, }, }, }, index: { type: "string", }, collection: { type: ["array", "string"], }, type: { type: "string", enum: ["clustered", "isolated"], required: false, }, condition: { type: "any", required: false, format: "isFunction", }, }, }; const Modelv1 = { type: "object", required: true, properties: { model: { type: "object", required: true, properties: { entity: { type: "string", required: true, }, version: { type: "string", required: true, }, service: { type: "string", required: true, }, }, }, table: { type: "string", }, attributes: { type: "object", patternProperties: { ["."]: { $ref: "/Attribute" }, }, }, indexes: { type: "object", minProperties: 1, patternProperties: { ["."]: { $ref: "/Index" }, }, }, filters: { $ref: "/Filters" }, }, required: ["model", "attributes", "indexes"], }; const ModelBeta = { type: "object", required: true, properties: { service: { type: "string", required: true, }, entity: { type: "string", required: true, }, table: { type: "string", }, version: { type: "string", }, attributes: { type: "object", patternProperties: { ["."]: { $ref: "/Attribute" }, }, }, indexes: { type: "object", minProperties: 1, patternProperties: { ["."]: { $ref: "/Index" }, }, }, filters: { $ref: "/Filters" }, }, required: ["attributes", "indexes"], }; const Filters = { id: "/Filters", type: "object", patternProperties: { ["."]: { type: "any", format: "isFunction", message: "Requires function", }, }, }; v.addSchema(Attribute, "/Attribute"); v.addSchema(Index, "/Index"); v.addSchema(Filters, "/Filters"); v.addSchema(ModelBeta, "/ModelBeta"); v.addSchema(Modelv1, "/Modelv1"); function validateModel(model = {}) { /** start beta/v1 condition **/ let betaErrors = v.validate(model, "/ModelBeta").errors; if (betaErrors.length) { /** end/v1 condition **/ let errors = v.validate(model, "/Modelv1").errors; if (errors.length) { throw new e.ElectroError( e.ErrorCodes.InvalidModel, errors .map((err) => { let message = `${err.property}`; switch (err.argument) { case "isFunction": return `${message} must be a function`; case "isFunctionOrString": return `${message} must be either a function or string`; case "isFunctionOrRegexp": return `${message} must be either a function or Regexp`; default: return `${message} ${err.message}`; } }) .join(", "), ); } } } function testModel(model) { let isModel = false; let error = ""; try { validateModel(model); isModel = true; } catch (err) { error = err.message; } return [isModel, error]; } function isStringHasLength(str) { return typeof str === "string" && str.length > 0; } function isObjectHasLength(obj) { return typeof obj === "object" && Object.keys(obj).length > 0; } function isArrayHasLength(arr) { return Array.isArray(arr) && arr.length > 0; } function isNameEntityRecordType(entityRecord) { return ( isObjectHasLength(entityRecord) && Object.values(entityRecord).find((value) => { return value._instance !== undefined; }) ); } function isNameModelRecordType(modelRecord) { return ( isObjectHasLength(modelRecord) && Object.values(modelRecord).find((value) => { return ( value.model && isStringHasLength(value.model.entity) && isStringHasLength(value.model.version) && isStringHasLength(value.model.service) ); }) ); } function isBetaServiceConfig(serviceConfig) { return ( isObjectHasLength(serviceConfig) && (isStringHasLength(serviceConfig.service) || isStringHasLength(serviceConfig.name)) && isStringHasLength(serviceConfig.version) ); } function isFunction(value) { return typeof value === "function"; } function stringArrayMatch(arr1, arr2) { let areArrays = Array.isArray(arr1) && Array.isArray(arr2); let match = areArrays && arr1.length === arr2.length; for (let i = 0; i < arr1.length; i++) { if (!match) { break; } match = isStringHasLength(arr1[i]) && arr1[i] === arr2[i]; } return match; } function isMatchingCasing(casing1, casing2) { const equivalentCasings = [KeyCasing.default, KeyCasing.lower]; if (isStringHasLength(casing1) && isStringHasLength(casing2)) { let isRealCase = KeyCasing[casing1.toLowerCase()] !== undefined; let casingsMatch = casing1 === casing2; let casingsAreEquivalent = [casing1, casing2].every((casing) => { return casing === KeyCasing.lower || casing === KeyCasing.default; }); return isRealCase && (casingsMatch || casingsAreEquivalent); } else if (isStringHasLength(casing1)) { return equivalentCasings.includes(casing1.toLowerCase()); } else if (isStringHasLength(casing2)) { return equivalentCasings.includes(casing2.toLowerCase()); } else { return casing1 === undefined && casing2 === undefined; } } module.exports = { testModel, isFunction, stringArrayMatch, isMatchingCasing, isArrayHasLength, isStringHasLength, isObjectHasLength, isBetaServiceConfig, isNameModelRecordType, isNameEntityRecordType, model: validateModel, };