nitweb: move APIHandler to web_base
[nit.git] / src / web / model_api.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 model_api
16
17 import web_base
18 import highlight
19 import uml
20
21 # Group all api handlers in one router.
22 class APIRouter
23 super Router
24
25 # Model to pass to handlers.
26 var model: Model
27
28 # ModelBuilder to pass to handlers.
29 var modelbuilder: ModelBuilder
30
31 # Mainmodule to pass to handlers.
32 var mainmodule: MModule
33
34 init do
35 use("/list", new APIList(model, mainmodule))
36 use("/search", new APISearch(model, mainmodule))
37 use("/random", new APIRandom(model, mainmodule))
38 use("/entity/:id", new APIEntity(model, mainmodule))
39 use("/code/:id", new APIEntityCode(model, mainmodule, modelbuilder))
40 use("/uml/:id", new APIEntityUML(model, mainmodule))
41 end
42 end
43
44 # List all mentities.
45 #
46 # MEntities can be filtered on their kind using the `k` parameter.
47 # Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
48 #
49 # List size can be limited with the `n` parameter.
50 #
51 # Example: `GET /list?k=module?n=10`
52 class APIList
53 super APIHandler
54
55 # List mentities depending on the `k` kind parameter.
56 fun list_mentities(req: HttpRequest): Array[MEntity] do
57 var k = req.string_arg("k")
58 var mentities = new Array[MEntity]
59 if k == "package" then
60 for mentity in view.mpackages do mentities.add mentity
61 else if k == "group" then
62 for mentity in view.mgroups do mentities.add mentity
63 else if k == "module" then
64 for mentity in view.mmodules do mentities.add mentity
65 else if k == "class" then
66 for mentity in view.mclasses do mentities.add mentity
67 else if k == "classdef" then
68 for mentity in view.mclassdefs do mentities.add mentity
69 else if k == "property" then
70 for mentity in view.mproperties do mentities.add mentity
71 else if k == "propdef" then
72 for mentity in view.mpropdefs do mentities.add mentity
73 else
74 for mentity in view.mentities do mentities.add mentity
75 end
76 return mentities
77 end
78
79 # Limit mentities depending on the `n` parameter.
80 fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
81 var n = req.int_arg("n")
82 if n != null then
83 return mentities.sub(0, n)
84 end
85 return mentities
86 end
87
88 redef fun get(req, res) do
89 var mentities = list_mentities(req)
90 mentities = limit_mentities(req, mentities)
91 res.json new JsonArray.from(mentities)
92 end
93 end
94
95 # Search mentities from a query string.
96 #
97 # Example: `GET /search?q=Arr`
98 class APISearch
99 super APIList
100
101 redef fun list_mentities(req) do
102 var q = req.string_arg("q")
103 var mentities = new Array[MEntity]
104 if q == null then return mentities
105 for mentity in view.mentities do
106 if mentity.name.has_prefix(q) then mentities.add mentity
107 end
108 return mentities
109 end
110 end
111
112 # Return a random list of MEntities.
113 #
114 # Example: `GET /random?n=10&k=module`
115 class APIRandom
116 super APIList
117
118 # Randomize mentities order.
119 fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
120 var res = mentities.to_a
121 res.shuffle
122 return res
123 end
124
125 redef fun get(req, res) do
126 var mentities = list_mentities(req)
127 mentities = limit_mentities(req, mentities)
128 mentities = randomize_mentities(req, mentities)
129 res.json new JsonArray.from(mentities)
130 end
131 end
132
133 # Return the JSON representation of a MEntity.
134 #
135 # Example: `GET /entity/core::Array`
136 class APIEntity
137 super APIHandler
138
139 redef fun get(req, res) do
140 var mentity = mentity_from_uri(req, res)
141 if mentity == null then return
142 res.json mentity.api_json(self)
143 end
144 end
145
146
147 # Return a UML representation of MEntity.
148 #
149 # Example: `GET /entity/core::Array/uml`
150 class APIEntityUML
151 super APIHandler
152
153 redef fun get(req, res) do
154 var mentity = mentity_from_uri(req, res)
155 var dot
156 if mentity isa MClassDef then mentity = mentity.mclass
157 if mentity isa MClass then
158 var uml = new UMLModel(view, mainmodule)
159 dot = uml.generate_class_uml.write_to_string
160 else if mentity isa MModule then
161 var uml = new UMLModel(view, mentity)
162 dot = uml.generate_package_uml.write_to_string
163 else
164 res.error 404
165 return
166 end
167 res.send render_svg(dot)
168 end
169
170 # Render a `dot` string as a svg image.
171 fun render_svg(dot: String): String do
172 var proc = new ProcessDuplex("dot", "-Tsvg")
173 var svg = proc.write_and_read(dot)
174 proc.close
175 proc.wait
176 return svg
177 end
178 end
179
180 # Return the source code of MEntity.
181 #
182 # Example: `GET /entity/core::Array/code`
183 class APIEntityCode
184 super APIHandler
185
186 # Modelbuilder used to access sources.
187 var modelbuilder: ModelBuilder
188
189 redef fun get(req, res) do
190 var mentity = mentity_from_uri(req, res)
191 if mentity == null then return
192 var source = render_source(mentity)
193 if source == null then
194 res.error 404
195 return
196 end
197 res.send source
198 end
199
200 # Highlight `mentity` source code.
201 private fun render_source(mentity: MEntity): nullable HTMLTag do
202 var node = modelbuilder.mentity2node(mentity)
203 if node == null then return null
204 var hl = new HighlightVisitor
205 hl.enter_visit node
206 return hl.html
207 end
208 end