Merge: nitweb: better namespaces
[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
21 redef class APIRouter
22 redef init do
23 super
24 use("/list", new APIList(config))
25 use("/search", new APISearch(config))
26 use("/random", new APIRandom(config))
27 use("/entity/:id", new APIEntity(config))
28 use("/code/:id", new APIEntityCode(config))
29 use("/uml/:id", new APIEntityUML(config))
30 use("/linearization/:id", new APIEntityLinearization(config))
31 use("/defs/:id", new APIEntityDefs(config))
32 use("/inheritance/:id", new APIEntityInheritance(config))
33 end
34 end
35
36 # List all mentities.
37 #
38 # MEntities can be filtered on their kind using the `k` parameter.
39 # Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
40 #
41 # List size can be limited with the `n` parameter.
42 #
43 # Example: `GET /list?k=module?n=10`
44 class APIList
45 super APIHandler
46
47 # List mentities depending on the `k` kind parameter.
48 fun list_mentities(req: HttpRequest): Array[MEntity] do
49 var k = req.string_arg("k")
50 var mentities = new Array[MEntity]
51 if k == "package" then
52 for mentity in view.mpackages do mentities.add mentity
53 else if k == "group" then
54 for mentity in view.mgroups do mentities.add mentity
55 else if k == "module" then
56 for mentity in view.mmodules do mentities.add mentity
57 else if k == "class" then
58 for mentity in view.mclasses do mentities.add mentity
59 else if k == "classdef" then
60 for mentity in view.mclassdefs do mentities.add mentity
61 else if k == "property" then
62 for mentity in view.mproperties do mentities.add mentity
63 else if k == "propdef" then
64 for mentity in view.mpropdefs do mentities.add mentity
65 else
66 for mentity in view.mentities do mentities.add mentity
67 end
68 return mentities
69 end
70
71 # Limit mentities depending on the `n` parameter.
72 fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
73 var n = req.int_arg("n")
74 if n != null then
75 return mentities.sub(0, n)
76 end
77 return mentities
78 end
79
80 redef fun get(req, res) do
81 var mentities = list_mentities(req)
82 mentities = limit_mentities(req, mentities)
83 res.json new JsonArray.from(mentities)
84 end
85 end
86
87 # Search mentities from a query string.
88 #
89 # Example: `GET /search?q=Arr`
90 class APISearch
91 super APIList
92
93 redef fun list_mentities(req) do
94 var q = req.string_arg("q")
95 var mentities = new Array[MEntity]
96 if q == null then return mentities
97 for mentity in view.mentities do
98 if mentity.name.has_prefix(q) then mentities.add mentity
99 end
100 return mentities
101 end
102 end
103
104 # Return a random list of MEntities.
105 #
106 # Example: `GET /random?n=10&k=module`
107 class APIRandom
108 super APIList
109
110 # Randomize mentities order.
111 fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
112 var res = mentities.to_a
113 res.shuffle
114 return res
115 end
116
117 redef fun get(req, res) do
118 var mentities = list_mentities(req)
119 mentities = limit_mentities(req, mentities)
120 mentities = randomize_mentities(req, mentities)
121 res.json new JsonArray.from(mentities)
122 end
123 end
124
125 # Return the JSON representation of a MEntity.
126 #
127 # Example: `GET /entity/core::Array`
128 class APIEntity
129 super APIHandler
130
131 redef fun get(req, res) do
132 var mentity = mentity_from_uri(req, res)
133 if mentity == null then return
134 res.json mentity.api_json(self)
135 end
136 end
137
138 # List ancestors, parents, child and descendants of MEntity
139 #
140 # Example: `GET /entity/core::Array/inheritance`
141 class APIEntityInheritance
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.json mentity.hierarchy_poset(view)[mentity]
148 end
149 end
150
151 # Linearize super definitions of a MClassDef or a MPropDef if any.
152 #
153 # Example: `GET /entity/core::Array/linearization`
154 class APIEntityLinearization
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 var lin = mentity.collect_linearization(config.mainmodule)
161 if lin == null then
162 res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
163 return
164 end
165 var mentities = new JsonArray
166 for e in lin do mentities.add e.full_json
167 res.json mentities
168 end
169 end
170
171 # List definitions of a MEntity.
172 #
173 # Example: `GET /defs/core::Array`
174 class APIEntityDefs
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 var arr = new JsonArray
181 if mentity isa MModule then
182 for mclassdef in mentity.mclassdefs do arr.add mclassdef
183 else if mentity isa MClass then
184 for mclassdef in mentity.mclassdefs do arr.add mclassdef
185 else if mentity isa MClassDef then
186 for mpropdef in mentity.mpropdefs do arr.add mpropdef
187 else if mentity isa MProperty then
188 for mpropdef in mentity.mpropdefs do arr.add mpropdef
189 else
190 res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
191 return
192 end
193 res.json arr
194 end
195 end
196
197 abstract class SVGHandler
198 super APIHandler
199
200 # Render a `dot` string as a svg image.
201 fun render_dot(dot: Text): String do
202 var proc = new ProcessDuplex("dot", "-Tsvg")
203 var svg = proc.write_and_read(dot)
204 proc.close
205 proc.wait
206 return svg
207 end
208 end
209
210 # Return a UML representation of MEntity.
211 #
212 # Example: `GET /entity/core::Array/uml`
213 class APIEntityUML
214 super SVGHandler
215
216 redef fun get(req, res) do
217 var mentity = mentity_from_uri(req, res)
218 if mentity == null then return
219 var dot
220 if mentity isa MClassDef then mentity = mentity.mclass
221 if mentity isa MClass then
222 var uml = new UMLModel(view, config.mainmodule)
223 dot = uml.generate_class_uml.write_to_string
224 else if mentity isa MModule then
225 var uml = new UMLModel(view, mentity)
226 dot = uml.generate_package_uml.write_to_string
227 else
228 res.api_error(404, "No UML for mentity `{mentity.full_name}`")
229 return
230 end
231 res.send render_dot(dot)
232 end
233 end
234
235 # Return the source code of MEntity.
236 #
237 # Example: `GET /entity/core::Array/code`
238 class APIEntityCode
239 super APIHandler
240
241 redef fun get(req, res) do
242 var mentity = mentity_from_uri(req, res)
243 if mentity == null then return
244 var source = render_source(mentity)
245 if source == null then
246 res.api_error(404, "No code for mentity `{mentity.full_name}`")
247 return
248 end
249 res.send source
250 end
251
252 # Highlight `mentity` source code.
253 private fun render_source(mentity: MEntity): nullable HTMLTag do
254 var node = config.modelbuilder.mentity2node(mentity)
255 if node == null then return null
256 var hl = new HighlightVisitor
257 hl.enter_visit node
258 return hl.html
259 end
260 end