4f1a148985b6c28d5d4b1e416a687df536ca2afa
[nit.git] / src / web / api_model.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 module api_model
16
17 import web_base
18 import highlight
19 import uml
20 import model::model_index
21
22 redef class APIRouter
23 redef init do
24 super
25 use("/list", new APIList(config))
26 use("/search", new APISearch(config))
27 use("/random", new APIRandom(config))
28 use("/entity/:id", new APIEntity(config))
29 use("/entity/:id/doc", new APIEntityDoc(config))
30 use("/code/:id", new APIEntityCode(config))
31 use("/uml/:id", new APIEntityUML(config))
32 use("/linearization/:id", new APIEntityLinearization(config))
33 use("/defs/:id", new APIEntityDefs(config))
34 use("/inheritance/:id", new APIEntityInheritance(config))
35 end
36 end
37
38 # List all mentities.
39 #
40 # MEntities can be filtered on their kind using the `k` parameter.
41 # Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
42 #
43 # List size can be limited with the `n` parameter.
44 #
45 # Example: `GET /list?k=module?n=10`
46 class APIList
47 super APIHandler
48
49 # List mentities depending on the `k` kind parameter.
50 fun list_mentities(req: HttpRequest): Array[MEntity] do
51 var k = req.string_arg("k")
52 var mentities = new Array[MEntity]
53 if k == "package" then
54 for mentity in config.view.mpackages do mentities.add mentity
55 else if k == "group" then
56 for mentity in config.view.mgroups do mentities.add mentity
57 else if k == "module" then
58 for mentity in config.view.mmodules do mentities.add mentity
59 else if k == "class" then
60 for mentity in config.view.mclasses do mentities.add mentity
61 else if k == "classdef" then
62 for mentity in config.view.mclassdefs do mentities.add mentity
63 else if k == "property" then
64 for mentity in config.view.mproperties do mentities.add mentity
65 else if k == "propdef" then
66 for mentity in config.view.mpropdefs do mentities.add mentity
67 else
68 for mentity in config.view.mentities do mentities.add mentity
69 end
70 return mentities
71 end
72
73 # Sort mentities by lexicographic order
74 #
75 # TODO choose order from request
76 fun sort_mentities(req: HttpRequest, mentities: Array[MEntity]) : Array[MEntity] do
77 var sorted = mentities.to_a
78 var sorter = new MEntityNameSorter
79 sorter.sort(sorted)
80 return sorted
81 end
82
83 # Limit mentities depending on the `n` parameter.
84 fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
85 var n = req.int_arg("n")
86 if n != null then
87 return mentities.sub(0, n)
88 end
89 return mentities
90 end
91
92 redef fun get(req, res) do
93 var mentities = list_mentities(req)
94 mentities = sort_mentities(req, mentities)
95 mentities = limit_mentities(req, mentities)
96 res.json new JsonArray.from(mentities)
97 end
98 end
99
100 # Search mentities from a query string.
101 #
102 # Example: `GET /search?q=Arr`
103 class APISearch
104 super APIList
105
106 redef fun get(req, res) do
107 var q = req.string_arg("q")
108 if q == null then
109 res.json new JsonArray
110 return
111 end
112 var n = req.int_arg("n")
113 res.json new JsonArray.from(config.view.find(q, n))
114 end
115 end
116
117 # Return a random list of MEntities.
118 #
119 # Example: `GET /random?n=10&k=module`
120 class APIRandom
121 super APIList
122
123 # Randomize mentities order.
124 fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
125 var res = mentities.to_a
126 res.shuffle
127 return res
128 end
129
130 redef fun get(req, res) do
131 var mentities = list_mentities(req)
132 mentities = randomize_mentities(req, mentities)
133 mentities = limit_mentities(req, mentities)
134 res.json new JsonArray.from(mentities)
135 end
136 end
137
138 # Return the JSON representation of a MEntity.
139 #
140 # Example: `GET /entity/core::Array`
141 class APIEntity
142 super APIHandler
143
144 redef fun get(req, res) do
145 var mentity = mentity_from_uri(req, res)
146 if mentity == null then return
147 res.raw_json mentity.to_full_json
148 end
149 end
150
151 # Return the full MDoc of a MEntity.
152 #
153 # Example: `GET /entity/core::Array/doc`
154 class APIEntityDoc
155 super APIHandler
156
157 redef fun get(req, res) do
158 var mentity = mentity_from_uri(req, res)
159 if mentity == null then return
160
161 var obj = new JsonObject
162 var mdoc = mentity.mdoc_or_fallback
163 if mdoc != null then
164 obj["documentation"] = mdoc.html_documentation.write_to_string
165 obj["location"] = mdoc.location
166 end
167 res.json obj
168 end
169 end
170
171 # List ancestors, parents, child and descendants of MEntity
172 #
173 # Example: `GET /entity/core::Array/inheritance`
174 class APIEntityInheritance
175 super APIHandler
176
177 redef fun get(req, res) do
178 var mentity = mentity_from_uri(req, res)
179 if mentity == null then return
180 res.json mentity.hierarchy_poset(config.view)[mentity]
181 end
182 end
183
184 # Linearize super definitions of a MClassDef or a MPropDef if any.
185 #
186 # Example: `GET /entity/core::Array/linearization`
187 class APIEntityLinearization
188 super APIHandler
189
190 redef fun get(req, res) do
191 var mentity = mentity_from_uri(req, res)
192 if mentity == null then return
193 var lin = mentity.collect_linearization(config.mainmodule)
194 if lin == null then
195 res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
196 return
197 end
198 var mentities = new JsonArray
199 for e in lin do mentities.add e
200 res.json mentities
201 end
202 end
203
204 # List definitions of a MEntity.
205 #
206 # Example: `GET /defs/core::Array`
207 class APIEntityDefs
208 super APIList
209
210 redef fun get(req, res) do
211 var mentity = mentity_from_uri(req, res)
212 if mentity == null then return
213 var mentities: Array[MEntity]
214 if mentity isa MPackage then
215 mentities = mentity.mgroups.to_a
216 else if mentity isa MGroup then
217 mentities = new Array[MEntity]
218 mentities.add_all mentity.in_nesting.direct_smallers
219 mentities.add_all mentity.mmodules
220 else if mentity isa MModule then
221 mentities = mentity.mclassdefs
222 else if mentity isa MClass then
223 mentities = mentity.mclassdefs
224 else if mentity isa MClassDef then
225 mentities = mentity.mpropdefs
226 else if mentity isa MProperty then
227 mentities = mentity.mpropdefs
228 else
229 res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
230 return
231 end
232 mentities = sort_mentities(req, mentities)
233 mentities = limit_mentities(req, mentities)
234 res.json new JsonArray.from(mentities)
235 end
236 end
237
238 abstract class SVGHandler
239 super APIHandler
240
241 # Render a `dot` string as a svg image.
242 fun render_dot(dot: Text): String do
243 var proc = new ProcessDuplex("dot", "-Tsvg")
244 var svg = proc.write_and_read(dot)
245 proc.close
246 proc.wait
247 return svg
248 end
249 end
250
251 # Return a UML representation of MEntity.
252 #
253 # Example: `GET /entity/core::Array/uml`
254 class APIEntityUML
255 super SVGHandler
256
257 redef fun get(req, res) do
258 var mentity = mentity_from_uri(req, res)
259 if mentity == null then return
260 var dot
261 if mentity isa MClassDef then mentity = mentity.mclass
262 if mentity isa MClass then
263 var uml = new UMLModel(config.view, config.mainmodule)
264 dot = uml.generate_class_uml.write_to_string
265 else if mentity isa MModule then
266 var uml = new UMLModel(config.view, mentity)
267 dot = uml.generate_package_uml.write_to_string
268 else
269 res.api_error(404, "No UML for mentity `{mentity.full_name}`")
270 return
271 end
272 res.send render_dot(dot)
273 end
274 end
275
276 # Return the source code of MEntity.
277 #
278 # Example: `GET /entity/core::Array/code`
279 class APIEntityCode
280 super APIHandler
281
282 redef fun get(req, res) do
283 var mentity = mentity_from_uri(req, res)
284 if mentity == null then return
285 var source = render_source(mentity)
286 if source == null then
287 res.api_error(404, "No code for mentity `{mentity.full_name}`")
288 return
289 end
290 res.send source
291 end
292
293 # Highlight `mentity` source code.
294 private fun render_source(mentity: MEntity): nullable HTMLTag do
295 var node = config.modelbuilder.mentity2node(mentity)
296 if node == null then return null
297 var hl = new HighlightVisitor
298 hl.enter_visit node
299 return hl.html
300 end
301 end