UNPKG

10.2 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
16const Logger = require('@accordproject/ergo-compiler').Logger;
17
18const ErgoCompiler = require('@accordproject/ergo-compiler').Compiler;
19
20const ciceroVersion = require('../package.json').version;
21
22const semver = require('semver');
23
24const templateTypes = {
25 CONTRACT: 0,
26 CLAUSE: 1
27};
28/**
29 * Defines the metadata for a Template, including the name, version, README markdown.
30 * @class
31 * @public
32 * @memberof module:cicero-core
33 */
34
35class Metadata {
36 /**
37 * Create the Metadata.
38 * <p>
39 * <strong>Note: Only to be called by framework code. Applications should
40 * retrieve instances from {@link Template}</strong>
41 * </p>
42 * @param {object} packageJson - the JS object for package.json (required)
43 * @param {String} readme - the README.md for the template (may be null)
44 * @param {object} samples - the sample text for the template in different locales,
45 * @param {object} request - the JS object for the sample request
46 * represented as an object whose keys are the locales and whose values are the sample text.
47 * For example:
48 * {
49 * default: 'default sample text',
50 * en: 'sample text in english',
51 * fr: 'exemple de texte français'
52 * }
53 * Locale keys (with the exception of default) conform to the IETF Language Tag specification (BCP 47).
54 * THe `default` key represents sample template text in a non-specified language, stored in a file called `sample.txt`.
55 */
56 constructor(packageJson, readme, samples, request) {
57 // name of the runtime that this template targets (if the template contains compiled code)
58 this.runtime = null; // the version of Cicero that this template is compatible with
59
60 this.ciceroVersion = null; // the version of Ergo that the Ergo source in this template is compatible with (if the tempalte contains source code)
61
62 this.ergoVersion = null;
63
64 if (!packageJson || typeof packageJson !== 'object') {
65 throw new Error('package.json is required and must be an object');
66 }
67
68 if (!packageJson.accordproject) {
69 throw new Error('Failed to find accordproject metadata in package.json');
70 }
71
72 if (!packageJson.accordproject.cicero) {
73 throw new Error('Failed to find accordproject cicero version in package.json');
74 }
75
76 if (!semver.valid(semver.coerce(packageJson.accordproject.cicero))) {
77 throw new Error('The cicero version must be a valid semantic version (semver) number.');
78 }
79
80 this.ciceroVersion = packageJson.accordproject.cicero;
81
82 if (!this.satisfiesCiceroVersion(ciceroVersion)) {
83 const msg = "The template targets Cicero (".concat(this.ciceroVersion, ") but the Cicero version is ").concat(ciceroVersion, ".");
84 Logger.error(msg);
85 throw new Error(msg);
86 } // the runtime property is optional, and is only present for templates that have been compiled
87
88
89 if (packageJson.accordproject.runtime && packageJson.accordproject.runtime !== 'ergo') {
90 ErgoCompiler.isValidTarget(packageJson.accordproject.runtime);
91 } else {
92 packageJson.accordproject.runtime = 'ergo';
93 }
94
95 this.runtime = packageJson.accordproject.runtime; // ergo property is optional, must be present for templates containing Ergo code
96
97 if (packageJson.accordproject.ergo) {
98 this.ergoVersion = packageJson.accordproject.ergo;
99 }
100
101 if (!samples || typeof samples !== 'object') {
102 throw new Error('sample.txt is required');
103 }
104
105 if (request && typeof request !== 'object') {
106 throw new Error('request.json must be an object');
107 }
108
109 if (!packageJson.name || !this._validName(packageJson.name)) {
110 throw new Error('template name can only contain lowercase alphanumerics, _ or -');
111 }
112
113 this.packageJson = packageJson;
114
115 if (readme && typeof readme !== 'string') {
116 throw new Error('README must be a string');
117 }
118
119 if (!packageJson.keywords) {
120 packageJson.keywords = [];
121 }
122
123 if (packageJson.keywords && !Array.isArray(packageJson.keywords)) {
124 throw new Error('keywords property in package.json must be an array.');
125 }
126
127 this.readme = readme;
128 this.samples = samples;
129 this.request = request;
130 this.type = templateTypes.CONTRACT;
131
132 if (packageJson.accordproject && packageJson.accordproject.template) {
133 if (packageJson.accordproject.template !== 'contract' && packageJson.accordproject.template !== 'clause') {
134 throw new Error('A cicero template must be either a "contract" or a "clause".');
135 }
136
137 if (packageJson.accordproject.template === 'clause') {
138 this.type = templateTypes.CLAUSE;
139 }
140 } else {
141 Logger.warn('No cicero template type specified. Assuming that this is a contract template');
142 }
143 }
144 /**
145 * check to see if it is a valid name. for some reason regex is not working when this executes
146 * inside the chaincode runtime, which is why regex hasn't been used.
147 *
148 * @param {string} name the template name to check
149 * @returns {boolean} true if valid, false otherwise
150 *
151 * @private
152 */
153
154
155 _validName(name) {
156 const validChars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'];
157
158 for (let i = 0; i < name.length; i++) {
159 const strChar = name.charAt(i);
160
161 if (validChars.indexOf(strChar) === -1) {
162 return false;
163 }
164 }
165
166 return true;
167 }
168 /**
169 * Returns either a 0 (for a contract template), or 1 (for a clause template)
170 * @returns {number} the template type
171 */
172
173
174 getTemplateType() {
175 return this.type;
176 }
177 /**
178 * Returns the name of the runtime target for this template, or null if this template
179 * has not been compiled for a specific runtime.
180 * @returns {string} the name of the runtime
181 */
182
183
184 getRuntime() {
185 return this.runtime;
186 }
187 /**
188 * Returns the Ergo version that the Ergo code in this template is compatible with. This
189 * is null for templates that do not contain source Ergo code.
190 * @returns {string} the version of Ergo
191 */
192
193
194 getErgoVersion() {
195 return this.ergoVersion;
196 }
197 /**
198 * Returns the version of Cicero that this template is compatible with.
199 * i.e. which version of the runtime was this template built for?
200 * The version string conforms to the semver definition
201 * @returns {string} the semantic version
202 */
203
204
205 getCiceroVersion() {
206 return this.ciceroVersion;
207 }
208 /**
209 * Only returns true if the current cicero version satisfies the target version of this template
210 * @param {string} version the cicero version to check against
211 * @returns {string} the semantic version
212 */
213
214
215 satisfiesCiceroVersion(version) {
216 return semver.satisfies(version, this.getCiceroVersion(), {
217 includePrerelease: true
218 });
219 }
220 /**
221 * Returns the samples for this template.
222 * @return {object} the sample files for the template
223 */
224
225
226 getSamples() {
227 return this.samples;
228 }
229 /**
230 * Returns the sample request for this template.
231 * @return {object} the sample request for the template
232 */
233
234
235 getRequest() {
236 return this.request;
237 }
238 /**
239 * Returns the sample for this template in the given locale. This may be null.
240 * If no locale is specified returns the default sample if it has been specified.
241 *
242 * @param {string} locale the IETF language code for the language.
243 * @return {string} the sample file for the template in the given locale or null
244 */
245
246
247 getSample() {
248 let locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
249
250 if (!locale && 'default' in this.samples) {
251 return this.samples.default;
252 } else if (locale && locale in this.samples) {
253 return this.samples[locale];
254 } else {
255 return null;
256 }
257 }
258 /**
259 * Returns the README.md for this template. This may be null if the template does not have a README.md
260 * @return {String} the README.md file for the template or null
261 */
262
263
264 getREADME() {
265 return this.readme;
266 }
267 /**
268 * Returns the package.json for this template.
269 * @return {object} the Javascript object for package.json
270 */
271
272
273 getPackageJson() {
274 return this.packageJson;
275 }
276 /**
277 * Returns the name for this template.
278 * @return {string} the name of the template
279 */
280
281
282 getName() {
283 return this.packageJson.name;
284 }
285 /**
286 * Returns the name for this template.
287 * @return {Array} the name of the template
288 */
289
290
291 getKeywords() {
292 if (this.packageJson.keywords.length < 1 || this.packageJson.keywords === undefined) {
293 return [];
294 } else {
295 return this.packageJson.keywords;
296 }
297 }
298 /**
299 * Returns the description for this template.
300 * @return {string} the description of the template
301 */
302
303
304 getDescription() {
305 return this.packageJson.description;
306 }
307 /**
308 * Returns the version for this template.
309 * @return {string} the description of the template
310 */
311
312
313 getVersion() {
314 return this.packageJson.version;
315 }
316 /**
317 * Returns the identifier for this template, formed from name@version.
318 * @return {string} the identifier of the template
319 */
320
321
322 getIdentifier() {
323 return this.packageJson.name + '@' + this.packageJson.version;
324 }
325 /**
326 * Return new Metadata for a target runtime
327 * @param {string} runtimeName - the target runtime name
328 * @return {object} the new Metadata
329 */
330
331
332 createTargetMetadata(runtimeName) {
333 const packageJson = JSON.parse(JSON.stringify(this.packageJson));
334 packageJson.accordproject.runtime = runtimeName;
335 return new Metadata(packageJson, this.readme, this.samples, this.request);
336 }
337
338}
339
340module.exports = Metadata;
\No newline at end of file