search_tests.sh: Unquote results
[nit.git] / src / web / web_base.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 # Base classes used by `nitweb`.
16 module web_base
17
18 import model::model_views
19 import model::model_json
20 import doc_down
21 import popcorn
22 import popcorn::pop_config
23 import popcorn::pop_repos
24
25 # Nitweb config file.
26 class NitwebConfig
27 super AppConfig
28
29 redef fun default_db_name do return "nitweb"
30
31 # Model to use.
32 var model: Model
33
34 # MModule used to flatten model.
35 var mainmodule: MModule
36
37 # Modelbuilder used to access sources.
38 var modelbuilder: ModelBuilder
39
40 # The JSON API does not filter anything by default.
41 #
42 # So we can cache the model view.
43 var view: ModelView is lazy do
44 var view = new ModelView(model)
45 view.min_visibility = private_visibility
46 view.include_fictive = true
47 view.include_empty_doc = true
48 view.include_attribute = true
49 view.include_test_suite = true
50 return view
51 end
52 end
53
54 # Specific handler for the nitweb API.
55 abstract class APIHandler
56 super Handler
57
58 # App config.
59 var config: NitwebConfig
60
61 # Find the MEntity ` with `full_name`.
62 fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
63 if full_name == null then return null
64 return model.mentity_by_full_name(full_name.from_percent_encoding)
65 end
66
67 # Try to load the mentity from uri with `/:id`.
68 #
69 # Send 400 if `:id` is null.
70 # Send 404 if no entity is found.
71 # Return null in both cases.
72 fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
73 var id = req.param("id")
74 if id == null then
75 res.api_error(400, "Expected mentity full name")
76 return null
77 end
78 var mentity = find_mentity(config.view, id)
79 if mentity == null then
80 res.api_error(404, "MEntity `{id}` not found")
81 end
82 return mentity
83 end
84 end
85
86 # A Rooter dedicated to APIHandlers.
87 class APIRouter
88 super Router
89
90 # App config
91 var config: NitwebConfig
92 end
93
94 redef class HttpResponse
95
96 # Return an HTTP error response with `status`
97 #
98 # Like the rest of the API, errors are formated as JSON:
99 # ~~~json
100 # { "status": 404, "message": "Not found" }
101 # ~~~
102 fun api_error(status: Int, message: String) do
103 json(new APIError(status, message), status)
104 end
105
106 # Write data as JSON and set the right content type header.
107 fun raw_json(json: nullable String, status: nullable Int) do
108 header["Content-Type"] = media_types["json"].as(not null)
109 if json == null then
110 send(null, status)
111 else
112 send(json, status)
113 end
114 end
115 end
116
117 # An error returned by the API.
118 #
119 # Can be serialized to json.
120 class APIError
121 serialize
122
123 # Reponse status
124 var status: Int
125
126 # Response error message
127 var message: String
128 end
129
130 # Fullname representation that can be used to build decorated links
131 #
132 # * MPackage: `mpackage_name`
133 # * MGroup: `(mpackage_name::)mgroup_name`
134 class Namespace
135 super Array[nullable NSEntity]
136 super NSEntity
137 serialize
138
139 redef fun to_s do return self.join("")
140 redef fun serialize_to(v) do to_a.serialize_to(v)
141 end
142
143 # Something that goes in a Namespace
144 #
145 # Can be either:
146 # * a `NSToken` for tokens like `::`, `>` and `$`
147 # * a `MSRef` for references to mentities
148 interface NSEntity
149 super Serializable
150 end
151
152 # A reference to a MEntity that can be rendered as a link.
153 #
154 # We do not reuse `MEntityRef` ref since NSRef can be found in a ref and create
155 # an infinite loop.
156 class NSRef
157 super NSEntity
158 serialize
159
160 # The mentity to link to/
161 var mentity: MEntity
162
163 redef fun core_serialize_to(v) do
164 v.serialize_attribute("web_url", mentity.web_url)
165 v.serialize_attribute("api_url", mentity.api_url)
166 v.serialize_attribute("name", mentity.name)
167 end
168 end
169
170 # A namespace token representation
171 #
172 # Used for namespace tokens like `::`, `>` and `$`
173 redef class String
174 super NSEntity
175 end
176
177 redef class MEntity
178
179 # URL to `self` within the web interface.
180 fun web_url: String do return "/doc/" / full_name
181
182 # URL to `self` within the JSON api.
183 fun api_url: String do return "/api/entity/" / full_name
184
185 redef fun core_serialize_to(v) do
186 super
187 v.serialize_attribute("namespace", namespace)
188 v.serialize_attribute("web_url", web_url)
189 v.serialize_attribute("api_url", api_url)
190 end
191
192 # Return `self.full_name` as an object that can be serialized to json.
193 fun namespace: nullable Namespace do return null
194
195 # Return a new NSRef to `self`.
196 fun to_ns_ref: NSRef do return new NSRef(self)
197 end
198
199 redef class MEntityRef
200 redef fun core_serialize_to(v) do
201 super
202 v.serialize_attribute("namespace", mentity.namespace)
203 v.serialize_attribute("web_url", mentity.web_url)
204 v.serialize_attribute("api_url", mentity.api_url)
205 v.serialize_attribute("name", mentity.name)
206 v.serialize_attribute("mdoc", mentity.mdoc_or_fallback)
207 v.serialize_attribute("visibility", mentity.visibility.to_s)
208 v.serialize_attribute("modifiers", mentity.collect_modifiers)
209 var mentity = self.mentity
210 if mentity isa MMethod then
211 v.serialize_attribute("msignature", mentity.intro.msignature)
212 else if mentity isa MMethodDef then
213 v.serialize_attribute("msignature", mentity.msignature)
214 else if mentity isa MVirtualTypeProp then
215 v.serialize_attribute("bound", to_mentity_ref(mentity.intro.bound))
216 else if mentity isa MVirtualTypeDef then
217 v.serialize_attribute("bound", to_mentity_ref(mentity.bound))
218 end
219 v.serialize_attribute("location", mentity.location)
220 end
221 end
222
223 redef class MDoc
224
225 # Add doc down processing
226 redef fun core_serialize_to(v) do
227 v.serialize_attribute("html_synopsis", html_synopsis.write_to_string)
228 v.serialize_attribute("html_documentation", html_documentation.write_to_string)
229 end
230 end
231
232 redef class MPackage
233 redef fun namespace do return new Namespace.from([to_ns_ref])
234 end
235
236 redef class MGroup
237 redef fun namespace do
238 var p = parent
239 if p == null then
240 return new Namespace.from([to_ns_ref, ">": nullable NSEntity])
241 end
242 return new Namespace.from([p.namespace, to_ns_ref, ">": nullable NSEntity])
243 end
244 end
245
246 redef class MModule
247 redef fun namespace do
248 var mgroup = self.mgroup
249 if mgroup == null then
250 return new Namespace.from([to_ns_ref])
251 end
252 return new Namespace.from([mgroup.mpackage.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
253 end
254
255 private fun ns_for(visibility: MVisibility): nullable Namespace do
256 if visibility <= private_visibility then return namespace
257 var mgroup = self.mgroup
258 if mgroup == null then return namespace
259 return mgroup.mpackage.namespace
260 end
261 end
262
263 redef class MClass
264 redef fun namespace do
265 return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity])
266 end
267 end
268
269 redef class MClassDef
270 redef fun namespace do
271 if is_intro then
272 return new Namespace.from([mmodule.ns_for(mclass.visibility), "$", to_ns_ref: nullable NSEntity])
273 else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
274 return new Namespace.from([mmodule.namespace, "$", mclass.namespace: nullable NSEntity])
275 else if mclass.visibility > private_visibility then
276 return new Namespace.from([mmodule.namespace, "$", mclass.to_ns_ref: nullable NSEntity])
277 end
278 return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
279 end
280 end
281
282 redef class MProperty
283 redef fun namespace do
284 if intro_mclassdef.is_intro then
285 return new Namespace.from([intro_mclassdef.mmodule.ns_for(visibility), "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
286 else
287 return new Namespace.from([intro_mclassdef.mmodule.namespace, "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
288 end
289 end
290 end
291
292 redef class MPropDef
293 redef fun namespace do
294 var res = new Namespace
295 res.add mclassdef.namespace
296 res.add "$"
297
298 if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
299 res.add to_ns_ref
300 else
301 if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
302 res.add mproperty.intro_mclassdef.mmodule.ns_for(mproperty.visibility)
303 res.add "::"
304 else if mproperty.visibility <= private_visibility then
305 if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
306 res.add "::"
307 res.add mproperty.intro_mclassdef.mmodule.to_ns_ref
308 res.add "::"
309 end
310 end
311 if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
312 res.add mproperty.intro_mclassdef.to_ns_ref
313 res.add "::"
314 end
315 res.add to_ns_ref
316 end
317 return res
318 end
319 end
320
321 redef class MClassType
322 redef var web_url = mclass.web_url is lazy
323 end
324
325 redef class MNullableType
326 redef var web_url = mtype.web_url is lazy
327 end
328
329 redef class MParameterType
330 redef var web_url = mclass.web_url is lazy
331 end
332
333 redef class MVirtualType
334 redef var web_url = mproperty.web_url is lazy
335 end
336
337 redef class POSetElement[E]
338 super Serializable
339
340 redef fun serialize_to(v) do
341 assert self isa POSetElement[MEntity]
342 v.serialize_attribute("greaters", to_mentity_refs(greaters))
343 v.serialize_attribute("direct_greaters", to_mentity_refs(direct_greaters))
344 v.serialize_attribute("direct_smallers", to_mentity_refs(direct_smallers))
345 v.serialize_attribute("smallers", to_mentity_refs(smallers))
346 end
347 end