UNPKG

15 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'use strict';
15
16function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
17
18function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
19
20const Metadata = require('./metadata');
21
22const Logger = require('@accordproject/ergo-compiler').Logger;
23
24const ParserManager = require('./parsermanager');
25
26const crypto = require('crypto');
27
28const stringify = require('json-stable-stringify');
29
30const TemplateLogic = require('@accordproject/ergo-compiler').TemplateLogic;
31
32const TemplateLoader = require('./templateloader');
33
34const TemplateSaver = require('./templatesaver');
35/**
36 * A template for a legal clause or contract. A Template has a template model, request/response transaction types,
37 * a template grammar (natural language for the template) as well as Ergo code for the business logic of the
38 * template.
39 * @class
40 * @public
41 * @abstract
42 * @memberof module:cicero-core
43 */
44
45
46class Template {
47 /**
48 * Create the Template.
49 * Note: Only to be called by framework code. Applications should
50 * retrieve instances from {@link Template.fromArchive} or {@link Template.fromDirectory}.
51 * @param {object} packageJson - the JS object for package.json
52 * @param {String} readme - the readme in markdown for the template (optional)
53 * @param {object} samples - the sample text for the template in different locales
54 * @param {object} request - the JS object for the sample request
55 */
56 constructor(packageJson, readme, samples, request) {
57 this.metadata = new Metadata(packageJson, readme, samples, request);
58 this.templateLogic = new TemplateLogic('cicero');
59 this.parserManager = new ParserManager(this);
60 }
61 /**
62 * Verifies that the template is well formed.
63 * Throws an exception with the details of any validation errors.
64 */
65
66
67 validate() {
68 this.getModelManager().validateModelFiles();
69 this.getTemplateModel();
70 this.getTemplateLogic().compileLogicSync(true);
71 }
72 /**
73 * Returns the template model for the template
74 * @throws {Error} if no template model is found, or multiple template models are found
75 * @returns {ClassDeclaration} the template model for the template
76 */
77
78
79 getTemplateModel() {
80 let modelType = 'org.accordproject.cicero.contract.AccordContract';
81
82 if (this.getMetadata().getTemplateType() !== 0) {
83 modelType = 'org.accordproject.cicero.contract.AccordClause';
84 }
85
86 const templateModels = this.getIntrospector().getClassDeclarations().filter(item => {
87 return !item.isAbstract() && Template.instanceOf(item, modelType);
88 });
89
90 if (templateModels.length > 1) {
91 throw new Error("Found multiple instances of ".concat(modelType, " in ").concat(this.metadata.getName(), ". The model for the template must contain a single asset that extends ").concat(modelType, "."));
92 } else if (templateModels.length === 0) {
93 throw new Error("Failed to find an asset that extends ".concat(modelType, " in ").concat(this.metadata.getName(), ". The model for the template must contain a single asset that extends ").concat(modelType, "."));
94 } else {
95 return templateModels[0];
96 }
97 }
98 /**
99 * Returns the identifier for this template
100 * @return {String} the identifier of this template
101 */
102
103
104 getIdentifier() {
105 return this.getMetadata().getIdentifier();
106 }
107 /**
108 * Returns the metadata for this template
109 * @return {Metadata} the metadata for this template
110 */
111
112
113 getMetadata() {
114 return this.metadata;
115 }
116 /**
117 * Returns the name for this template
118 * @return {String} the name of this template
119 */
120
121
122 getName() {
123 return this.getMetadata().getName();
124 }
125 /**
126 * Returns the version for this template
127 * @return {String} the version of this template. Use semver module
128 * to parse.
129 */
130
131
132 getVersion() {
133 return this.getMetadata().getVersion();
134 }
135 /**
136 * Returns the description for this template
137 * @return {String} the description of this template
138 */
139
140
141 getDescription() {
142 return this.getMetadata().getDescription();
143 }
144 /**
145 * Gets a content based SHA-256 hash for this template. Hash
146 * is based on the metadata for the template plus the contents of
147 * all the models and all the script files.
148 * @return {string} the SHA-256 hash in hex format
149 */
150
151
152 getHash() {
153 const content = {};
154 content.metadata = this.getMetadata();
155
156 if (this.parserManager.getTemplatizedGrammar()) {
157 content.templatizedGrammar = this.parserManager.getTemplatizedGrammar();
158 } else {
159 // do not include the generated grammar because
160 // the contents is not deterministic
161 content.grammar = this.parserManager.getGrammar();
162 }
163
164 content.models = {};
165 content.scripts = {};
166 let modelFiles = this.getModelManager().getModels();
167 modelFiles.forEach(function (file) {
168 content.models[file.name] = file.content;
169 });
170 let scriptManager = this.getScriptManager();
171 let scriptFiles = scriptManager.getScripts();
172 scriptFiles.forEach(function (file) {
173 content.scripts[file.getIdentifier()] = file.contents;
174 });
175 const hasher = crypto.createHash('sha256');
176 hasher.update(stringify(content));
177 return hasher.digest('hex');
178 }
179 /**
180 * Persists this template to a Cicero Template Archive (cta) file.
181 * @param {string} [language] - target language for the archive (should be 'ergo')
182 * @param {Object} [options] - JSZip options
183 * @return {Promise<Buffer>} the zlib buffer
184 */
185
186
187 toArchive(language, options) {
188 var _this = this;
189
190 return _asyncToGenerator(function* () {
191 return TemplateSaver.toArchive(_this, language, options);
192 })();
193 }
194 /**
195 * Builds a Template from the contents of a directory.
196 * The directory must include a package.json in the root (used to specify
197 * the name, version and description of the template).
198 *
199 * @param {String} path to a local directory
200 * @param {Object} [options] - an optional set of options to configure the instance.
201 * @return {Promise<Template>} a Promise to the instantiated template
202 */
203
204
205 static fromDirectory(path, options) {
206 return _asyncToGenerator(function* () {
207 return TemplateLoader.fromDirectory(Template, path, options);
208 })();
209 }
210 /**
211 * Create a template from an archive.
212 * @param {Buffer} buffer - the buffer to a Cicero Template Archive (cta) file
213 * @return {Promise<Template>} a Promise to the template
214 */
215
216
217 static fromArchive(buffer) {
218 return _asyncToGenerator(function* () {
219 return TemplateLoader.fromArchive(Template, buffer);
220 })();
221 }
222 /**
223 * Create a template from an URL.
224 * @param {String} url - the URL to a Cicero Template Archive (cta) file
225 * @param {object} options - additional options
226 * @return {Promise} a Promise to the template
227 */
228
229
230 static fromUrl(url) {
231 var _arguments = arguments;
232 return _asyncToGenerator(function* () {
233 let options = _arguments.length > 1 && _arguments[1] !== undefined ? _arguments[1] : null;
234 return TemplateLoader.fromUrl(Template, url, options);
235 })();
236 }
237 /**
238 * Visitor design pattern
239 * @param {Object} visitor - the visitor
240 * @param {Object} parameters - the parameter
241 * @return {Object} the result of visiting or null
242 * @private
243 */
244
245
246 accept(visitor, parameters) {
247 return visitor.visit(this, parameters);
248 }
249 /**
250 * Provides access to the parser manager for this template.
251 * The parser manager can convert template data to and from
252 * natural language text.
253 * @return {ParserManager} the ParserManager for this template
254 */
255
256
257 getParserManager() {
258 return this.parserManager;
259 }
260 /**
261 * Provides access to the template logic for this template.
262 * The template logic encapsulate the code necessary to
263 * execute the clause or contract.
264 * @return {TemplateLogic} the TemplateLogic for this template
265 */
266
267
268 getTemplateLogic() {
269 return this.templateLogic;
270 }
271 /**
272 * Provides access to the Introspector for this template. The Introspector
273 * is used to reflect on the types defined within this template.
274 * @return {Introspector} the Introspector for this template
275 */
276
277
278 getIntrospector() {
279 return this.templateLogic.getIntrospector();
280 }
281 /**
282 * Provides access to the Factory for this template. The Factory
283 * is used to create the types defined in this template.
284 * @return {Factory} the Factory for this template
285 */
286
287
288 getFactory() {
289 return this.templateLogic.getFactory();
290 }
291 /**
292 * Provides access to the Serializer for this template. The Serializer
293 * is used to serialize instances of the types defined within this template.
294 * @return {Serializer} the Serializer for this template
295 */
296
297
298 getSerializer() {
299 return this.templateLogic.getSerializer();
300 }
301 /**
302 * Provides access to the ScriptManager for this template. The ScriptManager
303 * manage access to the scripts that have been defined within this template.
304 * @return {ScriptManager} the ScriptManager for this template
305 * @private
306 */
307
308
309 getScriptManager() {
310 return this.templateLogic.getScriptManager();
311 }
312 /**
313 * Provides access to the ModelManager for this template. The ModelManager
314 * manage access to the models that have been defined within this template.
315 * @return {ModelManager} the ModelManager for this template
316 * @private
317 */
318
319
320 getModelManager() {
321 return this.templateLogic.getModelManager();
322 }
323 /**
324 * Set the samples within the Metadata
325 * @param {object} samples the samples for the tempalte
326 * @private
327 */
328
329
330 setSamples(samples) {
331 this.metadata = new Metadata(this.metadata.getPackageJson(), this.metadata.getREADME(), samples, this.metadata.getRequest());
332 }
333 /**
334 * Set a locale-specified sample within the Metadata
335 * @param {object} sample the samples for the template
336 * @param {string} locale the IETF Language Tag (BCP 47) for the language
337 * @private
338 */
339
340
341 setSample(sample, locale) {
342 const samples = this.metadata.getSamples();
343 samples[locale] = sample;
344 this.metadata = new Metadata(this.metadata.getPackageJson(), this.metadata.getREADME(), samples, this.metadata.getRequest());
345 }
346 /**
347 * Set the request within the Metadata
348 * @param {object} request the samples for the template
349 * @private
350 */
351
352
353 setRequest(request) {
354 this.metadata = new Metadata(this.metadata.getPackageJson(), this.metadata.getREADME(), this.metadata.getSamples(), request);
355 }
356 /**
357 * Set the readme file within the Metadata
358 * @param {String} readme the readme in markdown for the template
359 * @private
360 */
361
362
363 setReadme(readme) {
364 this.metadata = new Metadata(this.metadata.getPackageJson(), readme, this.metadata.getSamples(), this.metadata.getRequest());
365 }
366 /**
367 * Set the packageJson within the Metadata
368 * @param {object} packageJson the JS object for package.json
369 * @private
370 */
371
372
373 setPackageJson(packageJson) {
374 this.metadata = new Metadata(packageJson, this.metadata.getREADME(), this.metadata.getSamples(), this.metadata.getRequest());
375 }
376 /**
377 * Provides a list of the input types that are accepted by this Template. Types use the fully-qualified form.
378 * @return {Array} a list of the request types
379 */
380
381
382 getRequestTypes() {
383 return this.extractFuncDeclParameter(1);
384 }
385 /**
386 * Provides a list of the response types that are returned by this Template. Types use the fully-qualified form.
387 * @return {Array} a list of the response types
388 */
389
390
391 getResponseTypes() {
392 return this.extractFuncDeclParameter(2);
393 }
394 /**
395 * Provides a list of the emit types that are emitted by this Template. Types use the fully-qualified form.
396 * @return {Array} a list of the emit types
397 */
398
399
400 getEmitTypes() {
401 return this.extractFuncDeclParameter(3);
402 }
403 /**
404 * Provides a list of the state types that are expected by this Template. Types use the fully-qualified form.
405 * @return {Array} a list of the state types
406 */
407
408
409 getStateTypes() {
410 return this.extractFuncDeclParameter(4);
411 }
412 /**
413 * Helper method to retrieve types from a function declarations
414 * @param {number} index the parameter index for the function declaration
415 * 1 - Request Types
416 * 2 - Return Types
417 * 3 - Emit Types
418 * 4 - State Types
419 * @returns {Array} a list of types
420 * @private
421 */
422
423
424 extractFuncDeclParameter(index) {
425 const functionDeclarations = this.getScriptManager().allFunctionDeclarations();
426 let types = [];
427 functionDeclarations.forEach((ele, n) => {
428 const type = ele.getParameterTypes()[index];
429
430 if (type) {
431 types.push(type);
432 }
433 });
434 Logger.debug(types);
435 return types;
436 }
437 /**
438 * Returns true if the template has logic, i.e. has more than one script file.
439 * @return {boolean} true if the template has logic
440 */
441
442
443 hasLogic() {
444 return this.getScriptManager().getAllScripts().length > 0;
445 }
446 /**
447 * Check to see if a ClassDeclaration is an instance of the specified fully qualified
448 * type name.
449 * @internal
450 * @param {ClassDeclaration} classDeclaration The class to test
451 * @param {String} fqt The fully qualified type name.
452 * @returns {boolean} True if classDeclaration an instance of the specified fully
453 * qualified type name, false otherwise.
454 */
455
456
457 static instanceOf(classDeclaration, fqt) {
458 // Check to see if this is an exact instance of the specified type.
459 if (classDeclaration.getFullyQualifiedName() === fqt) {
460 return true;
461 } // Now walk the class hierachy looking to see if it's an instance of the specified type.
462
463
464 let superTypeDeclaration = classDeclaration.getSuperTypeDeclaration();
465
466 while (superTypeDeclaration) {
467 if (superTypeDeclaration.getFullyQualifiedName() === fqt) {
468 return true;
469 }
470
471 superTypeDeclaration = superTypeDeclaration.getSuperTypeDeclaration();
472 }
473
474 return false;
475 }
476
477}
478
479module.exports = Template;
\No newline at end of file