/*
* Copyright (C) 2021 - 2024, SanteSuite Inc. and the SanteSuite Contributors (See NOTICE.md for full copyright notices)
* Copyright (C) 2019 - 2021, Fyfe Software Inc. and the SanteSuite Contributors
* Portions Copyright (C) 2015-2018 Mohawk College of Applied Arts and Technology
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
if(!SanteDBBre)
var SanteDBBre =
/**
* @class
* @constructor
* @summary Environment for executing SanteDB business rules in JavaScript
* @description The BRE functions in this class are normally provided by the JINT JavaScript engine via C# classes. This wrapper is used by non
* JINT server environments to provide local execution support for business rules.
*/
new function () {
// Issue priority
var IssuePriority = {
/**
* @summary The issue raised is an error, processing cannot continue
*/
Error: 1,
/**
* @summary The issue raised is a warning, processing can continue however subsequent errors may occur
*/
Warning: 2,
/**
* @summary The issue raised can be ignored and is simply raised for information
*/
Information: 3
};
// Detected issue
var DetectedIssue = function (text, priority) {
/**
* @property {string}
* @memberof SanteDBBre.DetectedIssue
* @summary The textual information of the issue
*/
this.text = text;
/**
* @property {SanteDBBre.IssuePriority}
* @memberof SanteDBBre.DetectedIssue
* @summary The priority / severity of the issue
*/
this.priority = priority;
};
/**
* @property {SanteDBBre.ExecutionEnvironment}
* @memberof SanteDBBre
* @summary Indicates whether the business rules are running in the front-end or server side
*/
this.Environment = __SanteDBAppService.ExecutionEnvironment;
/**
* @method
* @private
* @summary Evaluates the guard instance
* @param {any} guard The guard which is being validated
* @param {any} instance The instance of the guard
* @returns {bool} True if the guard condition evaluated to true
*/
function _guardEval(guard, instance) {
var retVal = true;
for(gk in guard)
{
if (gk.indexOf(".") > -1 || gk.indexOf("[") > -1)
throw new Exception("BusinessRuleException", "error.businessRule", "Rule guards can only be simple property paths");
var subCond = false;
for(gv in guard[gk])
subCond |= instance[gk] === guard[gk][gv];
retVal &= subCond;
}
return retVal;
}
// Reference sets loaded
var _refs = {};
// Reference set urls
var _refsBase = [];
// Triggers loaded
var _triggers = [];
// Validators loaded
var _validators = [];
// New Guid function for front end
this.NewGuid = SanteDB.application.newGuid;
/**
* @class
* @constructor
* @summary Represents an issue that has been detected from the BRE with data
* @description Detected issues are typically caught by the business rules engine and returned as part of a 422 or other error process
* @memberof SanteDBBre
* @param {string} text The textual information for the detected issue
* @param {SanteDBBre.IssuePriority} priority The priority of the issue raised
*/
this.DetectedIssue = DetectedIssue;
/**
* @enum
* @summary Represents issue priority
* @memberof SanteDBBre
*/
this.IssuePriority = IssuePriority;
/**
* @enum
* @summary Represents execution environments
* @memberof SanteDBBre
*/
this.ExecutionEnvironment = ExecutionEnvironment;
/**
* @method
* @memberof SanteDBBre
* @summary Converts object to view model
* @param {any} object The object to convert
* @returns {any} The converted object
* @wrapper
*/
this.ToViewModel = function (object) { return object; };
/**
* @method
* @memberof SanteDBBre
* @summary Converts an object from view model
* @param {any} object The object to convert
* @returns {any} The converted object
* @wrapper
*/
this.FromViewModel = function (object) { return object; };
/**
* @method
* @memberof SanteDBBre
* @summary Adds a business rule to the business rule engine
* @description This method will add a business rule to the engine. A business rule can be executed before, after insert, update, delete or query. The business rules
* engine also allows for the passing of a guard condition which guards execution of the method only if the inbound object matches the provided guard.
* @param {string} id A unique identifier for this trigger (to prevent duplicate execution)
* @param {string} type The type of object the trigger is being assigned to
* @param {string} trigger The trigger for the object
* @param {any} guard The guard condition for the trigger. Note that these can only be simple property evaluations of property=value
* @param {function(any):void} callback The callback
* @example
* // Before an observation is inserted add a tag that sets reviewStatus if the obs is a weight and quantity observation
* SanteDBBre.AddBusinessRule("QuantityObservation", "BeforeInsert", { "typeConcept" : "a261f8cd-69b0-49aa-91f4-e6d3e5c612ed" }, function(obs) {
* // Have we already tagged?
* obs.tag = obs.tag || {};
* if(!obs.tag["reviewStatus"])
* obs.tag["reviewStatus"] = "NeedsApproval";
* return obs;
* });
*/
this.AddBusinessRule = function (id, type, trigger, guard, callback) {
_triggers.push({
id: id,
type: type,
trigger: trigger,
guard: guard,
callback: callback
});
};
/**
* @method
* @memberof SanteDBBre
* @summary Adds a validator to the current execution context
* @description This method will add a validator to the current execution context. A validator is run prior to persistence
* and is even run in the user interface before submitting the object to do a sanity check on the object.
* @param {string} type The type of object being registered
* @param {function} callback The callback function for validation, this function should return an array of DetectedIssue
* @see SanteDBBre.DetectedIssue
* @example
* // Add validator to make sure a completed act doesn't occur in the future
* SantEDBBre.AddValidator("Act", function(act) {
* var issues = [];
* if(act.statusCocnept == OpenIZModel.StatusKeys.Complete &&
* act.actTime > new Date())
* issues.push(new OpenIZBre.DetectedIssue("locale.error.actInTheFuture", OpenIZBre.IssuePriority.Error));
* return issues;
* });
* });
*/
this.AddValidator = function (type, callback) {
_validators.push({
type: type,
callback: callback
});
};
/**
* @method
* @memberof SanteDBBre
* @summary Simulates the rule being executed
* @description This function can be used by callers to execute the specified trigger for the specified instance. The instance
* is expected to be a valid class from OpenIZModel namespace
* @see SanteDBModel
* @param {string} trigger The trigger execute
* @param {any} instance The instance to execute the trigger on
* @returns {any} The object that has been modified after the rule executed
* @example
* // In my user interface I want to call beforeInsert to get the interpretation of the observation
* if(myObservation) {
* var beforeInsert = SanteDBBre.ExecuteRule("BeforeInsert", myObservation);
* alert("Observation was interpreted as " + myObservation.interpretationConcept);
* }
*/
this.ExecuteRule = function (trigger, instance) {
// Execute the rule
var retVal = instance;
for (var t in this._triggers)
if (_triggers[t].type === instance.$type && _triggers[t].trigger === trigger
&& _guardEval(_triggers[t].guard, instance)) {
var triggerResult = this._triggers[t].callback(retVal);
retVal = triggerResult || retVal;
}
return retVal;
};
/**
* @method
* @memberof SanteDBBre
* @summary Performs the validation function on the specified instance
* @description Calling this method will invoke all registered validators on the specified instance and will return a
* collection of DetectedIssues.
* @see SanteDBBre.DetectedIssue
* @param {any} instance The instance to be validated.
* @returns {SanteDBBre.DetectedIssue} The detected issues with the instances
* @example
* if(myObservation) {
* var issues = OpenIZBre.Validate(myObservation);
* alert("There are " + issues.length + " issues with this observation");
* }
*/
this.Validate = function (instance) {
// Execute the rule
var retVal = [];
for (var t in this._validators)
if (_validators[t].type === instance.$type) {
var issues = _validators[t].callback(instance);
for (var i in issues)
retVal.push(issues[i]);
}
return retVal;
};
}();