1 | fs = require 'fs'
|
2 | path = require 'path'
|
3 | refParser = require 'json-schema-ref-parser'
|
4 |
|
5 | schemas = null
|
6 | getSchemas = (callback) ->
|
7 | unless schemas
|
8 | schemas = {}
|
9 | dir = './schema/json/'
|
10 | for jsonFile in fs.readdirSync dir
|
11 | if jsonFile not in ['.', '..']
|
12 | name = jsonFile.split('.')[0]
|
13 | filename = path.join dir, jsonFile
|
14 | schema = JSON.parse fs.readFileSync filename
|
15 | schemas[name] = schema
|
16 | refParser.dereference schemas, callback
|
17 |
|
18 | mergeAllOf = (obj) ->
|
19 | return obj unless obj
|
20 | unless typeof obj is "object" and not obj.length?
|
21 | return obj
|
22 |
|
23 | newObj = {}
|
24 | for key, value of obj
|
25 | if key is 'allOf'
|
26 | for mergeObj in obj[key]
|
27 | for propName, prop of mergeObj
|
28 | newObj[propName] ?= prop
|
29 |
|
30 | newObj[key] = mergeAllOf value
|
31 |
|
32 | return newObj
|
33 |
|
34 |
|
35 | getDescriptions = (schemas) ->
|
36 |
|
37 | desc = {}
|
38 | protocols =
|
39 | runtime: ['input', 'output']
|
40 | graph: ['input']
|
41 | component: ['input', 'output']
|
42 | network: ['input', 'output']
|
43 | trace: ['input', 'output']
|
44 |
|
45 | for protocol, categories of protocols
|
46 | messages = {}
|
47 | desc[protocol] =
|
48 | title: schemas[protocol].title
|
49 | description: schemas[protocol].description
|
50 | messages: messages
|
51 |
|
52 | for category in categories
|
53 | for event, schema of schemas[protocol][category]
|
54 | message =
|
55 | id: schema.id
|
56 | description: schema.description
|
57 |
|
58 | if schema.allOf?
|
59 |
|
60 | for key, value of schema.allOf[1].properties.payload
|
61 | message[key] = value
|
62 |
|
63 |
|
64 |
|
65 | messages[event] = mergeAllOf message
|
66 |
|
67 | return desc
|
68 |
|
69 | isAllowedTypeless = (name, parent) ->
|
70 | return true if parent.id is 'port_definition' and name is 'default'
|
71 | return true if parent.id is 'input/packet' and name is 'payload'
|
72 | return true if parent.id is 'output/packet' and name is 'payload'
|
73 | return true if parent.id is 'output/packetsent' and name is 'payload'
|
74 | false
|
75 |
|
76 | renderProperty = (name, def, parent) ->
|
77 | throw new Error("Property #{name} is missing .description") if not def.description
|
78 | unless isAllowedTypeless name, parent
|
79 | throw new Error("Property #{name} is missing .type") if not def.type
|
80 | throw new Error("Parent schema not specified for #{name}") if not parent
|
81 | if parent.type == 'array'
|
82 | if not parent.items?.required?.length
|
83 | throw new Error(".required array not specified for #{name} of #{parent.id} (array)")
|
84 | else
|
85 | if not parent.required?.length?
|
86 | console.log(JSON.stringify(parent, null, 2))
|
87 | throw new Error(".required array not specified for #{name} of #{parent.id}")
|
88 |
|
89 | isOptional = (parent.type == 'array' and parent.required?.indexOf(name) == -1) or parent.items?.required?.indexOf(name) == -1
|
90 | classes = "property"
|
91 | classes += " optional" if isOptional
|
92 | name = "<label class='#{classes} name'>#{name}</label>"
|
93 | type = "<label class='#{classes} type'>#{def.type or 'any'}</label>"
|
94 |
|
95 | if def.enum?.length
|
96 | def.description += " (one of: #{def.enum.join(', ')})"
|
97 |
|
98 | description = "<label class='#{classes} description'>#{def.description}</label>"
|
99 | example = ""
|
100 | example = "<code class='#{classes} example'>#{JSON.stringify(def.example)}</code>" if def.example?
|
101 | return name + type + description + example
|
102 |
|
103 | renderMessage = (messageType, message, protocolName) ->
|
104 |
|
105 | lines = []
|
106 | p = (line) -> lines.push line
|
107 |
|
108 | messageId = "#{protocolName}-#{messageType}"
|
109 | anchorUrl = '#'+messageId
|
110 |
|
111 | p "<h3 id='#{messageId}' class='message name'><a href='#{anchorUrl}'>#{messageType}</a></h3>"
|
112 | p "<p>#{message.description}</p>"
|
113 |
|
114 | p "<ul class='message properties'>"
|
115 | for messagePropName, messageProp of message.properties
|
116 | line = "<li>#{renderProperty(messagePropName, messageProp, message)}</li>"
|
117 | items = messageProp.items
|
118 |
|
119 | if messageProp.type is 'object' and messageProp.properties?
|
120 | p line
|
121 | p "<ul class='properties'>"
|
122 | for subPropName, subProp of messageProp.properties
|
123 | p "<li>#{renderProperty(subPropName, subProp, messageProp)}</li>"
|
124 | p "</ul>"
|
125 |
|
126 | else if items?.type is 'object'
|
127 | line += "Each item contains:"
|
128 | p line
|
129 |
|
130 | p "<ul class='properties'>"
|
131 | for itemPropName, itemProp of items.properties
|
132 | if itemProp.type is 'object'
|
133 | p "<li>#{renderProperty(itemPropName, itemProp, messageProp)}</li>"
|
134 |
|
135 | p "<ul class='properties'>"
|
136 | for itemSubPropName, itemSubProp of itemProp.properties
|
137 | p "<li>#{renderProperty(itemSubPropName, itemSubProp, itemProp)}</li>"
|
138 | p "</ul>"
|
139 |
|
140 | else
|
141 | p "<li>#{renderProperty(itemPropName, itemProp, messageProp)}</li>"
|
142 | p "</ul>"
|
143 |
|
144 | else
|
145 | p line
|
146 | p "</ul>"
|
147 |
|
148 | return lines
|
149 |
|
150 | renderCapabilities = () ->
|
151 | tv4 = require '../schema/index.js'
|
152 | schema = tv4.getSchema '/shared/capabilities'
|
153 |
|
154 | lines = []
|
155 | p = (line) -> lines.push line
|
156 |
|
157 | p "<section class='capabilities'>"
|
158 | for enumDescription in schema.items._enumDescriptions
|
159 | p "<h4 class='capability name'>#{enumDescription.name}</h4>"
|
160 | p "<p>#{enumDescription.description}</p>"
|
161 |
|
162 | p "<h5 class='capability messages header'>input messages</h5>"
|
163 | p "<ul class='capability messages'>"
|
164 | for name in enumDescription.inputs
|
165 | messageUrl = "#"+name.replace(':', '-')
|
166 | p "<li><a href='#{messageUrl}'>#{name}</a></li>"
|
167 | p "</ul>"
|
168 |
|
169 | p "<h5 class='capability messages header'>output messages</h5>"
|
170 | p "<ul class='capability messages'>"
|
171 | for name in enumDescription.outputs
|
172 | messageUrl = "#"+name.replace(':', '-')
|
173 | p "<li><a href='#{messageUrl}'>#{name}</a></li>"
|
174 | p "</ul>"
|
175 |
|
176 | p "</section>"
|
177 |
|
178 | return lines.join('\n')
|
179 |
|
180 | renderMessages = (callback) ->
|
181 | getSchemas (err, schemas) ->
|
182 | return callback err if err
|
183 | descriptions = getDescriptions schemas
|
184 |
|
185 | lines = []
|
186 | p = (line) -> lines.push line
|
187 |
|
188 | for protocol, protocolProps of descriptions
|
189 | p "<h2 class='protocol name' id='#{protocol}-protocol'>#{protocolProps.title}</h2>"
|
190 | p "<p class='protocol description'>#{protocolProps.description}</p>"
|
191 |
|
192 | for messageType, message of protocolProps.messages
|
193 | m = renderMessage messageType, message, protocol
|
194 | lines = lines.concat m
|
195 |
|
196 | callback null, lines.join('\n')
|
197 |
|
198 | module.exports =
|
199 | renderMessages: renderMessages
|
200 | renderCapabilities: renderCapabilities
|
201 | getSchemas: getSchemas
|