1 | var fs, getDescriptions, getSchemas, isAllowedTypeless, mergeAllOf, path, refParser, renderCapabilities, renderMessage, renderMessages, renderProperty, schemas;
|
2 |
|
3 | fs = require('fs');
|
4 |
|
5 | path = require('path');
|
6 |
|
7 | refParser = require('json-schema-ref-parser');
|
8 |
|
9 | schemas = null;
|
10 |
|
11 | getSchemas = function(callback) {
|
12 | var dir, filename, i, jsonFile, len, name, ref, schema;
|
13 | if (!schemas) {
|
14 | schemas = {};
|
15 | dir = './schema/json/';
|
16 | ref = fs.readdirSync(dir);
|
17 | for (i = 0, len = ref.length; i < len; i++) {
|
18 | jsonFile = ref[i];
|
19 | if (jsonFile !== '.' && jsonFile !== '..') {
|
20 | name = jsonFile.split('.')[0];
|
21 | filename = path.join(dir, jsonFile);
|
22 | schema = JSON.parse(fs.readFileSync(filename));
|
23 | schemas[name] = schema;
|
24 | }
|
25 | }
|
26 | }
|
27 | return refParser.dereference(schemas, callback);
|
28 | };
|
29 |
|
30 | mergeAllOf = function(obj) {
|
31 | var i, key, len, mergeObj, newObj, prop, propName, ref, value;
|
32 | if (!obj) {
|
33 | return obj;
|
34 | }
|
35 | if (!(typeof obj === "object" && (obj.length == null))) {
|
36 | return obj;
|
37 | }
|
38 | newObj = {};
|
39 | for (key in obj) {
|
40 | value = obj[key];
|
41 | if (key === 'allOf') {
|
42 | ref = obj[key];
|
43 | for (i = 0, len = ref.length; i < len; i++) {
|
44 | mergeObj = ref[i];
|
45 | for (propName in mergeObj) {
|
46 | prop = mergeObj[propName];
|
47 | if (newObj[propName] == null) {
|
48 | newObj[propName] = prop;
|
49 | }
|
50 | }
|
51 | }
|
52 | }
|
53 | newObj[key] = mergeAllOf(value);
|
54 | }
|
55 | return newObj;
|
56 | };
|
57 |
|
58 |
|
59 | getDescriptions = function(schemas) {
|
60 | var categories, category, desc, event, i, key, len, message, messages, protocol, protocols, ref, ref1, schema, value;
|
61 | desc = {};
|
62 | protocols = {
|
63 | runtime: ['input', 'output'],
|
64 | graph: ['input'],
|
65 | component: ['input', 'output'],
|
66 | network: ['input', 'output'],
|
67 | trace: ['input', 'output']
|
68 | };
|
69 | for (protocol in protocols) {
|
70 | categories = protocols[protocol];
|
71 | messages = {};
|
72 | desc[protocol] = {
|
73 | title: schemas[protocol].title,
|
74 | description: schemas[protocol].description,
|
75 | messages: messages
|
76 | };
|
77 | for (i = 0, len = categories.length; i < len; i++) {
|
78 | category = categories[i];
|
79 | ref = schemas[protocol][category];
|
80 | for (event in ref) {
|
81 | schema = ref[event];
|
82 | message = {
|
83 | id: schema.id,
|
84 | description: schema.description
|
85 | };
|
86 | if (schema.allOf != null) {
|
87 | ref1 = schema.allOf[1].properties.payload;
|
88 |
|
89 | for (key in ref1) {
|
90 | value = ref1[key];
|
91 | message[key] = value;
|
92 | }
|
93 | }
|
94 |
|
95 |
|
96 | messages[event] = mergeAllOf(message);
|
97 | }
|
98 | }
|
99 | }
|
100 | return desc;
|
101 | };
|
102 |
|
103 | isAllowedTypeless = function(name, parent) {
|
104 | if (parent.id === 'port_definition' && name === 'default') {
|
105 | return true;
|
106 | }
|
107 | if (parent.id === 'input/packet' && name === 'payload') {
|
108 | return true;
|
109 | }
|
110 | if (parent.id === 'output/packet' && name === 'payload') {
|
111 | return true;
|
112 | }
|
113 | if (parent.id === 'output/packetsent' && name === 'payload') {
|
114 | return true;
|
115 | }
|
116 | return false;
|
117 | };
|
118 |
|
119 | renderProperty = function(name, def, parent) {
|
120 | var classes, description, example, isOptional, ref, ref1, ref2, ref3, ref4, ref5, ref6, type;
|
121 | if (!def.description) {
|
122 | throw new Error(`Property ${name} is missing .description`);
|
123 | }
|
124 | if (!isAllowedTypeless(name, parent)) {
|
125 | if (!def.type) {
|
126 | throw new Error(`Property ${name} is missing .type`);
|
127 | }
|
128 | }
|
129 | if (!parent) {
|
130 | throw new Error(`Parent schema not specified for ${name}`);
|
131 | }
|
132 | if (parent.type === 'array') {
|
133 | if (!((ref = parent.items) != null ? (ref1 = ref.required) != null ? ref1.length : void 0 : void 0)) {
|
134 | throw new Error(`.required array not specified for ${name} of ${parent.id} (array)`);
|
135 | }
|
136 | } else {
|
137 | if (((ref2 = parent.required) != null ? ref2.length : void 0) == null) {
|
138 | console.log(JSON.stringify(parent, null, 2));
|
139 | throw new Error(`.required array not specified for ${name} of ${parent.id}`);
|
140 | }
|
141 | }
|
142 | isOptional = (parent.type === 'array' && ((ref3 = parent.required) != null ? ref3.indexOf(name) : void 0) === -1) || ((ref4 = parent.items) != null ? (ref5 = ref4.required) != null ? ref5.indexOf(name) : void 0 : void 0) === -1;
|
143 | classes = "property";
|
144 | if (isOptional) {
|
145 | classes += " optional";
|
146 | }
|
147 | name = `<label class='${classes} name'>${name}</label>`;
|
148 | type = `<label class='${classes} type'>${def.type || 'any'}</label>`;
|
149 | if ((ref6 = def.enum) != null ? ref6.length : void 0) {
|
150 | def.description += ` (one of: ${def.enum.join(', ')})`;
|
151 | }
|
152 | description = `<label class='${classes} description'>${def.description}</label>`;
|
153 | example = "";
|
154 | if (def.example != null) {
|
155 | example = `<code class='${classes} example'>${JSON.stringify(def.example)}</code>`;
|
156 | }
|
157 | return name + type + description + example;
|
158 | };
|
159 |
|
160 | renderMessage = function(messageType, message, protocolName) {
|
161 | var anchorUrl, itemProp, itemPropName, itemSubProp, itemSubPropName, items, line, lines, messageId, messageProp, messagePropName, p, ref, ref1, ref2, ref3, subProp, subPropName;
|
162 | lines = [];
|
163 | p = function(line) {
|
164 | return lines.push(line);
|
165 | };
|
166 | messageId = `${protocolName}-${messageType}`;
|
167 | anchorUrl = '#' + messageId;
|
168 | p(`<h3 id='${messageId}' class='message name'><a href='${anchorUrl}'>${messageType}</a></h3>`);
|
169 | p(`<p>${message.description}</p>`);
|
170 | p("<ul class='message properties'>");
|
171 | ref = message.properties;
|
172 | for (messagePropName in ref) {
|
173 | messageProp = ref[messagePropName];
|
174 | line = `<li>${renderProperty(messagePropName, messageProp, message)}</li>`;
|
175 | items = messageProp.items;
|
176 | if (messageProp.type === 'object' && (messageProp.properties != null)) {
|
177 | p(line);
|
178 | p("<ul class='properties'>");
|
179 | ref1 = messageProp.properties;
|
180 | for (subPropName in ref1) {
|
181 | subProp = ref1[subPropName];
|
182 | p(`<li>${renderProperty(subPropName, subProp, messageProp)}</li>`);
|
183 | }
|
184 | p("</ul>");
|
185 | } else if ((items != null ? items.type : void 0) === 'object') {
|
186 | line += "Each item contains:";
|
187 | p(line);
|
188 | p("<ul class='properties'>");
|
189 | ref2 = items.properties;
|
190 | for (itemPropName in ref2) {
|
191 | itemProp = ref2[itemPropName];
|
192 | if (itemProp.type === 'object') {
|
193 | p(`<li>${renderProperty(itemPropName, itemProp, messageProp)}</li>`);
|
194 | p("<ul class='properties'>");
|
195 | ref3 = itemProp.properties;
|
196 | for (itemSubPropName in ref3) {
|
197 | itemSubProp = ref3[itemSubPropName];
|
198 | p(`<li>${renderProperty(itemSubPropName, itemSubProp, itemProp)}</li>`);
|
199 | }
|
200 | p("</ul>");
|
201 | } else {
|
202 | p(`<li>${renderProperty(itemPropName, itemProp, messageProp)}</li>`);
|
203 | }
|
204 | }
|
205 | p("</ul>");
|
206 | } else {
|
207 | p(line);
|
208 | }
|
209 | }
|
210 | p("</ul>");
|
211 | return lines;
|
212 | };
|
213 |
|
214 | renderCapabilities = function() {
|
215 | var enumDescription, i, j, k, len, len1, len2, lines, messageUrl, name, p, ref, ref1, ref2, schema, tv4;
|
216 | tv4 = require('../schema/index.js');
|
217 | schema = tv4.getSchema('/shared/capabilities');
|
218 | lines = [];
|
219 | p = function(line) {
|
220 | return lines.push(line);
|
221 | };
|
222 | p("<section class='capabilities'>");
|
223 | ref = schema.items._enumDescriptions;
|
224 | for (i = 0, len = ref.length; i < len; i++) {
|
225 | enumDescription = ref[i];
|
226 | p(`<h4 class='capability name'>${enumDescription.name}</h4>`);
|
227 | p(`<p>${enumDescription.description}</p>`);
|
228 | p("<h5 class='capability messages header'>input messages</h5>");
|
229 | p("<ul class='capability messages'>");
|
230 | ref1 = enumDescription.inputs;
|
231 | for (j = 0, len1 = ref1.length; j < len1; j++) {
|
232 | name = ref1[j];
|
233 | messageUrl = "#" + name.replace(':', '-');
|
234 | p(`<li><a href='${messageUrl}'>${name}</a></li>`);
|
235 | }
|
236 | p("</ul>");
|
237 | p("<h5 class='capability messages header'>output messages</h5>");
|
238 | p("<ul class='capability messages'>");
|
239 | ref2 = enumDescription.outputs;
|
240 | for (k = 0, len2 = ref2.length; k < len2; k++) {
|
241 | name = ref2[k];
|
242 | messageUrl = "#" + name.replace(':', '-');
|
243 | p(`<li><a href='${messageUrl}'>${name}</a></li>`);
|
244 | }
|
245 | p("</ul>");
|
246 | }
|
247 | p("</section>");
|
248 | return lines.join('\n');
|
249 | };
|
250 |
|
251 | renderMessages = function(callback) {
|
252 | return getSchemas(function(err, schemas) {
|
253 | var descriptions, lines, m, message, messageType, p, protocol, protocolProps, ref;
|
254 | if (err) {
|
255 | return callback(err);
|
256 | }
|
257 | descriptions = getDescriptions(schemas);
|
258 | lines = [];
|
259 | p = function(line) {
|
260 | return lines.push(line);
|
261 | };
|
262 | for (protocol in descriptions) {
|
263 | protocolProps = descriptions[protocol];
|
264 | p(`<h2 class='protocol name' id='${protocol}-protocol'>${protocolProps.title}</h2>`);
|
265 | p(`<p class='protocol description'>${protocolProps.description}</p>`);
|
266 | ref = protocolProps.messages;
|
267 | for (messageType in ref) {
|
268 | message = ref[messageType];
|
269 | m = renderMessage(messageType, message, protocol);
|
270 | lines = lines.concat(m);
|
271 | }
|
272 | }
|
273 | return callback(null, lines.join('\n'));
|
274 | });
|
275 | };
|
276 |
|
277 | module.exports = {
|
278 | renderMessages: renderMessages,
|
279 | renderCapabilities: renderCapabilities,
|
280 | getSchemas: getSchemas
|
281 | };
|