UNPKG

cicero-core

Version:

Cicero Core - implementation of Accord Protocol Template Specification

133 lines (115 loc) 4.38 kB
/* * 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. */ 'use strict'; const logger = require('./logger'); const crypto = require('crypto'); /** * A Clause is executable business logic, linked to a natural language (legally enforceable) template. * A Clause must be constructed with a template and then prior to execution the data for the clause must be set. * Set the data for the clause (an instance of the template model) by either calling the setData method or by * calling the parse method and passing in natural language text that conforms to the template grammar. * @public * @class * @memberof module:cicero-core */ class Clause { /** * Create the Clause and link it to a Template. * @param {Template} template - the template for the clause */ constructor(template) { this.template = template; this.data = null; } /** * Set the data for the clause * @param {object} data - the data for the clause, must be an instance of the * template model for the clause's template */ setData(data) { // verify that data is an instance of the template model const templateModel = this.getTemplate().getTemplateModel(); if (data.$class !== templateModel.getFullyQualifiedName()) { throw new Error(`Invalid data, must be a valid instance of the template model ${templateModel.getFullyQualifiedName()} but got: ${JSON.stringify(data)} `); } // validate the data using the template model logger.debug('Setting clause data: ' + JSON.stringify(data)); const resource = this.template.getSerializer().fromJSON(data); resource.validate(); // passed validation! this.data = data; } /** * Get the data for the clause * @return {object} - the data for the clause, or null if it has not been set */ getData() { return this.data; } /** * Set the data for the clause by parsing natural language text. * @param {string} text - the data for the clause */ parse(text) { let parser = this.getTemplate().getParser(); parser.feed(text); if (parser.results.length !== 1) { const head = JSON.stringify(parser.results[0]); parser.results.forEach(function (element) { if (head !== JSON.stringify(element)) { throw new Error('Ambigious clause text. Got ' + parser.results.length + ' ASTs for text: ' + text); } }, this); } const ast = parser.results[0]; logger.debug('Result of parsing: ' + ast); if(!ast) { throw new Error('Parsing clause text returned a null AST. This may mean the text is valid, but not complete.'); } this.setData(ast); } /** * Returns the identifier for this clause. The identifier is the identifier of * the template plus '-' plus a hash of the data for the clause (if set). * @return {String} the identifier of this clause */ getIdentifier() { let hash = ''; if (this.data) { const textToHash = JSON.stringify(this.data); const hasher = crypto.createHash('sha256'); hasher.update(textToHash); hash = '-' + hasher.digest('hex'); } return this.getTemplate().getIdentifier() + hash; } /** * Returns the template for this clause * @return {Template} the template for this clause */ getTemplate() { return this.template; } /** * Returns a JSON representation of the clause * @return {object} the JS object for serialization */ toJSON() { return { template: this.getTemplate().getIdentifier(), data: this.getData() }; } } module.exports = Clause;