UNPKG

@discipl/law-reg

Version:

Discipl Law and Regulation Compliance Library

289 lines (234 loc) 9.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProjectionExpressionChecker = void 0; var _baseSubExpressionChecker = require("./baseSubExpressionChecker"); var _defaultFactResolver = require("../defaultFactResolver"); var _index = require("../index"); class ProjectionExpressionChecker extends _baseSubExpressionChecker.BaseSubExpressionChecker { /** * Create a SubExpressionChecker * @param {ServiceProvider} serviceProvider */ constructor(serviceProvider) { super(serviceProvider); this.scopeCheckers = { 'single': new SingleScopeProjectionExpressionChecker(serviceProvider), 'all': new AllScopeProjectionExpressionChecker(serviceProvider), 'some': new SomeScopeProjectionExpressionChecker(serviceProvider) }; } async checkSubExpression(fact, ssid, context) { const scope = fact.scope ? fact.scope : 'single'; this.logger.debug('Handling PROJECTION Expression with', scope, 'scope'); return this.scopeCheckers[scope].checkSubExpression(fact, ssid, context); } } exports.ProjectionExpressionChecker = ProjectionExpressionChecker; class BaseScopeProjectionExpressionChecker extends _baseSubExpressionChecker.BaseSubExpressionChecker { _checkProjectionIsValid(fact) { if (!fact.context || fact.context.length === 0) { throw new Error('A \'context\' array must be given for the PROJECTION expression'); } if (!fact.operand) { // TODO deprecate projection expression fact property if (fact.fact !== undefined) { fact.operand = fact.fact; } else { throw new Error('A \'operand\' must be given for the PROJECTION expression'); } } } /** * Get filtered creating acts * @param {object} fact - the create expression * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<CreatingAct[]>} - Object where key is the act link and value is the provided facts * @protected */ async _getCreatingActs(fact, ssid, context) { const contextFact = fact.context[0]; const creatingActs = await this._getFactChecker().getCreatingActs(contextFact, ssid, context); const filteredActs = await this._filter(creatingActs, contextFact, ssid, context); return Promise.all(filteredActs.map(act => this._reduce(act, fact.context.slice(1), ssid))); } /** * Reduce the create act using the context array to get the targets creating act * @param {CreatingAct} creatingAct - The current creating act * @param {string[]} contextArray - The remaining context facts to reduce * @param {ssid} ssid - Identifies the actor * @return {Promise<CreatingAct>} * @protected */ async _reduce(creatingAct, contextArray, ssid) { this.logger.debug('Case for', creatingAct.contextFact, 'is', creatingAct.link, 'with facts', creatingAct.facts); const contextFact = contextArray[0]; const nextLink = creatingAct.facts[contextFact]; if (nextLink) { const newCreatingAct = await this._getCreatingAct(nextLink, contextFact, ssid); return this._reduce(newCreatingAct, contextArray.slice(1), ssid); } return creatingAct; } /** * Get creating act for an act link * @param {string} actLink - Act link * @param {string} contextFact - The fact that was created by this act * @param {ssid} ssid - Identifies the actor * @return {Promise<CreatingAct>} * @private */ async _getCreatingAct(actLink, contextFact, ssid) { const actData = await this._getAbundanceService().getCoreAPI().get(actLink, ssid); const result = {}; result.link = actLink; result.contextFact = contextFact; result.facts = actData.data[_index.DISCIPL_FLINT_FACTS_SUPPLIED]; return result; } /** * Resolve the value of a fact * @param {object} fact - the create expression * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @param {Object<string, *>} providedFacts - the facts provided by the create expression * @return {Promise<*>} * @protected */ async _resolve(fact, ssid, context, providedFacts) { const newContext = { ...context }; newContext.factResolver = (0, _defaultFactResolver.wrapWithDefault)(context.factResolver, providedFacts); let result = await this._getFactChecker().checkFactWithResolver(fact.operand, ssid, newContext); // noinspection JSUnresolvedVariable if (typeof result === 'object' && result.expression) { result = await this._getFactChecker().checkFact(result, ssid, newContext); } else if (result === undefined) { result = await this._getFactChecker().checkFact(fact.operand, ssid, newContext); } return result; } /** * Filter the creating acts (Should be implemented in sub expressions) * @param {CreatingAct[]} creatingActs - the creating acts * @param {string} contextFact - the context fact * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<CreatingAct[]>} * @protected */ async _filter(creatingActs, contextFact, ssid, context) { throw new Error('Not implemented'); } /** * Combine the values of creating acts into one provided facts object * @param {CreatingAct[]} creatingActs - the creating acts * @return {Object<string, *[]>} - object where key is the fact and value is an array of the facts values * @protected */ _toProvidedFacts(creatingActs) { const addCreatingAct = (providedFacts, creatingAct) => { const facts = creatingAct.facts; for (const fact in facts) { if (facts.hasOwnProperty(fact)) { const array = providedFacts[fact] ? providedFacts[fact] : []; array.push(facts[fact]); providedFacts[fact] = array; } } return providedFacts; }; return creatingActs.reduce((previousValue, currentValue) => addCreatingAct(previousValue, currentValue), {}); } } class SingleScopeProjectionExpressionChecker extends BaseScopeProjectionExpressionChecker { async checkSubExpression(fact, ssid, context) { this._checkProjectionIsValid(fact); const creatingActs = await this._getCreatingActs(fact, ssid, context); if (creatingActs.length !== 1) { context.searchingFor = fact.operand; return this._checkAtLeastTypeOf(fact.operand, ssid, context); } return this._resolve(fact, ssid, context, creatingActs[0].facts); } /** * Check if current actor is an actor of type searchingFor * @param {string} searchingFor - The fact we are searching for * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<undefined|boolean>} * @private */ async _checkAtLeastTypeOf(searchingFor, ssid, context) { if (context.myself) { this.logger.debug('Multiple creating acts found. Checking if you are at least a', searchingFor); const isActorType = await this._getFactChecker().checkFact(searchingFor, ssid, context); this.logger.debug('Resolved you are a', searchingFor, 'as', isActorType); return isActorType ? undefined : false; } return undefined; } /** * Filter the creating acts * @param {CreatingAct[]} creatingActs - the creating acts * @param {string} contextFact - the context fact * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<CreatingAct[]>} * @protected */ async _filter(creatingActs, contextFact, ssid, context) { const actLinks = creatingActs.map(act => act.link); const resolverLink = await this._getFactChecker().checkFactWithResolver(contextFact, ssid, context, actLinks); return creatingActs.filter(act => resolverLink === act.link); } } class AllScopeProjectionExpressionChecker extends BaseScopeProjectionExpressionChecker { async checkSubExpression(fact, ssid, context) { this._checkProjectionIsValid(fact); const creatingActs = await this._getCreatingActs(fact, ssid, context); if (creatingActs.length <= 0) return false; const providedFacts = this._toProvidedFacts(creatingActs); return this._resolve(fact, ssid, context, providedFacts); } /** * Filter the creating acts * @param {CreatingAct[]} creatingActs - the creating acts * @param {string} contextFact - the context fact * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<CreatingAct[]>} * @protected */ async _filter(creatingActs, contextFact, ssid, context) { return creatingActs; } } class SomeScopeProjectionExpressionChecker extends BaseScopeProjectionExpressionChecker { async checkSubExpression(fact, ssid, context) { this._checkProjectionIsValid(fact); const creatingActs = await this._getCreatingActs(fact, ssid, context); if (creatingActs.length <= 0) return false; const providedFacts = this._toProvidedFacts(creatingActs); return this._resolve(fact, ssid, context, providedFacts); } /** * Filter the creating acts * @param {CreatingAct[]} creatingActs - the creating acts * @param {string} contextFact - the context fact * @param {ssid} ssid - Identifies the actor * @param {Context} context - Context of the action * @return {Promise<CreatingAct[]>} * @protected */ async _filter(creatingActs, contextFact, ssid, context) { const actLinks = creatingActs.map(act => act.link); /** * @type {string[]} */ let resolverLinks = await this._getFactChecker().checkFactWithResolver(contextFact, ssid, context, actLinks); if (!Array.isArray(resolverLinks)) resolverLinks = []; return creatingActs.filter(act => resolverLinks.includes(act.link)); } }