+# Nitweb config file.
+class NitwebConfig
+ super AppConfig
+
+ redef fun default_db_name do return "nitweb"
+
+ # Model to use.
+ var model: Model
+
+ # MModule used to flatten model.
+ var mainmodule: MModule
+
+ # Modelbuilder used to access sources.
+ var modelbuilder: ModelBuilder
+
+ # The JSON API does not filter anything by default.
+ #
+ # So we can cache the model view.
+ var view: ModelView is lazy do
+ var view = new ModelView(model)
+ view.min_visibility = private_visibility
+ view.include_fictive = true
+ view.include_empty_doc = true
+ view.include_attribute = true
+ view.include_test_suite = true
+ return view
+ end
+end
+
+# Specific handler for the nitweb API.
+abstract class APIHandler
+ super Handler
+
+ # App config.
+ var config: NitwebConfig
+
+ # Find the MEntity ` with `full_name`.
+ fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+ if full_name == null then return null
+ return model.mentity_by_full_name(full_name.from_percent_encoding)
+ end
+
+ # Try to load the mentity from uri with `/:id`.
+ #
+ # Send 400 if `:id` is null.
+ # Send 404 if no entity is found.
+ # Return null in both cases.
+ fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
+ var id = req.param("id")
+ if id == null then
+ res.api_error(400, "Expected mentity full name")
+ return null
+ end
+ var mentity = find_mentity(config.view, id)
+ if mentity == null then
+ res.api_error(404, "MEntity `{id}` not found")
+ end
+ return mentity
+ end
+end
+
+# A Rooter dedicated to APIHandlers.
+class APIRouter
+ super Router
+
+ # App config
+ var config: NitwebConfig
+end
+
+redef class HttpResponse
+
+ # Return an HTTP error response with `status`
+ #
+ # Like the rest of the API, errors are formated as JSON:
+ # ~~~json
+ # { "status": 404, "message": "Not found" }
+ # ~~~
+ fun api_error(status: Int, message: String) do
+ json(new APIError(status, message), status)
+ end
+
+ # Write data as JSON and set the right content type header.
+ fun raw_json(json: nullable String, status: nullable Int) do
+ header["Content-Type"] = media_types["json"].as(not null)
+ if json == null then
+ send(null, status)
+ else
+ send(json, status)
+ end
+ end
+end
+
+# An error returned by the API.