diff --git a/package.json b/package.json index 6a6527b3..e240d7d8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Programming platform for Quantitative Systems Pharmacology modeling in NodeJS", "main": "src/index.js", "scripts": { - "test:dev": "mocha test/cases/14 --config=./test/.mocharc.json", + "test:dev": "mocha test/time-switcher/bind-error --config=./test/.mocharc.json", "test": "mocha test --config=./test/.mocharc.json", "jsdoc": "jsdoc -r -c .jsdoc.json --readme api-references.md -d docs/dev src", "test:cov": "nyc --reporter=lcov npm run test", diff --git a/src/another-xlsx-export/_size.js b/src/another-xlsx-export/_size.js index 41a9d227..791119c0 100644 --- a/src/another-xlsx-export/_size.js +++ b/src/another-xlsx-export/_size.js @@ -2,10 +2,10 @@ const { _Size } = require('../core/_size'); const legalUnits = require('./legal-units'); // old version -let _toQ = _Size.prototype.toQ; +let oldToQ = _Size.prototype.toQ; // new version _Size.prototype.toQ = function(options = {}){ - let res = _toQ.call(this, options); + let res = oldToQ.call(this, options); // unit with transformation to simbio standard // if (options.useAnotherUnits) res.units2 = this.units; diff --git a/src/container/actions.js b/src/container/actions.js index 8f595bf2..d5a6c539 100644 --- a/src/container/actions.js +++ b/src/container/actions.js @@ -58,7 +58,7 @@ Container.prototype.export = function(q = {}, isCore = false) { */ Container.prototype.defineUnit = function(q = {}, isCore = false){ // normal flow - let unitDefInstance = new this.classes.UnitDef(q, isCore); + let unitDefInstance = new this.classes.UnitDef(isCore).merge(q); if (!unitDefInstance.errored) this.unitDefStorage.set(unitDefInstance.id, unitDefInstance); return unitDefInstance; @@ -74,7 +74,7 @@ Container.prototype.defineUnit = function(q = {}, isCore = false){ */ Container.prototype.defineFunction = function(q = {}, isCore = false){ // normal flow - let functionDefInstance = new this.classes.FunctionDef(q, isCore); + let functionDefInstance = new this.classes.FunctionDef(isCore).merge(q); if (!functionDefInstance.errored) this.functionDefStorage.set(functionDefInstance.id, functionDefInstance); return functionDefInstance; @@ -90,7 +90,7 @@ Container.prototype.defineFunction = function(q = {}, isCore = false){ */ Container.prototype.setScenario = function(q = {}, isCore = false){ // normal flow - let scenarioInstance = new this.classes.Scenario(q, isCore); + let scenarioInstance = new this.classes.Scenario(isCore).merge(q); if (!scenarioInstance.errored) this.scenarioStorage.set(scenarioInstance.id, scenarioInstance); return scenarioInstance; @@ -159,7 +159,7 @@ Container.prototype.forceInsert = function(q = {}, isCore = false){ } // check if class is in the list - let selectedClass = this._componentClasses[q.class]; + let selectedClass = this.classes[q.class]; if (selectedClass === undefined){ this.logger.error( `"${ind}" Unknown class "${q.class}" for the component.`, @@ -179,10 +179,8 @@ Container.prototype.forceInsert = function(q = {}, isCore = false){ } // normal flow - let component = new selectedClass(isCore); - component._id = q.id; + let component = new selectedClass(isCore).merge(q); component.namespace = namespace; // set parent space directly to component - component.merge(q); namespace.set(q.id, component); return component; }; @@ -435,8 +433,7 @@ Container.prototype.importNS = function(_q = {}){ || [q.prefix, component.id, q.suffix].join(''); // prefix-id-suffix as default value // cloning and update references - let clone = component.clone(); - clone._id = newId; + let clone = component.clone().merge({id: newId}); clone.namespace = namespace; clone.updateReferences(q); @@ -536,8 +533,7 @@ Container.prototype.import = function(_q = {}){ } // normal flow - let clone = component.clone(); - clone._id = q.id; + let clone = component.clone().merge({id: q.id}); clone.namespace = namespace; clone.updateReferences(q); namespace.set(q.id, clone); diff --git a/src/container/main.js b/src/container/main.js index 93432ca3..a88019ca 100644 --- a/src/container/main.js +++ b/src/container/main.js @@ -32,7 +32,7 @@ const TopoSort = require('@insysbio/topo-sort'); * * @property {object} classes Map-like storage for all element constructors that can be created inside platform. * For example the element of the type `UnitsDef` can be created as follows: - * ```let new_unit = new c.classes.UnitDef({id: 'new', units: 'g/litre'})``` + * ```let new_unit = new c.classes.UnitDef().merge({id: 'new', units: 'g/litre'})``` * The `new_unit` element will be automatically bound to the container and pushed to `unitDefStorage`. * @property {Logger} logger object providing transport of errors, warnings and info messages on Heta platform level. * @property {object[]} defaultLogs Default storage of errors which will be used for diagnostics. @@ -43,7 +43,6 @@ const TopoSort = require('@insysbio/topo-sort'); * @property {Map} namespaceStorage Storage for `Namespace` instances. Key is a string identifier. * There is a default namespace with identifier `nameless` which will be used as a default namespace * for all components where namespace name is not set. - * @property {object} _componentClasses map-like structure for storing all available constructors for `Component`s. * @property {object} _builder reference to the parent builder object (if exists). */ class Container { @@ -60,6 +59,35 @@ class Container { this.classes.FunctionDef.prototype._container = this; this.classes.Scenario = class extends Scenario {}; this.classes.Scenario.prototype._container = this; + // + this.classes.Component = class extends Component {}; + this.classes.Component.prototype._container = this; + this.classes.Record = class extends Record {}; + this.classes.Record.prototype._container = this; + this.classes.Compartment = class extends Compartment {}; + this.classes.Compartment.prototype._container = this; + this.classes.Species = class extends Species {}; + this.classes.Species.prototype._container = this; + this.classes.Reaction = class extends Reaction {}; + this.classes.Reaction.prototype._container = this; + this.classes.Process = class extends Process {}; + this.classes.Process.prototype._container = this; + this.classes.DSwitcher = class extends DSwitcher {}; + this.classes.DSwitcher.prototype._container = this; + this.classes.StopSwitcher = class extends StopSwitcher {}; + this.classes.StopSwitcher.prototype._container = this; + this.classes.CSwitcher = class extends CSwitcher {}; + this.classes.CSwitcher.prototype._container = this; + this.classes.TimeSwitcher = class extends TimeSwitcher {}; + this.classes.TimeSwitcher.prototype._container = this; + this.classes.ReferenceDefinition = class extends ReferenceDefinition {}; + this.classes.ReferenceDefinition.prototype._container = this; + this.classes.Page = class extends Page {}; + this.classes.Page.prototype._container = this; + this.classes.Const = class extends Const {}; + this.classes.Const.prototype._container = this; + this.classes.TimeScale = class extends TimeScale {}; + this.classes.TimeScale.prototype._container = this; // logger this.logger = new Logger(); @@ -322,21 +350,4 @@ class Container { // only component classes are stored -Container.prototype._componentClasses = { - Component, - Record, - Compartment, - Species, - Process, - Reaction, - DSwitcher, - StopSwitcher, - CSwitcher, - TimeSwitcher, - ReferenceDefinition, - Page, - Const, - TimeScale -}; - module.exports = Container; diff --git a/src/core/_size.js b/src/core/_size.js index 2c7e23a9..46bfb06d 100644 --- a/src/core/_size.js +++ b/src/core/_size.js @@ -1,5 +1,43 @@ const { Component } = require('./component'); const { Unit } = require('./unit'); +const { ajv } = require('../utils'); + +const schema = { + type: "object", + properties: { + units: { anyOf: [ + { type: 'number', enum: [1] }, + { $ref: '#/definitions/UnitsExpr' }, + { type: 'array', items: { '$ref': '#/definitions/UnitComponent' } }, + { type: 'null' } + ] } + }, + definitions:{ + UnitsExpr: { + description: 'Unit expression, see qsp-units project.', + type: 'string', + pattern: '^[_a-zA-Z0-9./*^ ()+-]+$', + example: "1/h * ms" + }, + UnitComponent: { + type: 'object', + required: ['kind'], + properties: { + kind: { '$ref': '#/definitions/ID' }, + multiplier: { type: 'number', exclusiveMinimum: 0 }, + exponent: { type: 'number' } + }, + example: { kind: 'mole', multiplier: 1e-6, exponent: 1 } + }, + ID: { + description: 'First character is letter, others are letter, digit or lodash.', + type: 'string', + minLength: 1, + pattern: '^[_a-zA-Z][_a-zA-Z0-9]*$', + example: 'x_12_' + } + } +}; /* Abstract class _Size @@ -17,7 +55,7 @@ const { Unit } = require('./unit'); class _Size extends Component { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = _Size.isValid(q, logger); if (valid) { if (q.units !== undefined) { @@ -60,8 +98,8 @@ class _Size extends Component { /** Additional check of units items */ bind(namespace){ super.bind(namespace); - let logger = this.namespace.container.logger; - let storage = this.namespace.container.unitDefStorage; + let logger = this._container?.logger; + let storage = this._container?.unitDefStorage; if (this.unitsParsed) { this.unitsParsed.forEach((x) => { @@ -95,7 +133,7 @@ class _Size extends Component { .rebase(legalUnits) .toString(usePefix); } catch(err) { - let logger = this.namespace.container.logger; + let logger = this._container?.logger; let msg = err.message; logger.warn(msg); return undefined; @@ -104,8 +142,12 @@ class _Size extends Component { return undefined; } } + static get validate() { + return ajv.compile(schema); + } toQ(options = {}){ let res = super.toQ(options); + if (this.unitsParsed) { if (options.noUnitsExpr) { res.units = this.unitsParsed.toQ(options); @@ -116,14 +158,8 @@ class _Size extends Component { return res; } - _references(){ - let classSpecificRefs = []; - - return super._references() - .concat(classSpecificRefs); - } } module.exports = { _Size -}; \ No newline at end of file +}; diff --git a/src/core/_switcher.js b/src/core/_switcher.js index b26d38d4..d1ba2404 100644 --- a/src/core/_switcher.js +++ b/src/core/_switcher.js @@ -1,4 +1,27 @@ const { Component } = require('./component'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + atStart: {oneOf: [ + { + description: 'If true than the condition will be checked at start_', + enum: [true, false, 1, 0], + default: false + }, + { type: 'null' } + ]}, + active: {oneOf: [ + { + description: 'if false the event will not run.', + enum: [true, false, 1, 0], + default: true + }, + { type: 'null' } + ]} + } +}; /* _Switcher abstract class @@ -14,7 +37,7 @@ class _Switcher extends Component { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = _Switcher.isValid(q, logger); if (valid) { @@ -51,6 +74,9 @@ class _Switcher extends Component { return res; } + static get validate() { + return ajv.compile(schema); + } } _Switcher._requirements = { diff --git a/src/core/c-switcher.js b/src/core/c-switcher.js index c3470ee3..6c0599d8 100644 --- a/src/core/c-switcher.js +++ b/src/core/c-switcher.js @@ -1,5 +1,24 @@ const { _Switcher } = require('./_switcher'); const { Expression } = require('./expression'); +const { ajv } = require('../utils'); + +const schema = { + type: "object", + properties: { + trigger: {oneOf: [ + { '$ref': '#/definitions/ExprString' }, + { type: "null" } + ]} + }, + definitions: { + ExprString: { + description: 'Expression as string. Currently pattern does not analyze expressions.', + type: 'string', + minLength: 1, + pattern: '[a-zA-Z0-9. -+/*^()]*$' + } + } +}; /* CSwitcher class @@ -11,7 +30,7 @@ const { Expression } = require('./expression'); class CSwitcher extends _Switcher { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = CSwitcher.isValid(q, logger); if (valid) { @@ -60,7 +79,7 @@ class CSwitcher extends _Switcher { } bind(namespace){ super.bind(namespace); - let {logger, functionDefStorage} = this.namespace.container; + let {logger, functionDefStorage} = this._container; // get list of this.trigger && this.trigger.dependOnNodes().forEach((node) => { @@ -105,7 +124,7 @@ class CSwitcher extends _Switcher { Works only for bound switchers */ checkUnits(){ - let logger = this.namespace.container.logger; + let logger = this._container?.logger; if (typeof this.trigger !== 'undefined') { // skip empty let rightSideUnit = this.trigger.calcUnit(this); @@ -114,6 +133,9 @@ class CSwitcher extends _Switcher { } } } + static get validate() { + return ajv.compile(schema); + } } CSwitcher._requirements = { diff --git a/src/core/compartment.js b/src/core/compartment.js index 7c29cd12..195845c1 100644 --- a/src/core/compartment.js +++ b/src/core/compartment.js @@ -1,5 +1,10 @@ const { Record } = require('./record'); const { UnitTerm } = require('./unit-term'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object' +}; /* Compartment class @@ -14,7 +19,7 @@ class Compartment extends Record { } merge(q = {}){ super.merge(q); - //let logger = this.namespace?.container?.logger; + //let logger = this._container?.logger; //let valid = Compartment.isValid(q, logger); return this; @@ -34,6 +39,9 @@ class Compartment extends Record { new UnitTerm([{kind: 'length', exponent: 1}]) ]; } + static get validate() { + return ajv.compile(schema); + } } module.exports = { diff --git a/src/core/component.js b/src/core/component.js index 12a7d651..7560d8b6 100644 --- a/src/core/component.js +++ b/src/core/component.js @@ -1,10 +1,40 @@ +const { Top } = require('./top'); const MarkdownIt = require('markdown-it'); const md = new MarkdownIt({html: true, xhtmlOut: false, linkify: true}); -const { uniqBy, ajv, flatten, cloneDeep } = require('../utils'); +const { uniqBy, ajv, cloneDeep } = require('../utils'); const _get = require('lodash/get'); const _set = require('lodash/set'); +const schema = { + type: "object", + description: "Abstract class for all top elements.", + properties: { + class: { type: "string" }, + title: {oneOf: [ + { type: "null" }, + { type: "string" } + ]}, + notes: {oneOf: [ + { type: "null" }, + { type: "string" } + ]}, + tags: {oneOf: [ + { type: "null" }, + { type: "array", "items": { "type": "string" } } + ]}, + aux: {oneOf: [ + { type: "null" }, + { + type: "object", + additionalProperties: { + oneOf: [ { "type": "string" }, { "type": "number" }, {"type": "array"}, { "type": "object" }, { "type": "boolean"} ] + } + } + ]} + } +}; + /* class Component @@ -14,16 +44,19 @@ const _set = require('lodash/set'); aux: {} }; */ -class Component { - constructor(isCore = false){ +class Component extends Top { + constructor(isCore = false) { + super(isCore); + this.tags = []; this.aux = {}; - if (isCore) this._isCore = true; } - merge(q = {}){ - let logger = this.namespace?.container?.logger; - let valid = Component.isValid(q, logger); + merge(q = {}) { + super.merge(q); + let logger = this._container?.logger; + let valid = Component.isValid(q, logger); + if (valid) { if (q.title === null) { delete this.title; @@ -49,16 +82,10 @@ class Component { return this; } - get isCore(){ - return this._isCore; - } - get id(){ - return this._id; - } // if NS not set, than undefined // if set any, than spaceName // if set nameless, than 'nameless' - get space(){ + get space() { if (this.namespace) { return this.namespace.spaceName; } else { @@ -82,7 +109,7 @@ class Component { return { id: this.id, space: this.space }; } // creates copy of element - clone(){ + clone() { let componentClone = new this.constructor(); if (this.title) componentClone.title = this.title; @@ -99,7 +126,7 @@ class Component { return componentClone; } /** Change referencies of component based on suffix/prefix/rename */ - updateReferences(_q = {}){ + updateReferences(_q = {}) { // set defaults let q = Object.assign({ prefix: '', @@ -145,32 +172,17 @@ class Component { .trim(); return renderedOutput; } - static isValid(q, logger){ - let ind = q.space ? `${q.space}::${q.id}` : q.id; - - let validate = ajv - .getSchema('https://hetalang.github.io#/definitions/' + this.schemaName); - if (!validate) { - throw new TypeError(`The schema "${this.schemaName}" is not found.`); - } - let valid = validate(q); - if (!valid) { - let msg = `${ind} Some of properties do not satisfy requirements for class "${this.schemaName}"\n` - + validate.errors.map((x, i) => ` ${i+1}. ${x.dataPath} ${x.message}`) - .join('\n'); - logger && logger.error(msg, {type: 'ValidationError', space: q.space}); - logger && logger.warn('Some of component properties will not be updated.'); - } - - return valid; + static get validate() { + return ajv.compile(schema); } /* Checking references: - check properties based on requirements(): required, find by symbol link - create virtual component if local prop refferences to global component */ - bind(namespace){ - let logger = this.namespace.container.logger; + bind(namespace) { + + let logger = this._container?.logger; if (!namespace) throw new TypeError('"namespace" argument should be set.'); @@ -227,34 +239,22 @@ class Component { } }); } - toQ(options = {}){ - let res = {}; - res.class = this.className; - res.id = this.id; - if (this.namespace && this.namespace.spaceName !== 'nameless') res.space = this.space; - if (this.title) res.title = this.title; - if (this.notes) res.notes = this.notes; - if (this.tags.length > 0) res.tags = this.tags.map((tag) => tag); - if (Object.keys(this.aux).length > 0) res.aux = cloneDeep(this.aux); + toQ(options = {}) { + let q = super.toQ(options); + delete q.action; - return res; - } - toFlat(_options = {}){ - // set defaults - let options = Object.assign({ - simplifyModifiers: true, - simplifyActors: true, - simplifyExpressions: true - }, _options); + q.class = this.className; + if (this.namespace && this.namespace.spaceName !== 'nameless') q.space = this.space; + if (this.title) q.title = this.title; + if (this.notes) q.notes = this.notes; + if (this.tags.length > 0) q.tags = this.tags.map((tag) => tag); + if (Object.keys(this.aux).length > 0) q.aux = cloneDeep(this.aux); - let q = this.toQ(options); - let res = flatten(q); - - return res; + return q; } /* recursively create requirements from _requirements, currently it is not optimal */ - static requirements(){ + static requirements() { if (this.prototype.className === 'Component') { return this._requirements; } else if (this.hasOwnProperty('_requirements')) { @@ -269,29 +269,17 @@ class Component { return deeper; } } - /* recursively check class names */ - instanceOf(className){ - if (this.className === className) { - return true; - } else if (!this.className) { - return false; - } else { - let proto = Object.getPrototypeOf(this); - let isInstance = this.instanceOf.call(proto, className); - //let isInstance = Object.getPrototypeOf(this).instanceOf(className); - return isInstance; - } + /* non-unique references */ + _references() { + return []; } /* - array of direct references inside component to another _Size + array of direct references inside component to another components it is used inside irt-nav */ - references(){ - return uniqBy(this._references()); - } - /* non-unique references */ - _references(){ - return []; + references() { + let nonUnique = this._references(); + return uniqBy(nonUnique); } } diff --git a/src/core/const.js b/src/core/const.js index e80167ea..ed831e89 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -1,4 +1,32 @@ const { _Size } = require('./_size'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + description: 'Input value. Upper and lower describes possible values. Scale describes transformation for fitting.', + properties: { + free: {oneOf: [ + { enum: [true, false, 1, 0] }, + { type: 'null' } + ]}, + num: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]}, + scale: {oneOf: [ + { type: 'string', enum: ['direct', 'log', 'logit'], default: 'direct' }, + { type: 'null' } + ]}, + upper: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]}, + lower: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]} + } +}; /* size1 @Const { @@ -17,7 +45,7 @@ class Const extends _Size { // implicit extend Numeric } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Const.isValid(q, logger); if (valid) { @@ -62,7 +90,7 @@ class Const extends _Size { // implicit extend Numeric // It checks lower<=num<=upper, 0 this.num) { @@ -110,6 +138,9 @@ class Const extends _Size { // implicit extend Numeric return res; } + static get validate() { + return ajv.compile(schema); + } } Const._requirements = { diff --git a/src/core/d-switcher.js b/src/core/d-switcher.js index ac86e5f4..894c2168 100644 --- a/src/core/d-switcher.js +++ b/src/core/d-switcher.js @@ -1,5 +1,26 @@ const { _Switcher } = require('./_switcher'); const { Expression } = require('./expression'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + trigger: {oneOf: [ + { '$ref': '#/definitions/ExprString' }, + { enum: [true, false, 1, 0] }, + { type: 'null' } + ] + } + }, + definitions: { + ExprString: { + description: 'Expression as string. Currently pattern does not analyze expressions.', + type: 'string', + minLength: 1, + pattern: '[a-zA-Z0-9. -+/*^()]*$' + } + } +}; /* DSwitcher class @@ -13,7 +34,7 @@ const { Expression } = require('./expression'); class DSwitcher extends _Switcher { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = DSwitcher.isValid(q, logger); if (valid) { @@ -63,7 +84,7 @@ class DSwitcher extends _Switcher { } bind(namespace){ super.bind(namespace); - let {logger, functionDefStorage} = this.namespace.container; + let {logger, functionDefStorage} = this._container; // get list of this.trigger && this.trigger.dependOnNodes().forEach((node) => { @@ -108,7 +129,7 @@ class DSwitcher extends _Switcher { Works only for bound switchers */ checkUnits(){ - let logger = this.namespace.container.logger; + let logger = this._container?.logger; if (typeof this.trigger !== 'undefined') { // skip empty let rightSideUnit = this.trigger.calcUnit(this); @@ -117,6 +138,9 @@ class DSwitcher extends _Switcher { } } } + static get validate() { + return ajv.compile(schema); + } } DSwitcher._requirements = { diff --git a/src/core/dose.js b/src/core/dose.js index 4012ac98..1dbe8bb0 100644 --- a/src/core/dose.js +++ b/src/core/dose.js @@ -2,6 +2,12 @@ const { _Size } = require('./_size'); const { Const } = require('./const'); const { floor, min } = Math; +const { ajv } = require('../utils'); + +const schema = { + // TODO: add validation +}; + /* Dose class */ @@ -13,7 +19,7 @@ class Dose extends _Size { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Dose.isValid(q, logger); if (valid) { @@ -143,6 +149,9 @@ class Dose extends _Size { return res; } + static get validate() { + return ajv.compile(schema); + } } Dose._requirements = { diff --git a/src/core/function-def.js b/src/core/function-def.js index de9894c0..c6798cc2 100644 --- a/src/core/function-def.js +++ b/src/core/function-def.js @@ -40,36 +40,39 @@ const schema = { }; */ class FunctionDef extends Top { - constructor(q = {}, isCore = false) { - super(q, isCore); + merge(q = {}) { + super.merge(q); // check arguments here let logger = this._container?.logger; let valid = FunctionDef.isValid(q, logger); - if (!valid) { this.errored = true; return; } + if (!valid) { + this.errored = true; + return this; + } // undefined arguments means it can vary (for core elements) - if (q.arguments) { + if (!!q.arguments) { this.arguments = q.arguments; - } else if (!isCore) { + } else if (!this.isCore) { let msg = `The #defineFunction ${q.id} must have "arguments".`; - logger && logger.error(msg, {type: 'ValidationError'}); + logger?.error(msg, {type: 'ValidationError'}); this.errored = true; } - if (q.math) { + if (!!q.math) { try { var expr = Expression.fromString(q.math); if (!expr.hasBooleanResult()) { this.math = expr; } else { let msg = `Function math "${this.id}" should be a numeric expression.`; - logger && logger.error(msg, {type: 'ValidationError'}); + logger?.error(msg, {type: 'ValidationError'}); this.errored = true; } } catch (e) { let msg = this.id + ': '+ e.message + ` in "${q.math.toString()}"`; - logger && logger.error(msg, {type: 'ValidationError'}); + logger?.error(msg, {type: 'ValidationError'}); this.errored = true; return; // BRAKE } @@ -79,14 +82,16 @@ class FunctionDef extends Top { .filter((v) => this.arguments?.indexOf(v) === -1); if (lostVariables.length > 0) { let msg = this.id + ': '+ `variables [${lostVariables.join(', ')}] are presented in math but not in arguments.`; - logger && logger.error(msg, {type: 'ValidationError'}); + logger?.error(msg, {type: 'ValidationError'}); this.errored = true; } - } else if (!isCore) { + } else if (!this.isCore) { let msg = `The #defineFunction ${q.id} must have "math".`; - logger && logger.error(msg, {type: 'ValidationError'}); + logger?.error(msg, {type: 'ValidationError'}); this.errored = true; } + + return this; } get className(){ return 'FunctionDef'; @@ -119,29 +124,28 @@ class FunctionDef extends Top { // super.bind(); let {logger, functionDefStorage} = this._container; - if (this.math) { // if math is presented then it is user-defined functions - // find and set reference to other functions - this.math.functionList().forEach((functionNode) => { - // find target functionDef - let target = functionDefStorage.get(functionNode.fn.name); - if (!target) { - let msg = `FunctionDef "${functionNode.fn.name}" is not found as expected here: ` - + `${this.index} { math: ${this.math} };`; - logger.error(msg, {type: 'BindingError'}); - } else { - functionNode.fnObj = target; // used for units checking - } + // find and set reference to other functions + this.math?.functionList().forEach((functionNode) => { + // find target functionDef + let target = functionDefStorage.get(functionNode.fn.name); + if (!target) { + let msg = `FunctionDef "${functionNode.fn.name}" is not found as expected here: ` + + `${this.index} { math: ${this.math} };`; + logger?.error(msg, {type: 'BindingError'}); + } else { + functionNode.fnObj = target; // used for units checking + } - // check arguments in functionNode - if (target && functionNode.args.length < target.arguments.length) { - let msg = `FunctionDef "${this.id}": Not enough arguments inside function ${functionNode}, required ${target.arguments.length}`; - logger.error(msg, {type: 'BindingError'}); - } - }); - } + // check arguments in functionNode + if (target && functionNode.args.length < target.arguments.length) { + let msg = `FunctionDef "${this.id}": Not enough arguments inside function ${functionNode}, required ${target.arguments.length}`; + logger?.error(msg, {type: 'BindingError'}); + } + }); } - _toQ(options = {}) { - let q = super._toQ(options); + toQ(options = {}) { + let q = super.toQ(options); + q.action = 'defineFunction'; if (this.arguments && this.arguments.length > 0) { q.arguments = this.arguments.map((x) => x); @@ -150,12 +154,6 @@ class FunctionDef extends Top { q.math = this.math.toString(options); } - return q; - } - toQ(options = {}) { - let q = this._toQ(options); - q.action = 'defineFunction'; - return q; } } diff --git a/src/core/page.js b/src/core/page.js index c8db9d03..486ffc8c 100644 --- a/src/core/page.js +++ b/src/core/page.js @@ -1,9 +1,20 @@ const { Component } = require('./component'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + content: { anyOf: [ + { type: 'string' }, + { type: 'null' } + ]} + } +}; class Page extends Component { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Page.isValid(q, logger); if (valid) { @@ -31,6 +42,9 @@ class Page extends Component { if(this.content) res.content = this.content; return res; } + static get validate() { + return ajv.compile(schema); + } } module.exports = { diff --git a/src/core/process.js b/src/core/process.js index 7ffb95db..f5bffbaf 100644 --- a/src/core/process.js +++ b/src/core/process.js @@ -1,4 +1,23 @@ const { Record } = require('./record'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + actors: { + oneOf: [ + { type: "array", items: {'$ref': "#/definitions/Actor"}, errorMessage: {type: 'should be an array of actors.'}}, + { '$ref': '#/definitions/ProcessExpr' }, + { type: 'null'} + ] + } + }, + errorMessage: { + properties: { + actors: 'is not string or array.' + } + } +}; /* Process class @@ -18,7 +37,7 @@ class Process extends Record { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Process.isValid(q, logger); if (valid) { @@ -58,7 +77,7 @@ class Process extends Record { } bind(namespace) { super.bind(namespace); - let {logger} = this.namespace.container; + let {logger} = this._container; // check and warn if actors is empty if (this.actors.length === 0) { @@ -81,12 +100,10 @@ class Process extends Record { return res; } - _references(){ - let classSpecificRefs = this.actors - .map((actor) => actor.target); + _references() { + let classSpecificRefs = this.actors.map((actor) => actor.target); - return super._references() - .concat(classSpecificRefs); + return super._references().concat(classSpecificRefs); } } @@ -129,6 +146,9 @@ class Actor extends _Effector { get className(){ return 'Actor'; } + static get validate() { + return ajv.compile(schema); + } } function rct2actors(rct){ diff --git a/src/core/reaction.js b/src/core/reaction.js index c697b935..d60a986f 100644 --- a/src/core/reaction.js +++ b/src/core/reaction.js @@ -1,5 +1,62 @@ const { Process, _Effector, Actor } = require('./process'); const { UnitTerm } = require('./unit-term'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + reversible: {oneOf: [ + { enum: [true, false, 1, 0], default: true }, + { type: 'null' } + ]}, + modifiers: {oneOf: [ + { + type: 'array', + items: { + oneOf: [ + { '$ref': '#/definitions/Effector' }, + { '$ref': '#/definitions/ID' } + ] + } + }, + { type: 'null' } + ]} + }, + errorMessage: { + properties: { + modifiers: 'is not an array of ids or modifiers.' + } + }, + definitions: { + Effector: { + description: 'Abstract class for modifiers and actors', + type: 'object', + required: ['target'], + properties: { + target: { '$ref': '#/definitions/ID' } + } + }, + Actor: { + allOf: [ + { '$ref': '#/definitions/Effector' }, + { + type: 'object', + properties: { + stoichiometry: { type: 'number' } + } + } + ], + example: { target: 'x1', stoichiometry: -1 } + }, + ID: { + description: 'First character is letter, others are letter, digit or lodash.', + type: 'string', + minLength: 1, + pattern: '^[_a-zA-Z][_a-zA-Z0-9]*$', + example: 'x_12_' + } + } +}; /* Reaction class @@ -18,7 +75,7 @@ class Reaction extends Process { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Reaction.isValid(q, logger); if (valid) { @@ -73,6 +130,9 @@ class Reaction extends Process { new UnitTerm([{kind: 'mass'}, {kind: 'time', exponent: -1}]) ]; } + static get validate() { + return ajv.compile(schema); + } } Reaction._requirements = { diff --git a/src/core/record.js b/src/core/record.js index 4721cd9a..3bc2c1e9 100644 --- a/src/core/record.js +++ b/src/core/record.js @@ -1,5 +1,69 @@ const { _Size } = require('./_size'); const { Expression } = require('./expression'); +const { ajv } = require('../utils'); + + +const schema = { + type: 'object', + properties: { + assignments: { + '$ref': '#/definitions/AssignmentDict' + }, + boundary: {oneOf: [ + { + enum: [true, false, 1, 0], default: false, + description: 'If it is true the record cannot be changed by any process, only by expression in assignments.' + }, + { type: 'null' } + ]}, + ss: {oneOf: [ + { + enum: [true, false, 1, 0], + description: 'Steady-State variable' + }, + { type: 'null' } + ]}, + output: {oneOf: [ + { + enum: [true, false, 1, 0], + description: 'Should be the record listed as an output.' + }, + { type: 'null' } + ]} + }, + definitions: { + AssignmentDict: { + description: 'Stores initializations as key/value dictionary. Key is switcher when to use. Key is one of Switcher id.', + type: 'object', + propertyNames: { '$ref': '#/definitions/ID' }, + additionalProperties: { + oneOf: [ + { '$ref': '#/definitions/ExprString' }, + { type: 'number'}, + { type: 'null' } + ] + }, + example: { + start_: { expr: 1.2 }, + ode_: { expr: 'x * y' }, + evt1: { expr: 'z + 1.2' } + } + }, + ID: { + description: 'First character is letter, others are letter, digit or lodash.', + type: 'string', + minLength: 1, + pattern: '^[_a-zA-Z][_a-zA-Z0-9]*$', + example: 'x_12_' + }, + ExprString: { + description: 'Expression as string. Currently pattern does not analyze expressions.', + type: 'string', + minLength: 1, + pattern: '[a-zA-Z0-9. -+/*^()]*$' + } + } +}; /* record1 @Record { @@ -17,7 +81,7 @@ class Record extends _Size { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Record.isValid(q, logger); if (valid) { @@ -93,7 +157,7 @@ class Record extends _Size { } bind(namespace){ super.bind(namespace); - let {logger, functionDefStorage} = this.namespace.container; + let {logger, functionDefStorage} = this._container; // check initialization let hasInit = this.assignments?.start_ !== undefined @@ -229,7 +293,7 @@ class Record extends _Size { Works only for bound records */ checkUnits(){ - let logger = this.namespace.container.logger; + let logger = this._container?.logger; let leftSideUnit = this.unitsParsed; if (typeof leftSideUnit === 'undefined') { @@ -249,13 +313,15 @@ class Record extends _Size { } } } - _references(){ + _references() { let classSpecificRefs = Object.entries(this.assignments) .map(([key, expression]) => expression.dependOn()) .flat(1); - return super._references() - .concat(classSpecificRefs); + return super._references().concat(classSpecificRefs); + } + static get validate() { + return ajv.compile(schema); } } diff --git a/src/core/reference-definition.js b/src/core/reference-definition.js index eea7c540..9340b782 100644 --- a/src/core/reference-definition.js +++ b/src/core/reference-definition.js @@ -1,9 +1,18 @@ const { Component } = require('./component'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + prefix: { type: 'string' }, + suffix: { type: 'string' } + } +}; class ReferenceDefinition extends Component { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = ReferenceDefinition.isValid(q, logger); if (valid) { @@ -40,6 +49,9 @@ class ReferenceDefinition extends Component { return res; } + static get validate() { + return ajv.compile(schema); + } } module.exports = { diff --git a/src/core/scenario.js b/src/core/scenario.js index 45855bdb..ef9d8e9e 100644 --- a/src/core/scenario.js +++ b/src/core/scenario.js @@ -54,13 +54,16 @@ const schema = { }; class Scenario extends Top { - constructor(q = {}, isCore = false){ - super(q, isCore); + merge(q = {}){ + super.merge(q); // check arguments here - let logger = this._container.logger; + let logger = this._container?.logger; let valid = Scenario.isValid(q, logger); - if (!valid) { this.errored = true; return; } + if (!valid) { + this.errored = true; + return this; + } // set properties if (q.model) { @@ -100,6 +103,8 @@ class Scenario extends Top { if (q.events_save) { this.events_save = q.events_save; } + + return this; } get className(){ return 'Scenario'; @@ -107,8 +112,10 @@ class Scenario extends Top { static get validate(){ return ajv.compile(schema); } - _toQ(options = {}){ - let q = super._toQ(options); + toQ(options = {}){ + let q = super.toQ(options); + q.action = 'setScenario'; + if (this.model !== 'nameless') { q.model = this.model; } @@ -130,12 +137,6 @@ class Scenario extends Top { if (this.events_save) { q.events_save = this.events_save; } - - return q; - } - toQ(options = {}){ - let q = this._toQ(options); - q.action = 'setScenario'; return q; } @@ -144,11 +145,11 @@ class Scenario extends Top { Called by Container.prototype.knitMany() */ bind(){ - let logger = this._container.logger; + let logger = this._container?.logger; // set model/namespace - if (this._container.namespaceStorage.has(this.model)) { - this.modelObj = this._container.namespaceStorage.get(this.model); + if (this._container?.namespaceStorage.has(this.model)) { + this.modelObj = this._container?.namespaceStorage.get(this.model); } else { let msg = `Scenario's ${this.id} "model" property must refer to a namespace, got "${this.model}".`; logger.error(msg, {type: 'BindingError'}); diff --git a/src/core/species.js b/src/core/species.js index c9ef07a2..db4121cf 100644 --- a/src/core/species.js +++ b/src/core/species.js @@ -2,6 +2,35 @@ const { Record } = require('./record'); const { UnitTerm } = require('./unit-term'); const { Unit } = require('./unit'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + properties: { + compartment: {oneOf: [ + { '$ref': '#/definitions/ID' }, + { type: 'null' } + ]}, + isAmount: {oneOf: [ + { + description: 'If it is false then the value represents the concentration, i.e. normalized to compartment.', + enum: [true, false, 1, 0], + default: false + }, + { type: 'null' } + ]} + }, + definitions: { + ID: { + description: 'First character is letter, others are letter, digit or lodash.', + type: 'string', + minLength: 1, + pattern: '^[_a-zA-Z][_a-zA-Z0-9]*$', + example: 'x_12_' + } + } +}; + /* Species class @@ -13,7 +42,7 @@ const { Unit } = require('./unit'); class Species extends Record { merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = Species.isValid(q, logger); if (valid) { @@ -83,11 +112,10 @@ class Species extends Record { return this.assignments.ode_ !== undefined || !this.isAmount; } - _references(){ - let classSpecificRefs = [this.compartment]; - - return super._references() - .concat(classSpecificRefs); + _references() { + return super._references().concat([ + this.compartment + ]); } /* Check units for left and right side of ODE @@ -96,7 +124,7 @@ class Species extends Record { checkUnits() { super.checkUnits(); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; // if not in processes do not check let processes = this.backReferences.map(x => x._process_); @@ -163,6 +191,9 @@ class Species extends Record { ]; } } + static get validate() { + return ajv.compile(schema); + } } Species._requirements = { diff --git a/src/core/stop-switcher.js b/src/core/stop-switcher.js index 37235875..e521e5a3 100644 --- a/src/core/stop-switcher.js +++ b/src/core/stop-switcher.js @@ -1,6 +1,29 @@ const { _Switcher } = require('./_switcher'); const { Expression } = require('./expression'); +const { ajv } = require('../utils'); +const { default: def } = require('ajv/dist/vocabularies/discriminator'); + +const schema = { + type: "object", + properties: { + trigger: {oneOf: [ + { '$ref': '#/definitions/ExprString' }, + { enum: [true, false, 1, 0] }, + { type: 'null' } + ] + } + }, + definitions: { + ExprString: { + description: 'Expression as string. Currently pattern does not analyze expressions.', + type: 'string', + minLength: 1, + pattern: '[a-zA-Z0-9. -+/*^()]*$' + } + } +}; + /* StopSwitcher class (experimental) @@ -13,7 +36,7 @@ const { Expression } = require('./expression'); class StopSwitcher extends _Switcher { merge(q = {}) { super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = StopSwitcher.isValid(q, logger); if (valid) { @@ -63,7 +86,7 @@ class StopSwitcher extends _Switcher { } bind(namespace){ super.bind(namespace); - let {logger, functionDefStorage} = this.namespace.container; + let {logger, functionDefStorage} = this._container; // get list of this.trigger && this.trigger.dependOnNodes().forEach((node) => { @@ -108,7 +131,7 @@ class StopSwitcher extends _Switcher { Works only for bound switchers */ checkUnits(){ - let logger = this.namespace.container.logger; + let logger = this._container?.logger; if (typeof this.trigger !== 'undefined') { // skip empty let rightSideUnit = this.trigger.calcUnit(this); @@ -117,6 +140,9 @@ class StopSwitcher extends _Switcher { } } } + static get validate() { + return ajv.compile(schema); + } } StopSwitcher._requirements = { diff --git a/src/core/time-scale.js b/src/core/time-scale.js index 69391c51..35e12c90 100644 --- a/src/core/time-scale.js +++ b/src/core/time-scale.js @@ -1,5 +1,25 @@ const { _Size } = require('./_size'); const { UnitTerm } = require('./unit-term'); +const { ajv } = require('../utils'); + +const schema = { + type: 'object', + description: 't and other time scales', + properties: { + slope: {oneOf: [ + { type: 'number', exclusiveMinimum: 0 }, + { type: 'null' } + ]}, + intercept: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]}, + output: {oneOf: [ + { enum: [true, false, 1, 0] }, + { type: 'null' } + ]} + } +}; /* time_in_minutes @TimeScale { @@ -20,7 +40,7 @@ class TimeScale extends _Size { // implicit extend Numeric } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = TimeScale.isValid(q, logger); if (valid) { @@ -73,6 +93,9 @@ class TimeScale extends _Size { // implicit extend Numeric new UnitTerm([{kind: 'time'}]) ]; } + static get validate() { + return ajv.compile(schema); + } } module.exports = { diff --git a/src/core/time-switcher.js b/src/core/time-switcher.js index f375ba58..4f672f99 100644 --- a/src/core/time-switcher.js +++ b/src/core/time-switcher.js @@ -1,5 +1,68 @@ const { _Switcher } = require('./_switcher'); const { Const } = require('./const'); + +const { ajv } = require('../utils'); + + +const schema = { + type: 'object', + properties: { + start: {oneOf: [ + { '$ref' : '#/definitions/ConstInternal' }, + { type: "null" } + ]}, + stop: {oneOf: [ + { '$ref' : '#/definitions/ConstInternal' }, + { type: "null" } + ]}, + period: {oneOf: [ + { '$ref' : '#/definitions/ConstInternal' }, + { type: 'null' } + ]} + }, + definitions: { + Const: { + type: 'object', + description: 'Input value. Upper and lower describes possible values. Scale describes transformation for fitting.', + properties: { + free: {oneOf: [ + { enum: [true, false, 1, 0] }, + { type: 'null' } + ]}, + num: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]}, + scale: {oneOf: [ + { type: 'string', enum: ['direct', 'log', 'logit'], default: 'direct' }, + { type: 'null' } + ]}, + upper: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]}, + lower: {oneOf: [ + { type: 'number' }, + { type: 'null' } + ]} + } + }, + ConstInternal: {anyOf: [ + { allOf: [ { '$ref': '#/definitions/Const' }, { type: 'object', required: ['num'] } ] }, + { '$ref': '#/definitions/ID' }, + { type: 'number' }, + { type: 'null' } + ]}, + ID: { + description: 'First character is letter, others are letter, digit or lodash.', + type: 'string', + minLength: 1, + pattern: '^[_a-zA-Z][_a-zA-Z0-9]*$', + example: 'x_12_' + } + } +}; + /* TimeSwitcher class @@ -28,8 +91,9 @@ class TimeSwitcher extends _Switcher { } merge(q = {}){ super.merge(q); - let logger = this.namespace?.container?.logger; + let logger = this._container?.logger; let valid = TimeSwitcher.isValid(q, logger); + console.log(this) if (valid) { // empty means anon 0 as default @@ -125,6 +189,9 @@ class TimeSwitcher extends _Switcher { return res; } + static get validate() { + return ajv.compile(schema); + } } TimeSwitcher._requirements = { diff --git a/src/core/top.js b/src/core/top.js index 59b3df9b..0eb4204e 100644 --- a/src/core/top.js +++ b/src/core/top.js @@ -36,23 +36,28 @@ const schema = { } */ -class Top { // or const Top = class {...} - /* - new Top({id: 'ttt1'}); - */ - constructor(q = {}, isCore = false){ +class Top { + constructor(isCore = false) { + if (isCore) this._isCore = true; + + this._id = 'rand_' + randomId(lengthRandom, patternRandom); + this.isRandomId = true; + } + merge(q = {}) { let logger = this._container?.logger; let valid = Top.isValid(q, logger); - if (!valid) { this.errored = true; return; } - if (isCore) this.isCore = true; - if (typeof q.id !== 'undefined') { + if (!valid) { + this.errored = true; + return this; + } + + if (!!q.id) { this._id = q.id; - this.isRandomId = false; - } else { - this._id = 'rand_' + randomId(lengthRandom, patternRandom); - this.isRandomId = true; + delete this.isRandomId; } + + return this; } get id(){ return this._id; @@ -60,6 +65,9 @@ class Top { // or const Top = class {...} get index(){ return this._id; } + get isCore(){ + return this._isCore; + } get className(){ return 'Top'; } @@ -68,7 +76,9 @@ class Top { // or const Top = class {...} } static isValid(q, logger){ let valid = this.validate(q); + if (!valid) { + console.log(q, valid); let msg = `${q.id} Some of properties do not satisfy requirements for class "${this.name}"\n` + this.validate.errors.map((x, i) => ` ${i+1}. ${x.dataPath} ${x.message}`) .join('\n'); @@ -77,14 +87,12 @@ class Top { // or const Top = class {...} return valid; } - _toQ(options = {}){ + toQ(options = {}){ let q = {}; - if (!this.isRandomId) q.id = this.id; + if (!this.isRandomId) { + q.id = this.id + }; - return q; - } - toQ(options = {}){ - let q = this._toQ(options); q.action = 'defineTop'; return q; @@ -102,6 +110,19 @@ class Top { // or const Top = class {...} return res; } + /* recursively check class names */ + instanceOf(className){ + if (this.className === className) { + return true; + } else if (!this.className) { + return false; + } else { + let proto = Object.getPrototypeOf(this); + let isInstance = this.instanceOf.call(proto, className); + //let isInstance = Object.getPrototypeOf(this).instanceOf(className); + return isInstance; + } + } } module.exports = { diff --git a/src/core/unit-def.js b/src/core/unit-def.js index b101fa58..bf7590c5 100644 --- a/src/core/unit-def.js +++ b/src/core/unit-def.js @@ -65,15 +65,16 @@ const schema = { ]}; */ class UnitDef extends Top { - constructor(q = {}, isCore = false){ - super(q, isCore); - - //this.unitsParsed = new Unit(); // XXX: I am not sure maybe this was important + merge(q = {}) { + super.merge(q); // check arguments here let logger = this._container?.logger; let valid = UnitDef.isValid(q, logger); - if (!valid) { this.errored = true; return; } + if (!valid) { + this.errored = true; + return this; + } // units or terms are required but not both if (q.units && q.terms) { @@ -91,27 +92,29 @@ class UnitDef extends Top { this.unitsParsed = Unit.parse(q.units); } catch (e) { let msg = this.index + ': '+ e.message; - logger && logger.error(msg, {type: 'ValidationError', space: this.space}); + logger?.error(msg, {type: 'ValidationError', space: this.space}); } } else if (q.units && q.units instanceof Array) { this.unitsParsed = Unit.fromQ(q.units); } if (q.terms) this.terms = new UnitTerm(q.terms); + + return this; } get units(){ - if (this.unitsParsed !== undefined) { + if (!!this.unitsParsed) { return this.unitsParsed.toString(); } else { return undefined; } } - bind(){ + bind() { // super.bind(); - let logger = this._container.logger; - let storage = this._container.unitDefStorage; + let logger = this._container?.logger; + let storage = this._container?.unitDefStorage; - if (this.unitsParsed) { + if (!!this.unitsParsed) { // set kindObj this.unitsParsed.forEach((x) => { let target = storage.get(x.kind); @@ -119,7 +122,7 @@ class UnitDef extends Top { if (!target) { let msg = `UnitDef "${x.kind}" is not found as expected here: ` + `${this.index} { units: ${this.units} };`; - logger.error(msg, {type: 'BindingError'}); + logger?.error(msg, {type: 'BindingError'}); } else { x.kindObj = target; } @@ -132,8 +135,9 @@ class UnitDef extends Top { static get validate(){ return ajv.compile(schema); } - _toQ(options = {}){ - let q = super._toQ(options); + toQ(options = {}){ + let q = super.toQ(options); + q.action = 'defineUnit'; if (this.unitsParsed) { if (options.noUnitsExpr) { @@ -142,11 +146,6 @@ class UnitDef extends Top { q.units = this.unitsParsed.toString(); } } - return q; - } - toQ(options = {}){ - let q = this._toQ(options); - q.action = 'defineUnit'; return q; } diff --git a/src/heta.json-schema.json b/src/heta.json-schema.json index 9b98c866..b28f2330 100644 --- a/src/heta.json-schema.json +++ b/src/heta.json-schema.json @@ -51,28 +51,6 @@ } }, - "Effector": { - "description": "Abstract class for modifiers and actors", - "type": "object", - "required": ["target"], - "properties": { - "target": { "$ref": "#/definitions/ID" } - } - }, - - "Actor": { - "allOf": [ - { "$ref": "#/definitions/Effector" }, - { - "type": "object", - "properties": { - "stoichiometry": { "type": "number" } - } - } - ], - "example": { "target": "x1", "stoichiometry": -1 } - }, - "Statistics": { "description": "Describes different outputs for monte-carlo problem.", "type": "object", @@ -82,36 +60,6 @@ } }, - "Component": { - "type": "object", - "description": "Abstract class for all top elements.", - "properties": { - "class": { "type": "string" }, - "id": { "$ref": "#/definitions/ID" }, - "title": {"oneOf": [ - { "type": "null" }, - { "type": "string" } - ]}, - "notes": {"oneOf": [ - { "type": "null" }, - { "type": "string" } - ]}, - "tags": {"oneOf": [ - { "type": "null" }, - { "type": "array", "items": { "type": "string" } } - ]}, - "aux": {"oneOf": [ - { "type": "null" }, - { - "type": "object", - "additionalProperties": { - "oneOf": [ { "type": "string" }, { "type": "number" }, {"type": "array"}, { "type": "object" }, { "type": "boolean"} ] - } - } - ]} - } - }, - "_Scoped": { "description": "Abstract class for Records and other classes with space.", "properties": { @@ -119,151 +67,6 @@ } }, - "Record": { - "type": "object", - "properties": { - "assignments": { - "$ref": "#/definitions/AssignmentDict" - }, - "boundary": {"oneOf": [ - { - "enum": [true, false, 1, 0], "default": false, - "description": "If it is true the record cannot be changed by any process, only by expression in assignments." - }, - { "type": "null" } - ]}, - "ss": {"oneOf": [ - { - "enum": [true, false, 1, 0], - "description": "Steady-State variable" - }, - { "type": "null" } - ]}, - "output": {"oneOf": [ - { - "enum": [true, false, 1, 0], - "description": "Should be the record listed as an output" - }, - { "type": "null" } - ]} - } - }, - - "Compartment": { - "type": "object" - }, - - "Species": { - "type": "object", - "properties": { - "compartment": {"oneOf": [ - { "$ref": "#/definitions/ID" }, - { "type": "null" } - ]}, - "isAmount": {"oneOf": [ - { - "description": "If it is false then the value represents the concentration, i.e. normalized to compartment.", - "enum": [true, false, 1, 0], - "default": false - }, - { "type": "null" } - ]} - } - }, - - "Process": { - "type": "object", - "properties": { - "actors": { - "oneOf": [ - { "type": "array", "items": {"$ref": "#/definitions/Actor"}, "errorMessage": {"type": "should be an array of actors."}}, - { "$ref": "#/definitions/ProcessExpr" }, - { "type": "null"} - ] - } - }, - "errorMessage": { - "properties": { - "actors": "is not string or array." - } - } - }, - - "Reaction": { - "type": "object", - "properties": { - "reversible": {"oneOf": [ - { "enum": [true, false, 1, 0], "default": true }, - { "type": "null" } - ]}, - "modifiers": {"oneOf": [ - { - "type": "array", - "items": { - "oneOf": [ - { "$ref": "#/definitions/Effector" }, - { "$ref": "#/definitions/ID" } - ] - } - }, - { "type": "null" } - ]} - }, - "errorMessage": { - "properties": { - "modifiers": "is not an array of ids or modifiers." - } - } - }, - - - - "_Switcher": { - "type": "object", - "properties": { - "atStart": {"oneOf": [ - { - "description": "If true than the condition will be checked at start_", - "enum": [true, false, 1, 0], - "default": false - }, - { "type": "null" } - ]}, - "active": {"oneOf": [ - { - "description": "if false the event will not run.", - "enum": [true, false, 1, 0], - "default": true - }, - { "type": "null" } - ]} - } - }, - - "CSwitcher": { - "type": "object", - "properties": { - "trigger": {"oneOf": [ - { "$ref": "#/definitions/ExprString" }, - { "type": "null" } - ]} - } - }, - - - - "DSwitcher": { - "type": "object", - "properties": { - "trigger": {"oneOf": [ - { "$ref": "#/definitions/ExprString" }, - { "enum": [true, false, 1, 0] }, - { "type": "null" } - ] - } - } - }, - "StopSwitcher": { "type": "object", "properties": { @@ -276,53 +79,6 @@ } }, - "TimeSwitcher": { - "type": "object", - "properties": { - "start": {"oneOf": [ - { "$ref" : "#/definitions/ConstInternal" }, - { "type": "null" } - ]}, - "stop": {"oneOf": [ - { "$ref" : "#/definitions/ConstInternal" }, - { "type": "null" } - ]}, - "period": {"oneOf": [ - { "$ref" : "#/definitions/ConstInternal" }, - { "type": "null" } - ]} - } - }, - - - - "Const": { - "type": "object", - "description": "Input value. Upper and lower describes possible values. Scale describes transformation for fitting.", - "properties": { - "free": {"oneOf": [ - { "enum": [true, false, 1, 0] }, - { "type": "null" } - ]}, - "num": {"oneOf": [ - { "type": "number" }, - { "type": "null" } - ]}, - "scale": {"oneOf": [ - { "type": "string", "enum": ["direct", "log", "logit"], "default": "direct" }, - { "type": "null" } - ]}, - "upper": {"oneOf": [ - { "type": "number" }, - { "type": "null" } - ]}, - "lower": {"oneOf": [ - { "type": "number" }, - { "type": "null" } - ]} - } - }, - "TimeScale": { "type": "object", "description": "t and other time scales", @@ -351,44 +107,6 @@ "exponent": { "type": "number" } }, "example": { "kind": "mole", "multiplier": 1e-6, "exponent": 1 } - }, - - "_Size": { - "type": "object", - "properties": { - "units": { "anyOf": [ - { "type": "number", "enum": [1] }, - { "$ref": "#/definitions/UnitsExpr" }, - { "type": "array", "items": { "$ref": "#/definitions/UnitComponent" } }, - { "type": "null" } - ] } - } - }, - - "ConstInternal": {"anyOf": [ - { "allOf": [ { "$ref": "#/definitions/Const" }, { "type": "object", "required": ["num"] } ] }, - { "$ref": "#/definitions/ID" }, - { "type": "number" }, - { "type": "null" } - ] - }, - - "Page": { - "type": "object", - "properties": { - "content": {"anyOf": [ - { "type": "string" }, - { "type": "null" } - ]} - } - }, - - "ReferenceDefinition": { - "type": "object", - "properties": { - "prefix": { "type": "string" }, - "suffix": { "type": "string" } - } } } } diff --git a/test/cases/7.tmp.js b/test/cases/7.tmp.js index ffef6a8d..e0c7f73c 100644 --- a/test/cases/7.tmp.js +++ b/test/cases/7.tmp.js @@ -41,7 +41,7 @@ describe('Testing "cases/7-importNS"', () => { }); */ it('Run @JSONExport, check and compare.', () => { - const JSONExport = b.container._componentClasses.JSONExport; + const JSONExport = b.container.classes.JSONExport; let json_export = new JSONExport; json_export._id = 'output_json'; json_export.namespace = b.container.namespaceStorage.get('two'); diff --git a/test/clone/clone-component.js b/test/clone/clone-component.js index 50d54324..770fa155 100644 --- a/test/clone/clone-component.js +++ b/test/clone/clone-component.js @@ -6,6 +6,7 @@ const { Species } = require('../../src/core/species'); describe('Clone components', () => { it('clone @Record without rename', () => { let record = new Record().merge({ + id: 'test_id', title: 'test title', notes: '**test** notes', tags: ['a', 'b', 'c'], @@ -30,6 +31,7 @@ describe('Clone components', () => { it('clone @Record with rename', () => { let record = new Species().merge({ + id: 'test_id', compartment: 'comp1', title: 'test title', aux: {x: 1.1, y: 'qwerty', z: {}}, diff --git a/test/container/knit0.js b/test/container/knit0.js index 8dd6377b..2a7580f1 100644 --- a/test/container/knit0.js +++ b/test/container/knit0.js @@ -34,6 +34,7 @@ describe('Check knitMany() for Species', () => { compartment: 'comp1' }); c.knitMany(); + console.log(c.hetaErrors()); expect(c.hetaErrors()).to.be.lengthOf(1); }); diff --git a/test/core/const.js b/test/core/const.js index 730ff0bb..1775b1e2 100644 --- a/test/core/const.js +++ b/test/core/const.js @@ -6,10 +6,10 @@ const { expect } = require('chai'); describe('Unit tests for Const.', () => { it('Minimal properties set', () => { let con = (new Const).merge({ + id: 'k1', class: 'Const', num: 1.5 }); - con._id = 'k1'; expect(con.toQ()).to.be.deep.equal({ class: 'Const', id: 'k1', @@ -18,6 +18,7 @@ describe('Unit tests for Const.', () => { }); it('Maximal properties set', () => { let con = (new Const).merge({ + id: 'k1', class: 'Const', num: 1.5, free: true, @@ -25,7 +26,6 @@ describe('Unit tests for Const.', () => { lower: 1e-9, upper: 1e9 }); - con._id = 'k1'; expect(con.toQ()).to.be.deep.equal({ class: 'Const', id: 'k1', diff --git a/test/core/d-switcher.js b/test/core/d-switcher.js index 99eca57b..2a56f55b 100644 --- a/test/core/d-switcher.js +++ b/test/core/d-switcher.js @@ -5,10 +5,10 @@ const { expect } = require('chai'); describe('Simple test for DSwitcher', () => { it('Set minimal properties', () => { let sw1 = (new DSwitcher).merge({ + id: 'sw1', trigger: '(x>1) and (y!=x)', atStart: false }); - sw1._id = 'sw1'; expect(sw1.toQ()).to.be.deep.equal({ id: 'sw1', class: 'DSwitcher', @@ -17,10 +17,10 @@ describe('Simple test for DSwitcher', () => { }); it('Set maximal properties', () => { let sw1 = (new DSwitcher).merge({ + id: 'sw1', trigger: 'x>y', atStart: true }); - sw1._id = 'sw1'; expect(sw1.toQ()).to.be.deep.equal({ id: 'sw1', class: 'DSwitcher', diff --git a/test/core/function-def.js b/test/core/function-def.js index a45a4c31..092b1f1a 100644 --- a/test/core/function-def.js +++ b/test/core/function-def.js @@ -5,14 +5,14 @@ const { expect } = require('chai'); describe('Unit test for FunctionDef', () => { const p = new Container(); it('Error: Empty FunctionDef', () => { - let simple = new p.classes.FunctionDef(); + let simple = new p.classes.FunctionDef().merge({}); expect(simple._container.logger).property('hasErrors').true; simple._container.logger.resetErrors(); }); it('Correct FunctionDef', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x1', 'x2'], math: 'sqrt(x1^2 + x2^2)' @@ -31,7 +31,7 @@ describe('Unit test for FunctionDef', () => { }); it('defineFunction without math is not ok.', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x'] }); @@ -40,7 +40,7 @@ describe('Unit test for FunctionDef', () => { }); it('Error: wrong input 1 (bad arguments).', () => { - let simple1 = new p.classes.FunctionDef({ + let simple1 = new p.classes.FunctionDef().merge({ id: 'u1', arguments: 'xxx' }); @@ -50,7 +50,7 @@ describe('Unit test for FunctionDef', () => { }); it('Error: wrong input 2 (no id).', () => { - let simple2 = new p.classes.FunctionDef({ + let simple2 = new p.classes.FunctionDef().merge({ arguments: ['xxx'] }); expect(simple2._container.logger).to.has.property('hasErrors', true); @@ -58,7 +58,7 @@ describe('Unit test for FunctionDef', () => { }); it('Error: error input 3 (math without arguments).', () => { - let simple3 = new p.classes.FunctionDef({ + let simple3 = new p.classes.FunctionDef().merge({ id: 'u3', math: '1*1' }); @@ -111,7 +111,7 @@ describe('Testing arguments vs math', () => { const p = new Container(); it('3 vs 3', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x1', 'x2', 'x3'], math: 'sqrt(x1^2 + x2^2 + x3^2 + x3^2)' @@ -122,7 +122,7 @@ describe('Testing arguments vs math', () => { }); it('3 vs 2', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x1', 'x2', 'x3'], math: 'sqrt(x1^2 + x2^2)' @@ -133,7 +133,7 @@ describe('Testing arguments vs math', () => { }); it('Error: 2 vs 3', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x1', 'x2'], math: 'sqrt(x1^2 + x2^2 + x3^2 + x3^2)' @@ -145,7 +145,7 @@ describe('Testing arguments vs math', () => { it('Error: 3 vs 3 but wrong', () => { - let simple = new p.classes.FunctionDef({ + let simple = new p.classes.FunctionDef().merge({ id: 'ud1', arguments: ['x1', 'x2', 'x666'], math: 'sqrt(x1^2 + x2^2 + x3^2 + x3^2)' diff --git a/test/core/reaction.js b/test/core/reaction.js index 5a1182c9..bff57f83 100644 --- a/test/core/reaction.js +++ b/test/core/reaction.js @@ -5,6 +5,7 @@ const { expect } = require('chai'); describe('Unit tests for Reaction.', () => { it('Check toQ.', () => { let simple = (new Reaction).merge({ + id: 'r1', class: 'Reaction', actors: [ {target: 's1', stoichiometry: -1}, @@ -18,7 +19,6 @@ describe('Unit tests for Reaction.', () => { assignments: { ode_: 'k1*s1' }, units: 'umole/h' }); - simple._id = 'r1'; expect(simple.toQ()).to.be.deep.equal({ class: 'Reaction', diff --git a/test/core/record.js b/test/core/record.js index 76ff10fb..9cf5349b 100644 --- a/test/core/record.js +++ b/test/core/record.js @@ -33,12 +33,12 @@ describe('Unit tests for Record.', () => { it('Check toQ for expression Record.', () => { let simple = (new Record).merge({ + id: 'r1', title: 'complex record', assignments: { ode_: 'm*c^2' }, units: 'J', output: true }); - simple._id = 'r1'; expect(simple.toQ()).to.be.deep.equal({ class: 'Record', id: 'r1', diff --git a/test/core/scenario.js b/test/core/scenario.js index f4b23335..b1d525f8 100644 --- a/test/core/scenario.js +++ b/test/core/scenario.js @@ -6,7 +6,7 @@ describe('Unit test for Scenario', () => { const c = new Container(); it('Minimal correct scenario', () => { - let scn1 = new c.classes.Scenario({ + let scn1 = new c.classes.Scenario().merge({ id: 'scn1', tspan: [0, 120] }); @@ -25,7 +25,7 @@ describe('Unit test for Scenario', () => { }); it('Maximal correct scenario', () => { - let scn2 = new c.classes.Scenario({ + let scn2 = new c.classes.Scenario().merge({ id: 'scn2', model: 'mouse', parameters: {weight: 20, kel: 1e-3}, @@ -51,7 +51,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "model" property', () => { - let scn3 = new c.classes.Scenario({ + let scn3 = new c.classes.Scenario().merge({ id: 'scn3', model: '123' }); @@ -61,7 +61,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "parameters" argument', () => { - let scn4 = new c.classes.Scenario({ + let scn4 = new c.classes.Scenario().merge({ id: 'scn4', parameters: [] }); @@ -71,7 +71,7 @@ describe('Unit test for Scenario', () => { }); it('No "saveat" or "tspan" arguments', () => { - let scn5 = new c.classes.Scenario({ + let scn5 = new c.classes.Scenario().merge({ id: 'scn5' }); @@ -80,7 +80,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "saveat" argument', () => { - let scn6 = new c.classes.Scenario({ + let scn6 = new c.classes.Scenario().merge({ id: 'scn6', saveat: ['a'] }); @@ -90,7 +90,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "tspan" argument', () => { - let scn7 = new c.classes.Scenario({ + let scn7 = new c.classes.Scenario().merge({ id: 'scn7', tspan: [] }); @@ -100,7 +100,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "tspan" argument', () => { - let scn8 = new c.classes.Scenario({ + let scn8 = new c.classes.Scenario().merge({ id: 'scn8', tspan: [10, 0] }); @@ -110,7 +110,7 @@ describe('Unit test for Scenario', () => { }); it('Wrong "observables" argument', () => { - let scn9 = new c.classes.Scenario({ + let scn9 = new c.classes.Scenario().merge({ id: 'scn9', observables: [0, 0] }); diff --git a/test/core/top.js b/test/core/top.js index 8f7b0749..ae0640ed 100644 --- a/test/core/top.js +++ b/test/core/top.js @@ -18,9 +18,10 @@ describe('Check Top class', () => { }); it('Top with id', () => { - let t1 = new p.classes.Top({id: 'xxx'}); + let t1 = new p.classes.Top().merge({id: 'xxx'}); + expect(t1).to.have.property('_id', 'xxx'); - expect(t1).property('isRandomId').to.be.false; + expect(t1).to.not.have.property('isRandomId'); expect(t1).to.have.property('id', 'xxx'); expect(t1.toQ()).to.be.deep.equal({id: 'xxx', action: 'defineTop'}); expect(t1._container).to.be.instanceOf(Container); @@ -29,13 +30,13 @@ describe('Check Top class', () => { p.logger.resetErrors(); }); it('Error: not a string id', () => { - let t1 = new p.classes.Top({id: 123}); + let t1 = new p.classes.Top().merge({id: 123}); expect(p.logger).property('hasErrors').true; p.logger.resetErrors(); }); it('Error: wrong id string', () => { - let t1 = new p.classes.Top({id: '123'}); + let t1 = new p.classes.Top().merge({id: '123'}); expect(p.logger).property('hasErrors').true; p.logger.resetErrors(); diff --git a/test/core/unit-def.js b/test/core/unit-def.js index 9283c251..51751738 100644 --- a/test/core/unit-def.js +++ b/test/core/unit-def.js @@ -5,14 +5,14 @@ const { expect } = require('chai'); describe('Unit test for UnitDef', () => { const p = new Container(); it('Error: Empty UnitDef', () => { - let simple = new p.classes.UnitDef(); + let simple = new p.classes.UnitDef().merge({}); expect(simple._container.logger).property('hasErrors').true; simple._container.logger.resetErrors(); }); it('Correct UnitDef', () => { - let simple = new p.classes.UnitDef({ + let simple = new p.classes.UnitDef().merge({ id: 'ud1', units: [ {kind: 'g', multiplier: 1e3, exponent: 1}, @@ -40,7 +40,7 @@ describe('Unit test for UnitDef', () => { }); it('Error: wrong input 1.', () => { - let simple1 = new p.classes.UnitDef({ + let simple1 = new p.classes.UnitDef().merge({ id: 'u1', units: ['xxx'] }); @@ -50,7 +50,7 @@ describe('Unit test for UnitDef', () => { }); it('Error: wrong input 2.', () => { - let simple2 = new p.classes.UnitDef({ + let simple2 = new p.classes.UnitDef().merge({ units: [{}] }); expect(simple2._container.logger).to.has.property('hasErrors', true); @@ -102,22 +102,22 @@ describe('Testing UnitDef with "terms"', () => { const p = new Container(); it('Error: units without "units" and "terms"', () => { - let ud0 = new p.classes.UnitDef({id: 'ud0'}); + let ud0 = new p.classes.UnitDef().merge({id: 'ud0'}); expect(ud0).to.have.property('errored').true; }); it('Error: units with "terms" only', () => { - let ud1 = new p.classes.UnitDef({id: 'ud1', terms: [{kind: 'time'}]}); + let ud1 = new p.classes.UnitDef().merge({id: 'ud1', terms: [{kind: 'time'}]}); expect(ud1).to.not.have.property('errored').true; }); it('Error: units with "units" and "terms"', () => { - let ud2 = new p.classes.UnitDef({id: 'ud2', terms: [{kind: 'time'}], units: [{kind: 'litre'}]}); + let ud2 = new p.classes.UnitDef().merge({id: 'ud2', terms: [{kind: 'time'}], units: [{kind: 'litre'}]}); expect(ud2).to.have.property('errored').true; }); it('UnitDef with empty "terms"', () => { - let ud3 = new p.classes.UnitDef({id: 'ud3', terms: []}); + let ud3 = new p.classes.UnitDef().merge({id: 'ud3', terms: []}); expect(ud3).to.not.have.property('errored').true; }); }); diff --git a/test/instance-of.js b/test/instance-of.js index d76a440b..7e647161 100644 --- a/test/instance-of.js +++ b/test/instance-of.js @@ -1,7 +1,7 @@ /* global describe, it */ const { Container } = require('../src'); let c = new Container(); -const { Page } = c._componentClasses; +const { Page } = c.classes; const { expect } = require('chai'); describe('Test for instanceOf', () => { diff --git a/test/null/null-errors.js b/test/null/null-errors.js index 6b3564b1..b1e258cc 100644 --- a/test/null/null-errors.js +++ b/test/null/null-errors.js @@ -30,7 +30,7 @@ describe('Unit tests for wrong null properties.', () => { }); it('Cannot set null for math FunctionDef', () => { - let fd = new FunctionDef({ + let fd = new FunctionDef().merge({ id: 'f1', math: null }); @@ -39,7 +39,7 @@ describe('Unit tests for wrong null properties.', () => { }); it('Cannot set null for arguments FunctionDef 1', () => { - let fd = new FunctionDef({ + let fd = new FunctionDef().merge({ id: 'f1', arguments: null, math: '15' @@ -49,7 +49,7 @@ describe('Unit tests for wrong null properties.', () => { }); it('Cannot set null for arguments FunctionDef 2', () => { - let fd = new FunctionDef({ + let fd = new FunctionDef().merge({ id: 'f1', arguments: [null, 'x1', 'x2'], math: 'x1 + x2' @@ -59,7 +59,7 @@ describe('Unit tests for wrong null properties.', () => { }); it('Cannot set null for units UnitDef', () => { - let ud = new UnitDef({ + let ud = new UnitDef().merge({ id: 'u1', units: null }); diff --git a/test/null/null-properties.js b/test/null/null-properties.js index f3506dfd..2a55144e 100644 --- a/test/null/null-properties.js +++ b/test/null/null-properties.js @@ -7,13 +7,13 @@ const { expect } = require('chai'); describe('Unit tests for null properties.', () => { it('Set all null properties', () => { let rec = (new Record).merge({ + id: 'x1', title: null, notes: null, assignments: {start_: null, ode_: null, xxx: null}, tags: null, aux: null }); - rec._id = 'x1'; expect(rec.toQ()).to.be.deep.equal({ class: 'Record', @@ -24,6 +24,7 @@ describe('Unit tests for null properties.', () => { it('Set all properties, than delete by null', () => { let rec = (new Record).merge({ + id: 'x1', title: 'Title', notes: 'Notes', units: 'mm', @@ -31,7 +32,6 @@ describe('Unit tests for null properties.', () => { tags: ['tag1', 'tag2'], aux: {a: 1, b: 2} }); - rec._id = 'x1'; rec.merge({ title: null, diff --git a/test/requirements.js b/test/requirements.js index 0c49d96c..f0e32755 100644 --- a/test/requirements.js +++ b/test/requirements.js @@ -1,7 +1,7 @@ /* global describe, it */ const { Container } = require('../src'); let c = new Container(); -const { Page, Reaction } = c._componentClasses; +const { Page, Reaction } = c.classes; const { expect } = require('chai'); describe('Test for requirements', () => { diff --git a/test/schema/c-switcher.json b/test/schema/c-switcher.json deleted file mode 100644 index eece1ca7..00000000 --- a/test/schema/c-switcher.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "class": "CSwitcher", - "id": "sw1", - "space": "one", - "trigger": "xxx" - } -] diff --git a/test/schema/const.json b/test/schema/const.json deleted file mode 100644 index 22cf5611..00000000 --- a/test/schema/const.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "class": "Const", - "id": "avogadro", - "num": 6e23, - "free": false - } -] diff --git a/test/schema/index.js b/test/schema/index.js deleted file mode 100644 index d9f4345e..00000000 --- a/test/schema/index.js +++ /dev/null @@ -1,55 +0,0 @@ -/*global describe, it*/ -const schema = require('../../src/heta.json-schema.json'); -const { expect } = require('chai'); - -const Ajv = require('ajv'); -const validator = new Ajv().addSchema(schema); - -// scoped -const record = require('./record'); -const recordError = require('./record-error'); -const process = require('./process'); -const processError = require('./process-error'); -const page = require('./page'); -const pageError = require('./page-error'); -//const switcherError = require('./c-switcher-error'); -const timeSwitcher = require('./timeSwitcher'); -const timeSwitcherError = require('./timeSwitcher-error'); - -// unscoped -const referenceDefinition = require('./reference-definition'); -const const_ = require('./const'); - -// scoped -//singleTest('Record', record, recordError); -//singleTest('Process', process, processError); -//singleTest('CSwitcher', switcher, switcherError); -//singleTest('Page', page, pageError); -//singleTest('TimeSwitcher', timeSwitcher, timeSwitcherError); - -// unscoped -singleTest('ReferenceDefinition', referenceDefinition); -singleTest('Const', const_); - -function singleTest(className, checkedArray, errorArray){ - describe(`Test ${className} instances.`, () => { - let validate = validator.getSchema(`https://hetalang.github.io#/definitions/${className}`); - // no errors - checkedArray && checkedArray.forEach((component) => { - it(`Structure OK of ${component.class} id :"${component.id}"`, () => { - let valid = validate(component); - if(!valid) console.log(validate.errors); - expect(valid).to.be.true; - }); - }); - // errors - errorArray && errorArray.forEach((component) => { - it(`Wrong structure of ${component.class} id : "${component.id}"`, () => { - validate(component); - //console.log(validate.errors); - expect(validate.errors[component.aux.validationResult.num||0].params) - .to.have.property(component.aux.validationResult.prop); - }); - }); - }); -} diff --git a/test/schema/page-error.json b/test/schema/page-error.json deleted file mode 100644 index a0124b72..00000000 --- a/test/schema/page-error.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - - { - "class": "Page", - "id": "pg6", - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - - { - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - - { - "id": "1pg8", - "content": "wrong id", - "aux": { - "validationResult": { - "num": 0, - "prop": "pattern" - } - } - } - - -] diff --git a/test/schema/page.json b/test/schema/page.json deleted file mode 100644 index 73fb0b03..00000000 --- a/test/schema/page.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "class": "Page", - "id": "pg1", - "content": "Minimal one. This is *text* with _Markdown_ notation." - } - -] diff --git a/test/schema/process-error.json b/test/schema/process-error.json deleted file mode 100644 index 9e091d16..00000000 --- a/test/schema/process-error.json +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "class": "Process", - "id": "prc6", - "space": "one", - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "Process", - "id": "pr8", - "space": "one", - "actors": [ - {} - ], - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "Process", - "id": "pr10", - "space": "one", - "actors": [], - "assignments": {}, - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - } -] diff --git a/test/schema/process.json b/test/schema/process.json deleted file mode 100644 index 95fb8200..00000000 --- a/test/schema/process.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "class": "Process", - "id": "pr1", - "space": "one", - "actors": [], - "effectors": [] - }, - { - "class": "Process", - "id": "pr2", - "space": "two", - "assignments": { - "ode_": "0.5*x1" - }, - "actors": [ - {"target": "x1", "stoichiometry": -1}, - {"target": "x2"} - ], - "effectors": [ - {"target": "x3"} - ] - } -] diff --git a/test/schema/record-error.json b/test/schema/record-error.json deleted file mode 100644 index 10554484..00000000 --- a/test/schema/record-error.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "class": "Record", - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "Record", - "id": "k4_err", - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "Record", - "id": "k10_err", - "space": "one", - "assignments": {"ode_": {}}, - "aux": { - "validationResult": { - "num": 2, - "prop": "passingSchemas" - } - } - } -] diff --git a/test/schema/record.json b/test/schema/record.json deleted file mode 100644 index 4d67b9c9..00000000 --- a/test/schema/record.json +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "class": "Record", - "id": "k1", - "title": "Record for example", - "space": "one", - "units": "1/h", - "assignments": { - "start_": 1e-4 - }, - "notes": "Hello world!\nThis _record_ is just *for example*.", - "aux": { - "prop1": "Some string", - "prop2": 3.141592, - "prop3": {"subprop": 1e-5}, - "prop4": [1, 2, 3] - } - }, - { - "class": "Record", - "id": "k2", - "space": "default_space_", - "assignments": { - "start_": 1e-4 - } - }, - { - "class": "Record", - "id": "k3", - "space": "default_space_", - "assignments": { - "ode_": "x*y" - } - }, - { - "class": "Record", - "id": "k5", - "space": "default_space_" - }, - { - "class": "Record", - "id": "k6_err", - "space": "one", - "assignments": {} - } -] diff --git a/test/schema/reference-definition.json b/test/schema/reference-definition.json deleted file mode 100644 index 53a7626f..00000000 --- a/test/schema/reference-definition.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "class": "ReferenceDefinition", - "id": "pmid", - "prefix": "https://pubmed.org", - "suffix": "/" - } -] diff --git a/test/schema/timeSwitcher-error.json b/test/schema/timeSwitcher-error.json deleted file mode 100644 index ae1b7c76..00000000 --- a/test/schema/timeSwitcher-error.json +++ /dev/null @@ -1,39 +0,0 @@ -[ - { - "class": "TimeSwitcher", - "id": "tsw1_err", - "space": "one", - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "TimeSwitcher", - "id": "tsw2_err", - "space": "one", - "start": 12, - "period": {}, - "aux": { - "validationResult": { - "num": 0, - "prop": "missingProperty" - } - } - }, - { - "class": "TimeSwitcher", - "id": "tsw3_err", - "space": "one", - "start": "12", - "period": {}, - "aux": { - "validationResult": { - "num": 1, - "prop": "pattern" - } - } - } -] diff --git a/test/schema/timeSwitcher.json b/test/schema/timeSwitcher.json deleted file mode 100644 index 8733f873..00000000 --- a/test/schema/timeSwitcher.json +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "class": "TimeSwitcher", - "id": "tsw1", - "space": "one", - "start": 0 - }, - { - "class": "TimeSwitcher", - "id": "tsw2", - "space": "one", - "start": 5.5, - "period": 12.1 - }, - { - "class": "TimeSwitcher", - "id": "tsw3", - "space": "one", - "start": 5.5, - "period": 0 - }, - { - "class": "TimeSwitcher", - "id": "tsw4", - "space": "one", - "start": 5.5, - "period": 12.2, - "repeatCount": 2 - }, - { - "class": "TimeSwitcher", - "id": "tsw5", - "space": "one", - "start": 5.5, - "period": 12.2, - "repeatCount": 0 - }, - { - "class": "TimeSwitcher", - "id": "tsw6", - "space": "one", - "start": "start0", - "period": { "num": 12.2, "units": "h" }, - "repeatCount": 0 - } -] diff --git a/test/time-switcher/bind-error.js b/test/time-switcher/bind-error.js index fba293d2..73929117 100644 --- a/test/time-switcher/bind-error.js +++ b/test/time-switcher/bind-error.js @@ -17,16 +17,17 @@ describe('TimeSwitcher errors', () => { it('Wrong reference type', () => { let c = new Container(); - c.loadMany([ - { id: 'sw1', class: 'TimeSwitcher', start: {} } - ]); + let sw1 = c.load( + { id: 'sw2', class: 'TimeSwitcher', start: {} } + ); + //console.log(sw1.namespace); expect(c.hetaErrors()).to.be.lengthOf(1); }); it('No reference', () => { let c = new Container(); c.loadMany([ - { id: 'sw1', class: 'TimeSwitcher', start: 'start' }, + { id: 'sw3', class: 'TimeSwitcher', start: 'start' }, { id: 'start', class: 'Record', assignments: { start_: 12 } } ]);