src/doc/commands/templates: move `commands_json` to `json_commands`
[nit.git] / src / doc / api / api_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 api_base
17
18 import popcorn
19 import popcorn::pop_config
20 import popcorn::pop_repos
21 import popcorn::pop_json
22
23 import commands::commands_http
24
25 import templates::json_commands
26 import templates::html_commands
27
28 # Nitweb config file.
29 class NitwebConfig
30 super AppConfig
31
32 redef fun default_db_name do return "nitweb"
33
34 # Model to use.
35 var model: Model
36
37 # MModule used to flatten model.
38 var mainmodule: MModule
39
40 # Modelbuilder used to access sources.
41 var modelbuilder: ModelBuilder
42
43 # The JSON API does not filter anything by default.
44 var filter: nullable ModelFilter
45
46 # Catalog to pass to handlers.
47 var catalog: Catalog
48 end
49
50 # Specific handler for the nitweb API.
51 abstract class APIHandler
52 super Handler
53
54 # App config.
55 var config: NitwebConfig
56
57 # Find the MEntity ` with `full_name`.
58 fun find_mentity(full_name: nullable String): nullable MEntity do
59 if full_name == null then return null
60 var mentity = config.model.mentity_by_full_name(full_name.from_percent_encoding, config.filter)
61 if mentity == null then return null
62
63 var filter = config.filter
64 if filter == null or filter.accept_mentity(mentity) then return mentity
65 return null
66 end
67
68 # Try to load the mentity from uri with `/:id`.
69 #
70 # Send 400 if `:id` is null.
71 # Send 404 if no entity is found.
72 # Return null in both cases.
73 fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
74 var id = req.param("id")
75 if id == null then
76 res.api_error(400, "Expected mentity full name")
77 return null
78 end
79 var mentity = find_mentity(id)
80 if mentity == null then
81 res.api_error(404, "MEntity `{id}` not found")
82 end
83 return mentity
84 end
85
86 # Paginate a json array
87 #
88 # Returns only a subset of `results` depending on the current `page` and the
89 # number of elements to return set by `limit`.
90 #
91 # Transforms the json array into an object:
92 # ~~~json
93 # {
94 # "page": 2,
95 # "limit": 10,
96 # "results: [ ... ],
97 # "max": 5,
98 # "total": 49
99 # }
100 # ~~~
101 fun paginate(results: JsonArray, count: Int, page, limit: nullable Int): JsonObject do
102 if page == null or page <= 0 then page = 1
103 if limit == null or limit <= 0 then limit = 20
104
105 var max = count / limit
106 if max == 0 then
107 page = 1
108 max = 1
109 else if page > max then
110 page = max
111 end
112
113 var lstart = (page - 1) * limit
114 var lend = limit
115 if lstart + lend > count then lend = count - lstart
116
117 var res = new JsonObject
118 res["page"] = page
119 res["limit"] = limit
120 res["results"] = new JsonArray.from(results.subarray(lstart, lend))
121 res["max"] = max
122 res["total"] = count
123 return res
124 end
125 end
126
127 # A Rooter dedicated to APIHandlers.
128 class APIRouter
129 super Router
130
131 # App config
132 var config: NitwebConfig
133 end
134
135 redef class HttpResponse
136
137 # Return an HTTP error response with `status`
138 #
139 # Like the rest of the API, errors are formated as JSON:
140 # ~~~json
141 # { "status": 404, "message": "Not found" }
142 # ~~~
143 fun api_error(status: Int, message: String) do
144 json(new APIError(status, message), status)
145 end
146
147 # Return `serializable` as a json string
148 #
149 # Uses `req` to define serialization options.
150 fun api_json(req: HttpRequest, serializable: nullable Serializable, status: nullable Int, plain, pretty: nullable Bool) do
151 json(serializable, status, plain, req.bool_arg("pretty"))
152 end
153 end
154
155 # An error returned by the API.
156 #
157 # Can be serialized to json.
158 class APIError
159 serialize
160
161 # Reponse status
162 var status: Int
163
164 # Response error message
165 var message: String
166 end
167
168 redef class MEntity
169
170 # URL to `self` within the web interface.
171 fun web_url: String do return "/doc/" / full_name
172
173 # URL to `self` within the JSON api.
174 fun api_url: String do return "/api/entity/" / full_name
175
176 redef fun html_url do return web_url
177
178 redef fun core_serialize_to(v) do
179 super
180 v.serialize_attribute("web_url", web_url)
181 v.serialize_attribute("api_url", api_url)
182 end
183 end
184
185 redef class MEntityRef
186 redef fun core_serialize_to(v) do
187 super
188 v.serialize_attribute("web_url", mentity.web_url)
189 v.serialize_attribute("api_url", mentity.api_url)
190 end
191 end
192
193 redef class MClassDef
194 redef fun web_url do return "{mclass.web_url}/lin#{full_name}"
195 end
196
197 redef class MPropDef
198 redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
199 end
200
201 redef class MType
202 redef fun core_serialize_to(v) do
203 super
204 v.serialize_attribute("web_url", web_url)
205 v.serialize_attribute("api_url", api_url)
206 end
207 end
208
209 redef class MClassType
210 redef var web_url = mclass.web_url is lazy
211 redef var api_url = mclass.api_url is lazy
212 end
213
214 redef class MNullableType
215 redef var web_url = mtype.web_url is lazy
216 redef var api_url = mtype.api_url is lazy
217 end
218
219 redef class MParameterType
220 redef var web_url = mclass.web_url is lazy
221 redef var api_url = mclass.api_url is lazy
222 end
223
224 redef class MVirtualType
225 redef var web_url = mproperty.web_url is lazy
226 redef var api_url = mproperty.api_url is lazy
227 end
228
229 redef class HtmlightVisitor
230 redef fun hrefto(mentity) do return mentity.html_url
231 end
232
233 redef class CmdLicenseFile
234 redef var file_url is lazy do
235 var mentity = self.mentity
236 if mentity == null then return super
237 return "{mentity.web_url}/license"
238 end
239 end
240
241 redef class CmdContribFile
242 redef var file_url is lazy do
243 var mentity = self.mentity
244 if mentity == null then return super
245 return "{mentity.web_url}/contrib"
246 end
247 end