UNPKG

12.7 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15'use strict';
16
17const Logger = require('@accordproject/ergo-compiler').Logger;
18const Util = require('@accordproject/ergo-compiler').Util;
19
20/**
21 * <p>
22 * Engine class. Execution of template logic against a request object, returning a response to the caller.
23 * </p>
24 * @class
25 * @public
26 * @memberof module:ergo-engine
27 */
28class Engine {
29 /**
30 * Create the Engine.
31 */
32 constructor() {
33 this.scripts = {};
34 }
35
36 /**
37 * Engine kind
38 * @return {string} which kind of engine
39 */
40 kind() {
41 return 'empty';
42 }
43
44 /**
45 /**
46 * Compile a script for a JavaScript machine
47 * @param {string} script - the script
48 */
49 compileVMScript(script) {
50 throw new Error('[compileVMScript] Cannot execute Engine: instantiate either VMEngine or EvalEngine');
51 }
52
53 /**
54 * Execute a call in a JavaScript machine
55 * @param {number} utcOffset - UTC Offset for this execution
56 * @param {object} context - global variables to set in the VM
57 * @param {object} script - the initial script to load
58 * @param {object} call - the execution call
59 */
60 runVMScriptCall(utcOffset,context,script,call) {
61 throw new Error('[runVMScriptCall] Cannot execute Engine: instantiate either VMEngine or EvalEngine');
62 }
63
64 /**
65 * Clear the JavaScript logic cache
66 * @private
67 */
68 clearCacheJsScript() {
69 this.scripts = {};
70 }
71
72 /**
73 * Compile and cache JavaScript logic
74 * @param {ScriptManager} scriptManager - the script manager
75 * @param {string} contractId - the contract identifier
76 * @return {VMScript} the cached script
77 * @private
78 */
79 cacheJsScript(scriptManager, contractId) {
80 if (!this.scripts[contractId]) {
81 const allJsScripts = scriptManager.getCompiledJavaScript();
82 const script = this.compileVMScript(allJsScripts);
83 this.scripts[contractId] = script;
84 }
85 return this.scripts[contractId];
86 }
87
88 /**
89 * Trigger a clause, passing in the request object
90 * @param {LogicManager} logic - the logic
91 * @param {string} contractId - the contract identifier
92 * @param {object} contract - the contract data
93 * @param {object} request - the request, a JS object that can be deserialized
94 * using the Composer serializer.
95 * @param {object} state - the contract state, a JS object that can be deserialized
96 * using the Composer serializer.
97 * @param {string} currentTime - the definition of 'now'
98 * @param {object} options to the text generation
99 * @return {Promise} a promise that resolves to a result for the clause
100 */
101 async trigger(logic, contractId, contract, request, state, currentTime, options) {
102 // Set the current time and UTC Offset
103 const now = Util.setCurrentTime(currentTime);
104 const utcOffset = now.utcOffset();
105 const validOptions = options ? options : {
106 options: {
107 '$class': 'org.accordproject.ergo.options.Options',
108 'wrapVariables': false,
109 'template': false,
110 }
111 };
112
113 const validContract = logic.validateContract(contract); // ensure the contract is valid
114 const validRequest = logic.validateInput(request); // ensure the request is valid
115 const validState = logic.validateInput(state); // ensure the state is valid
116
117 Logger.debug('Engine processing request ' + request.$class + ' with state ' + state.$class);
118
119 const script = this.cacheJsScript(logic.getScriptManager(), contractId);
120 const callScript = logic.getDispatchCall();
121
122 const context = {
123 data: validContract.serialized,
124 state: validState,
125 request: validRequest
126 };
127
128 // execute the logic
129 const result = this.runVMScriptCall(utcOffset,now,validOptions,context,script,callScript);
130
131 const validResponse = logic.validateOutput(result.__response); // ensure the response is valid
132 const validNewState = logic.validateOutput(result.__state); // ensure the new state is valid
133 const validEmit = logic.validateOutputArray(result.__emit); // ensure all the emits are valid
134
135 const answer = {
136 'clause': contractId,
137 'request': request, // Keep the original request
138 'response': validResponse,
139 'state': validNewState,
140 'emit': validEmit,
141 };
142 return Promise.resolve(answer);
143 }
144
145 /**
146 * Invoke a clause, passing in the parameters for that clause
147 * @param {LogicManager} logic - the logic
148 * @param {string} contractId - the contract identifier
149 * @param {string} clauseName - the clause name
150 * @param {object} contract - the contract data
151 * @param {object} params - the clause parameters
152 * @param {object} state - the contract state, a JS object that can be deserialized
153 * using the Composer serializer.
154 * @param {string} currentTime - the definition of 'now'
155 * @param {object} options to the text generation
156 * @return {Promise} a promise that resolves to a result for the clause
157 */
158 async invoke(logic, contractId, clauseName, contract, params, state, currentTime, options) {
159 // Set the current time and UTC Offset
160 const now = Util.setCurrentTime(currentTime);
161 const utcOffset = now.utcOffset();
162 const validOptions = options ? options : {
163 options: {
164 '$class': 'org.accordproject.ergo.options.Options',
165 'wrapVariables': false,
166 'template': false,
167 }
168 };
169
170 const validContract = logic.validateContract(contract, options); // ensure the contract is valid
171 const validParams = logic.validateInputRecord(params); // ensure the parameters are valid
172 const validState = logic.validateInput(state); // ensure the state is valid
173
174 Logger.debug('Engine processing clause ' + clauseName + ' with state ' + state.$class);
175
176 const script = this.cacheJsScript(logic.getScriptManager(), contractId);
177 const callScript = logic.getInvokeCall(clauseName);
178 const context = {
179 data: validContract.serialized,
180 state: validState,
181 params: validParams
182 };
183
184 // execute the logic
185 const result = this.runVMScriptCall(utcOffset,now,validOptions,context,script,callScript);
186
187 const validResponse = logic.validateOutput(result.__response); // ensure the response is valid
188 const validNewState = logic.validateOutput(result.__state); // ensure the new state is valid
189 const validEmit = logic.validateOutputArray(result.__emit); // ensure all the emits are valid
190
191 const answer = {
192 'clause': contractId,
193 'params': params, // Keep the original params
194 'response': validResponse,
195 'state': validNewState,
196 'emit': validEmit,
197 };
198 return Promise.resolve(answer);
199 }
200
201 /**
202 * Initialize a clause
203 * @param {LogicManager} logic - the logic
204 * @param {string} contractId - the contract identifier
205 * @param {object} contract - the contract data
206 * @param {object} params - the clause parameters
207 * @param {string} currentTime - the definition of 'now'
208 * @param {object} options to the text generation
209 * @return {Promise} a promise that resolves to a result for the clause initialization
210 */
211 async init(logic, contractId, contract, params, currentTime, options) {
212 const defaultState = {
213 '$class':'org.accordproject.cicero.contract.AccordContractState',
214 'stateId':'org.accordproject.cicero.contract.AccordContractState#1'
215 };
216 return this.invoke(logic, contractId, 'init', contract, params, defaultState, currentTime, options);
217 }
218
219 /**
220 * Generate Text
221 * @param {TemplateLogic} logic - the logic
222 * @param {string} contractId - the contract identifier
223 * @param {object} contract - the contract data
224 * @param {object} params - the clause parameters
225 * @param {string} currentTime - the definition of 'now'
226 * @param {object} options to the text generation
227 * @return {Promise} a promise that resolves to a result for the clause initialization
228 */
229 async draft(logic, contractId, contract, params, currentTime, options) {
230 options = options || {};
231
232 const defaultState = {
233 '$class':'org.accordproject.cicero.contract.AccordContractState',
234 'stateId':'org.accordproject.cicero.contract.AccordContractState#1'
235 };
236 return this.invoke(logic, contractId, 'toText', contract, params, defaultState, currentTime, Object.assign(options, {convertResourcesToId: true}));
237 }
238
239 /**
240 * Compile then initialize a clause
241 *
242 * @param {LogicManager} logic - the logic
243 * @param {object} contract - the contract data
244 * @param {object} params - the clause parameters
245 * @param {string} currentTime - the definition of 'now'
246 * @param {object} options to the text generation
247 * @return {Promise} a promise that resolves to a result for the clause initialization
248 */
249 compileAndInit(logic, contract, params, currentTime, options) {
250 return logic.compileLogic(false).then(() => {
251 const contractId = logic.getContractName();
252 return this.init(logic, contractId, contract, params, currentTime, options);
253 });
254 }
255
256 /**
257 * Compile then generate text
258 *
259 * @param {TemplateLogic} logic - the logic
260 * @param {object} contract - the contract data
261 * @param {object} params - the clause parameters
262 * @param {string} currentTime - the definition of 'now'
263 * @param {object} options to the text generation
264 * @return {Promise} a promise that resolves to a result for the clause initialization
265 */
266 compileAndDraft(logic, contract, params, currentTime, options) {
267 return logic.compileLogic(false).then(() => {
268 const contractId = logic.getContractName();
269 return this.draft(logic, contractId, contract, params, currentTime, options);
270 });
271 }
272
273 /**
274 * Compile then invoke a clause
275 *
276 * @param {LogicManager} logic - the logic
277 * @param {string} clauseName - the clause name
278 * @param {object} contract contract data in JSON
279 * @param {object} params - the clause parameters
280 * @param {object} state - the contract state, a JS object that can be deserialized
281 * using the Composer serializer.
282 * @param {string} currentTime - the definition of 'now'
283 * @param {object} options to the text generation
284 * @return {Promise} a promise that resolves to a result for the clause initialization
285 */
286 compileAndInvoke(logic, clauseName, contract, params, state, currentTime, options) {
287 return logic.compileLogic(false).then(() => {
288 const contractId = logic.getContractName();
289 return this.invoke(logic, contractId, clauseName, contract, params, state, currentTime, options);
290 });
291 }
292
293 /**
294 * Compile then trigger a clause, passing in the request object
295 *
296 * @param {LogicManager} logic - the logic
297 * @param {object} contract - the contract data
298 * @param {object} request - the request, a JS object that can be deserialized
299 * using the Composer serializer.
300 * @param {object} state - the contract state, a JS object that can be deserialized
301 * using the Composer serializer.
302 * @param {string} currentTime - the definition of 'now'
303 * @param {object} options to the text generation
304 * @return {Promise} a promise that resolves to a result for the clause
305 */
306 compileAndTrigger(logic, contract, request, state, currentTime, options) {
307 return logic.compileLogic(false).then(() => {
308 const contractId = logic.getContractName();
309 return this.trigger(logic, contractId, contract, request, state, currentTime, options);
310 });
311 }
312
313}
314
315module.exports = Engine;