import popcorn
import popcorn::pop_config
import popcorn::pop_repos
+import popcorn::pop_json
# Nitweb config file.
class NitwebConfig
super AppConfig
- redef var default_db_name = "nitweb"
+ redef fun default_db_name do return "nitweb"
# Model to use.
var model: Model
# 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 = true
+ return view
+ end
end
# Specific handler for the nitweb API.
return model.mentity_by_full_name(full_name.from_percent_encoding)
end
- # 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(config.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
-
# Try to load the mentity from uri with `/:id`.
#
# Send 400 if `:id` is null.
res.api_error(400, "Expected mentity full name")
return null
end
- var mentity = find_mentity(view, id)
+ var mentity = find_mentity(config.view, id)
if mentity == null then
res.api_error(404, "MEntity `{id}` not found")
end
return mentity
end
+
+ # Paginate a json array
+ #
+ # Returns only a subset of `results` depending on the current `page` and the
+ # number of elements to return set by `limit`.
+ #
+ # Transforms the json array into an object:
+ # ~~~json
+ # {
+ # "page": 2,
+ # "limit": 10,
+ # "results: [ ... ],
+ # "max": 5,
+ # "total": 49
+ # }
+ # ~~~
+ fun paginate(results: JsonArray, count: Int, page, limit: nullable Int): JsonObject do
+ if page == null or page <= 0 then page = 1
+ if limit == null or limit <= 0 then limit = 20
+
+ var max = count / limit
+ if max == 0 then
+ page = 1
+ max = 1
+ else if page > max then
+ page = max
+ end
+
+ var lstart = (page - 1) * limit
+ var lend = limit
+ if lstart + lend > count then lend = count - lstart
+
+ var res = new JsonObject
+ res["page"] = page
+ res["limit"] = limit
+ res["results"] = new JsonArray.from(results.subarray(lstart, lend))
+ res["max"] = max
+ res["total"] = count
+ return res
+ end
end
# A Rooter dedicated to APIHandlers.
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.
#
# Can be serialized to json.
class APIError
- super Jsonable
+ serialize
# Reponse status
var status: Int
# Response error message
var message: String
-
- # Json Object for this error
- var json: JsonObject is lazy do
- var obj = new JsonObject
- obj["status"] = status
- obj["message"] = message
- return obj
- end
-
- redef fun to_json do return json.to_json
end
# Fullname representation that can be used to build decorated links
class Namespace
super Array[nullable NSEntity]
super NSEntity
+ serialize
redef fun to_s do return self.join("")
- redef fun to_json do return (new JsonArray.from(self)).to_json
+ redef fun serialize_to(v) do to_a.serialize_to(v)
end
# Something that goes in a Namespace
# * a `NSToken` for tokens like `::`, `>` and `$`
# * a `MSRef` for references to mentities
interface NSEntity
- super Jsonable
+ super Serializable
end
# A reference to a MEntity that can be rendered as a link.
# an infinite loop.
class NSRef
super NSEntity
+ serialize
# The mentity to link to/
var mentity: MEntity
- redef fun to_json do
- var obj = new JsonObject
- obj["web_url"] = mentity.web_url
- obj["api_url"] = mentity.api_url
- obj["name"] = mentity.name
- return obj.to_json
+ redef fun core_serialize_to(v) do
+ v.serialize_attribute("web_url", mentity.web_url)
+ v.serialize_attribute("api_url", mentity.api_url)
+ v.serialize_attribute("name", mentity.name)
end
end
# URL to `self` within the JSON api.
fun api_url: String do return "/api/entity/" / full_name
- redef fun json do
- var obj = super
- obj["namespace"] = namespace
- obj["web_url"] = web_url
- obj["api_url"] = api_url
- return obj
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("namespace", namespace)
+ v.serialize_attribute("web_url", web_url)
+ v.serialize_attribute("api_url", api_url)
end
- # Get the full json repesentation of `self` with MEntityRefs resolved.
- fun api_json(handler: APIHandler): JsonObject do return full_json
-
# Return `self.full_name` as an object that can be serialized to json.
fun namespace: nullable Namespace do return null
end
redef class MEntityRef
- redef fun json do
- var obj = super
- obj["namespace"] = mentity.namespace
- obj["web_url"] = mentity.web_url
- obj["api_url"] = mentity.api_url
- obj["name"] = mentity.name
- obj["mdoc"] = mentity.mdoc_or_fallback
- obj["visibility"] = mentity.visibility
- var modifiers = new JsonArray
- for modifier in mentity.collect_modifiers do
- modifiers.add modifier
- end
- obj["modifiers"] = modifiers
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("namespace", mentity.namespace)
+ v.serialize_attribute("web_url", mentity.web_url)
+ v.serialize_attribute("api_url", mentity.api_url)
+ v.serialize_attribute("name", mentity.name)
+ v.serialize_attribute("mdoc", mentity.mdoc_or_fallback)
+ v.serialize_attribute("visibility", mentity.visibility.to_s)
+ v.serialize_attribute("modifiers", mentity.collect_modifiers)
+ v.serialize_attribute("class_name", mentity.class_name)
var mentity = self.mentity
if mentity isa MMethod then
- obj["msignature"] = mentity.intro.msignature
+ v.serialize_attribute("msignature", mentity.intro.msignature)
else if mentity isa MMethodDef then
- obj["msignature"] = mentity.msignature
+ v.serialize_attribute("msignature", mentity.msignature)
else if mentity isa MVirtualTypeProp then
- obj["bound"] = to_mentity_ref(mentity.intro.bound)
+ v.serialize_attribute("bound", to_mentity_ref(mentity.intro.bound))
else if mentity isa MVirtualTypeDef then
- obj["bound"] = to_mentity_ref(mentity.bound)
+ v.serialize_attribute("bound", to_mentity_ref(mentity.bound))
end
- return obj
- end
-
- redef fun full_json do
- var obj = super
- obj["location"] = mentity.location
- return obj
+ v.serialize_attribute("location", mentity.location)
end
end
redef class MDoc
# Add doc down processing
- redef fun json do
- var obj = new JsonObject
- obj["html_synopsis"] = html_synopsis.write_to_string
- obj["html_documentation"] = html_documentation.write_to_string
- return obj
+ redef fun core_serialize_to(v) do
+ v.serialize_attribute("html_synopsis", html_synopsis.write_to_string)
end
end
end
return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
end
+
+ redef fun web_url do return "{mclass.web_url}/lin#{full_name}"
end
redef class MProperty
end
return res
end
+
+ redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
end
redef class MClassType
end
redef class POSetElement[E]
- super Jsonable
+ super Serializable
- # Return JSON representation of `self`.
- fun json: JsonObject do
+ redef fun core_serialize_to(v) do
assert self isa POSetElement[MEntity]
- var obj = new JsonObject
- obj["greaters"] = to_mentity_refs(greaters)
- obj["direct_greaters"] = to_mentity_refs(direct_greaters)
- obj["direct_smallers"] = to_mentity_refs(direct_smallers)
- obj["smallers"] = to_mentity_refs(smallers)
- return obj
+ v.serialize_attribute("direct_greaters", to_mentity_refs(direct_greaters))
+ v.serialize_attribute("direct_smallers", to_mentity_refs(direct_smallers))
end
-
- redef fun to_json do return json.to_json
end