import api_auth
import api_model
-import api_catalog
-import api_graph
import api_docdown
import api_metrics
import api_feedback
# Base classes used by `nitweb`.
module api_base
-import model::model_views
-import model::model_json
-import doc_down
import popcorn
import popcorn::pop_config
import popcorn::pop_repos
import popcorn::pop_json
+import commands::commands_http
+import commands::commands_json
+import commands::commands_html
+
# Nitweb config file.
class NitwebConfig
super AppConfig
# Modelbuilder used to access sources.
var modelbuilder: ModelBuilder
- # ModelView used to access model.
+ # The JSON API does not filter anything by default.
+ #
+ # So we can cache the model view.
var view: ModelView
+
+ # Catalog to pass to handlers.
+ var catalog: Catalog is noinit
+
+ # Build the catalog
+ #
+ # This method should be called at nitweb startup.
+ # TODO move to nitweb
+ fun build_catalog do
+ var catalog = new Catalog(modelbuilder)
+ # Compute the poset
+ for p in view.mpackages do
+ var g = p.root
+ assert g != null
+ modelbuilder.scan_group(g)
+
+ catalog.deps.add_node(p)
+ for gg in p.mgroups do for m in gg.mmodules do
+ for im in m.in_importation.direct_greaters do
+ var ip = im.mpackage
+ if ip == null or ip == p then continue
+ catalog.deps.add_edge(p, ip)
+ end
+ end
+ end
+ # Build the catalog
+ for mpackage in view.mpackages do
+ catalog.package_page(mpackage)
+ catalog.git_info(mpackage)
+ catalog.mpackage_stats(mpackage)
+ end
+ self.catalog = catalog
+ end
end
# Specific handler for the nitweb API.
fun api_json(req: HttpRequest, serializable: nullable Serializable, status: nullable Int, plain, pretty: nullable Bool) do
json(serializable, status, plain, req.bool_arg("pretty"))
end
-
- # Return the full version of `serializable` as a json string
- #
- # Uses `req` to define serializable options.
- fun api_full_json(req: HttpRequest, serializable: nullable MEntity, status: nullable Int, plain, pretty: nullable Bool) do
- if serializable == null then
- json(null, status)
- else
- raw_json(serializable.serialize_to_full_json(
- plain or else true, req.bool_arg("pretty") or else false), status)
- end
- 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.
var message: String
end
-# Fullname representation that can be used to build decorated links
-#
-# * MPackage: `mpackage_name`
-# * MGroup: `(mpackage_name::)mgroup_name`
-class Namespace
- super Array[nullable NSEntity]
- super NSEntity
- serialize
-
- redef fun to_s do return self.join("")
- redef fun serialize_to(v) do to_a.serialize_to(v)
-end
-
-# Something that goes in a Namespace
-#
-# Can be either:
-# * a `NSToken` for tokens like `::`, `>` and `$`
-# * a `MSRef` for references to mentities
-interface NSEntity
- super Serializable
-end
-
-# A reference to a MEntity that can be rendered as a link.
-#
-# We do not reuse `MEntityRef` ref since NSRef can be found in a ref and create
-# an infinite loop.
-class NSRef
- super NSEntity
- serialize
-
- # The mentity to link to/
- var mentity: MEntity
-
- 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
-
-# A namespace token representation
-#
-# Used for namespace tokens like `::`, `>` and `$`
-redef class String
- super NSEntity
-end
-
redef class MEntity
# URL to `self` within the web interface.
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
-
- # Return `self.full_name` as an object that can be serialized to json.
- fun namespace: nullable Namespace do return null
-
- # Return a new NSRef to `self`.
- fun to_ns_ref: NSRef do return new NSRef(self)
end
redef class MEntityRef
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
- v.serialize_attribute("msignature", mentity.intro.msignature)
- else if mentity isa MMethodDef then
- v.serialize_attribute("msignature", mentity.msignature)
- else if mentity isa MVirtualTypeProp then
- v.serialize_attribute("bound", to_mentity_ref(mentity.intro.bound))
- else if mentity isa MVirtualTypeDef then
- v.serialize_attribute("bound", to_mentity_ref(mentity.bound))
- end
- v.serialize_attribute("location", mentity.location)
- end
-end
-
-redef class MDoc
-
- # Add doc down processing
- redef fun core_serialize_to(v) do
- v.serialize_attribute("html_synopsis", html_synopsis.write_to_string)
- end
-end
-
-redef class MPackage
- redef fun namespace do return new Namespace.from([to_ns_ref])
-end
-
-redef class MGroup
- redef fun namespace do
- var p = parent
- if p == null then
- return new Namespace.from([to_ns_ref, ">": nullable NSEntity])
- end
- return new Namespace.from([p.namespace, to_ns_ref, ">": nullable NSEntity])
- end
-end
-
-redef class MModule
- redef fun namespace do
- var mgroup = self.mgroup
- if mgroup == null then
- return new Namespace.from([to_ns_ref])
- end
- return new Namespace.from([mgroup.mpackage.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
- end
-
- private fun ns_for(visibility: MVisibility): nullable Namespace do
- if visibility <= private_visibility then return namespace
- var mgroup = self.mgroup
- if mgroup == null then return namespace
- return mgroup.mpackage.namespace
- end
-end
-
-redef class MClass
- redef fun namespace do
- return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity])
end
end
redef class MClassDef
- redef fun namespace do
- if is_intro then
- return new Namespace.from([mmodule.ns_for(mclass.visibility), "$", to_ns_ref: nullable NSEntity])
- else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
- return new Namespace.from([mmodule.namespace, "$", mclass.namespace: nullable NSEntity])
- else if mclass.visibility > private_visibility then
- return new Namespace.from([mmodule.namespace, "$", mclass.to_ns_ref: nullable NSEntity])
- 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
- redef fun namespace do
- if intro_mclassdef.is_intro then
- return new Namespace.from([intro_mclassdef.mmodule.ns_for(visibility), "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
- else
- return new Namespace.from([intro_mclassdef.mmodule.namespace, "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
- end
- end
+redef class MPropDef
+ redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
end
-redef class MPropDef
- redef fun namespace do
- var res = new Namespace
- res.add mclassdef.namespace
- res.add "$"
-
- if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
- res.add to_ns_ref
- else
- if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
- res.add mproperty.intro_mclassdef.mmodule.ns_for(mproperty.visibility)
- res.add "::"
- else if mproperty.visibility <= private_visibility then
- if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
- res.add "::"
- res.add mproperty.intro_mclassdef.mmodule.to_ns_ref
- res.add "::"
- end
- end
- if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
- res.add mproperty.intro_mclassdef.to_ns_ref
- res.add "::"
- end
- res.add to_ns_ref
- end
- return res
+redef class MType
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("web_url", web_url)
+ v.serialize_attribute("api_url", api_url)
end
-
- redef fun web_url do return "{mproperty.web_url}/lin#{full_name}"
end
redef class MClassType
redef var web_url = mclass.web_url is lazy
+ redef var api_url = mclass.api_url is lazy
end
redef class MNullableType
redef var web_url = mtype.web_url is lazy
+ redef var api_url = mtype.api_url is lazy
end
redef class MParameterType
redef var web_url = mclass.web_url is lazy
+ redef var api_url = mclass.api_url is lazy
end
redef class MVirtualType
redef var web_url = mproperty.web_url is lazy
-end
-
-redef class POSetElement[E]
- super Serializable
-
- redef fun core_serialize_to(v) do
- assert self isa POSetElement[MEntity]
- v.serialize_attribute("direct_greaters", to_mentity_refs(direct_greaters))
- v.serialize_attribute("direct_smallers", to_mentity_refs(direct_smallers))
- end
+ redef var api_url = mproperty.api_url is lazy
end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-module api_catalog
-
-import api_model
-import catalog::catalog_json
-
-redef class NitwebConfig
-
- # Catalog to pass to handlers.
- var catalog: Catalog is noinit
-
- # Build the catalog
- #
- # This method should be called at nitweb startup.
- fun build_catalog do
- self.catalog = new Catalog(modelbuilder)
- self.catalog.build_catalog(model.mpackages)
- end
-end
-
-redef class APIRouter
- redef init do
- super
- use("/catalog/packages/", new APICatalogPackages(config))
- use("/catalog/stats", new APICatalogStats(config))
-
- use("/catalog/tags", new APICatalogTags(config))
- use("/catalog/tag/:tid", new APICatalogTag(config))
-
- use("/catalog/person/:pid", new APICatalogPerson(config))
- use("/catalog/person/:pid/maintaining", new APICatalogMaintaining(config))
- use("/catalog/person/:pid/contributing", new APICatalogContributing(config))
- end
-end
-
-abstract class APICatalogHandler
- super APIHandler
-
- # Sorter used to sort packages
- #
- # Sorting is based on mpackage score.
- var mpackages_sorter = new CatalogScoreSorter(config.catalog) is lazy
-end
-
-# Get all the packages from the catalog using pagination
-#
-# `GET /packages?p=1&n=10`: get the list of catalog by page
-class APICatalogPackages
- super APICatalogHandler
-
- redef fun get(req, res) do
- var page = req.int_arg("p")
- var limit = req.int_arg("n")
- var mpackages = config.catalog.mpackages.values.to_a
- mpackages_sorter.sort(mpackages)
- var response = new JsonArray.from(mpackages)
- res.api_json(req, paginate(response, response.length, page, limit))
- end
-end
-
-# Get the catalog statistics
-#
-# `GET /stats`: return the catalog statistics
-class APICatalogStats
- super APICatalogHandler
-
- redef fun get(req, res) do
- res.api_json(req, config.catalog.catalog_stats)
- end
-end
-
-# Get all the tags from the catalog
-#
-# `GET /tags`: the list of tags associated with their number of packages
-class APICatalogTags
- super APICatalogHandler
-
- # Sorter to sort tags alphabetically
- var tags_sorter = new CatalogTagsSorter
-
- redef fun get(req, res) do
- var obj = new JsonObject
-
- var tags = config.catalog.tag2proj.keys.to_a
- tags_sorter.sort(tags)
-
- for tag in tags do
- if not config.catalog.tag2proj.has_key(tag) then continue
- obj[tag] = config.catalog.tag2proj[tag].length
- end
- res.api_json(req, obj)
- end
-end
-
-# Get the packages related to a tag
-#
-# `GET /tag/:tid?p=1&n=10`: return a paginated list of packages
-class APICatalogTag
- super APICatalogHandler
-
- redef fun get(req, res) do
- var page = req.int_arg("p")
- var limit = req.int_arg("n")
- var id = req.param("tid")
- if id == null then
- res.api_error(400, "Missing tag")
- return
- end
- id = id.from_percent_encoding
- if not config.catalog.tag2proj.has_key(id) then
- res.api_error(404, "Tag not found")
- return
- end
- var obj = new JsonObject
- obj["tag"] = id
- var mpackages = config.catalog.tag2proj[id]
- mpackages_sorter.sort(mpackages)
- var response = new JsonArray.from(mpackages)
- obj["packages"] = paginate(response, response.length, page, limit)
- res.api_json(req, obj)
- end
-end
-
-# Get a person existing in the catalog
-#
-# `GET /person/:pid`: get the person with `pid`
-class APICatalogPerson
- super APICatalogHandler
-
- # Get the person with `:pid` or throw a 404 error
- fun get_person(req: HttpRequest, res: HttpResponse): nullable Person do
- var id = req.param("pid")
- if id == null then
- res.api_error(400, "Missing package full_name")
- return null
- end
- id = id.from_percent_encoding
- if not config.catalog.name2person.has_key(id) then
- res.api_error(404, "Person not found")
- return null
- end
- return config.catalog.name2person[id]
- end
-
- redef fun get(req, res) do
- var person = get_person(req, res)
- if person == null then return
- res.api_json(req, person)
- end
-end
-
-# Get the list of mpackages maintained by a person
-#
-# `GET /person/:pid/maintaining?p=1&n=10`: return a paginated list of packages
-class APICatalogMaintaining
- super APICatalogPerson
-
- redef fun get(req, res) do
- var person = get_person(req, res)
- if person == null then return
-
- var page = req.int_arg("p")
- var limit = req.int_arg("n")
- var array = new Array[MPackage]
- if config.catalog.maint2proj.has_key(person) then
- array = config.catalog.maint2proj[person].to_a
- end
- mpackages_sorter.sort(array)
- var response = new JsonArray.from(array)
- res.api_json(req, paginate(response, response.length, page, limit))
- end
-end
-
-# Get the list of mpackages contributed by a person
-#
-# `GET /person/:pid/contributing?p=1&n=10`: return a paginated list of packages
-class APICatalogContributing
- super APICatalogPerson
-
- redef fun get(req, res) do
- var person = get_person(req, res)
- if person == null then return
-
- var page = req.int_arg("p")
- var limit = req.int_arg("n")
- var array = new Array[MPackage]
- if config.catalog.contrib2proj.has_key(person) then
- array = config.catalog.contrib2proj[person].to_a
- end
- mpackages_sorter.sort(array)
- var response = new JsonArray.from(array)
- res.api_json(req, paginate(response, response.length, page, limit))
- end
-end
-
-redef class APIEntity
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
-
- # Special case for packages (catalog view)
- if mentity isa MPackage then
- res.raw_json mentity.to_full_catalog_json(config.catalog, plain = true, pretty = req.bool_arg("pretty"))
- else
- res.api_full_json(req, mentity)
- end
- end
-end
-
-redef class APISearch
- super APICatalogHandler
-
- redef fun search(query, limit) do
- var index = config.view.index
-
- # lookup by name prefix
- var matches = index.find_by_name_prefix(query).uniq.
- sort(lname_sorter, name_sorter, kind_sorter)
- matches = matches.rerank.sort(vis_sorter, score_sorter)
-
- # lookup by tags
- var malus = matches.length
- if config.catalog.tag2proj.has_key(query) then
- for mpackage in config.catalog.tag2proj[query] do
- matches.add new IndexMatch(mpackage, malus)
- malus += 1
- end
- matches = matches.uniq.rerank.sort(vis_sorter, score_sorter)
- end
-
- # lookup by full_name prefix
- malus = matches.length
- var full_matches = new IndexMatches
- for match in index.find_by_full_name_prefix(query).
- sort(lfname_sorter, fname_sorter) do
- match.score += 1
- full_matches.add match
- end
- matches = matches.uniq
-
- # lookup by similarity
- malus = matches.length
- var sim_matches = new IndexMatches
- for match in index.find_by_similarity(query).sort(score_sorter, lname_sorter, name_sorter) do
- if match.score > query.length then break
- match.score += 1
- sim_matches.add match
- end
- matches.add_all sim_matches
- matches = matches.uniq
- return matches.rerank.sort(vis_sorter, score_sorter).mentities
- end
-
- private var score_sorter = new ScoreComparator
- private var vis_sorter = new VisibilityComparator
- private var name_sorter = new NameComparator
- private var lname_sorter = new NameLengthComparator
- private var fname_sorter = new FullNameComparator
- private var lfname_sorter = new FullNameLengthComparator
- private var kind_sorter = new MEntityComparator
-end
-
-redef class Catalog
-
- # Build the catalog from `mpackages`
- fun build_catalog(mpackages: Array[MPackage]) do
- # Compute the poset
- for p in mpackages do
- var g = p.root
- assert g != null
- modelbuilder.scan_group(g)
-
- deps.add_node(p)
- for gg in p.mgroups do for m in gg.mmodules do
- for im in m.in_importation.direct_greaters do
- var ip = im.mpackage
- if ip == null or ip == p then continue
- deps.add_edge(p, ip)
- end
- end
- end
- # Build the catalog
- for mpackage in mpackages do
- package_page(mpackage)
- git_info(mpackage)
- mpackage_stats(mpackage)
- end
- end
-end
-
-redef class MPackage
- # Serialize the full catalog version of `self` to JSON
- #
- # See: `FullCatalogSerializer`
- fun to_full_catalog_json(mainmodule: MModule, catalog: Catalog, plain, pretty: nullable Bool): String do
- var stream = new StringWriter
- var serializer = new FullCatalogSerializer(stream, mainmodule, catalog)
- serializer.plain_json = plain or else false
- serializer.pretty_json = pretty or else false
- serializer.serialize self
- stream.close
- return stream.to_s
- end
-
- redef fun core_serialize_to(v) do
- super
- v.serialize_attribute("metadata", metadata)
- if v isa FullCatalogSerializer then
- v.serialize_attribute("stats", v.catalog.mpackages_stats[self])
-
- var parents = v.catalog.deps[self].direct_greaters.to_a
- v.serialize_attribute("dependencies", v.deps_to_json(parents))
- var children = v.catalog.deps[self].direct_smallers.to_a
- v.serialize_attribute("clients", v.deps_to_json(children))
- end
- end
-end
-
-# CatalogSerializer decorate the Package JSON with full catalog metadata
-#
-# See MEntity::to_full_catalog_json.
-class FullCatalogSerializer
- super FullJsonSerializer
-
- # Catalog used to decorate the MPackages
- var catalog: Catalog
-
- private fun deps_to_json(mpackages: Array[MPackage]): JsonArray do
- var res = new JsonArray
- for mpackage in mpackages do
- res.add dep_to_json(mpackage)
- end
- return res
- end
-
- private fun dep_to_json(mpackage: MPackage): JsonObject do
- var obj = new JsonObject
- obj["name"] = mpackage.name
- var mdoc = mpackage.mdoc_or_fallback
- if mdoc != null then
- obj["synopsis"] = mdoc.synopsis.write_to_string
- end
- return obj
- end
-end
# Nitdoc specific Markdown format handling for Nitweb
module api_docdown
-import api_graph
-intrude import doc_down
-intrude import markdown::wikilinks
-import doc_commands
-import model::model_index
+import api_model
+import commands::commands_docdown
redef class NitwebConfig
# Specific Markdown processor to use within Nitweb
var md_processor: MarkdownProcessor is lazy do
- var proc = new MarkdownProcessor
- proc.decorator = new NitwebDecorator(view, modelbuilder)
+ var parser = new CommandParser(view, modelbuilder)
+ var proc = new CmdMarkdownProcessor(parser)
+ proc.decorator = new CmdDecorator(view)
return proc
end
end
res.html config.md_processor.process(req.body)
end
end
-
-# Specific Markdown decorator for Nitweb
-#
-# We reuse all the implementation of the NitdocDecorator and add the wikilinks handling.
-class NitwebDecorator
- super NitdocDecorator
-
- # View used by wikilink commands to find model entities
- var view: ModelView
-
- # Modelbuilder used to access code
- var modelbuilder: ModelBuilder
-
- redef fun add_span_code(v, buffer, from, to) do
- var text = new FlatBuffer
- buffer.read(text, from, to)
- var name = text.write_to_string
- name = name.replace("nullable ", "")
- var mentity = try_find_mentity(view, name)
- if mentity == null then
- super
- else
- v.add "<code>"
- v.write_mentity_link(mentity, text.write_to_string)
- v.add "</code>"
- end
- end
-
- private fun try_find_mentity(view: ModelView, text: String): nullable MEntity do
- var mentity = view.mentity_by_full_name(text)
- if mentity != null then return mentity
-
- var mentities = view.mentities_by_name(text)
- if mentities.is_empty then
- return null
- else if mentities.length > 1 then
- # TODO smart resolve conflicts
- end
- return mentities.first
- end
-
- redef fun add_wikilink(v, token) do
- v.render_wikilink(token, view)
- end
-end
-
-# Same as `InlineDecorator` but with wikilink commands handling
-class NitwebInlineDecorator
- super InlineDecorator
-
- # View used by wikilink commands to find model entities
- var view: ModelView
-
- # Modelbuilder used to access code
- var modelbuilder: ModelBuilder
-
- redef fun add_wikilink(v, token) do
- v.render_wikilink(token, view)
- end
-end
-
-redef class MarkdownProcessor
-
- # Parser used to process doc commands
- var parser = new DocCommandParser
-
- # Render a wikilink
- fun render_wikilink(token: TokenWikiLink, model: ModelView) do
- var link = token.link
- if link == null then return
- var name = token.name
- if name != null then link = "{name} | {link}"
- var cmd = parser.parse(link.write_to_string)
- if cmd == null then
- var full_name = if token.link != null then token.link.as(not null).write_to_string.trim else null
- if full_name == null or full_name.is_empty then
- write_error("empty wikilink")
- return
- end
- var mentity = find_mentity(model, full_name)
- if mentity == null then return
- name = if token.name != null then token.name.as(not null).to_s else null
- write_mentity_link(mentity, name)
- return
- else
- for message in parser.errors do
- if message.level == 1 then
- write_error(message.message)
- else if message.level > 1 then
- write_warning(message.message)
- end
- end
- end
- cmd.render(self, token, model)
- end
-
- # Find the MEntity that matches `name`.
- #
- # Write an error if the entity is not found
- fun find_mentity(model: ModelView, name: nullable String): nullable MEntity do
- if name == null then
- write_error("no MEntity found")
- return null
- end
- # Lookup by full name
- var mentity = model.mentity_by_full_name(name)
- if mentity != null then return mentity
-
- var mentities = model.mentities_by_name(name)
- if mentities.is_empty then
- var suggest = model.find(name, 3)
- var msg = new Buffer
- msg.append "no MEntity found for name `{name}`"
- if suggest.not_empty then
- msg.append " (suggestions: "
- var i = 0
- for s in suggest do
- msg.append "`{s.full_name}`"
- if i < suggest.length - 1 then msg.append ", "
- i += 1
- end
- msg.append ")"
- end
- write_error(msg.write_to_string)
- return null
- else if mentities.length > 1 then
- var msg = new Buffer
- msg.append "conflicts for name `{name}`"
- msg.append " (conflicts: "
- var i = 0
- for s in mentities do
- msg.append "`{s.full_name}`"
- if i < mentities.length - 1 then msg.append ", "
- i += 1
- end
- msg.append ")"
- write_warning(msg.write_to_string)
- end
- return mentities.first
- end
-
- # Write a warning in the output
- fun write_warning(text: String) do
- emit_text "<p class='text-warning'>Warning: {text}</p>"
- end
-
- # Write an error in the output
- fun write_error(text: String) do
- emit_text "<p class='text-danger'>Error: {text}</p>"
- end
-
- # Write a link to a mentity in the output
- fun write_mentity_link(mentity: MEntity, text: nullable String) do
- var link = mentity.web_url
- var name = text or else mentity.name
- var mdoc = mentity.mdoc_or_fallback
- var comment = null
- if mdoc != null then comment = mdoc.synopsis
- decorator.add_link(self, link, name, comment)
- end
-
- # Write a mentity list in the output
- fun write_mentity_list(mentities: Collection[MEntity]) do
- add "<ul>"
- for mentity in mentities do
- var mdoc = mentity.mdoc_or_fallback
- add "<li>"
- write_mentity_link(mentity)
- if mdoc != null then
- add " - "
- emit_text mdoc.synopsis
- end
- add "</li>"
- end
- add "</ul>"
- end
-end
-
-redef class DocCommand
-
- # Emit the HTML related to the execution of this doc command
- fun render(v: MarkdownProcessor, token: TokenWikiLink, model: ModelView) do
- v.write_error("not yet implemented command `{token.link or else "null"}`")
- end
-end
-
-redef class CommentCommand
- redef fun render(v, token, model) do
- var name = arg
- var mentity = v.find_mentity(model, name)
- if mentity == null then return
- var mdoc = mentity.mdoc_or_fallback
- if mdoc == null then
- v.write_warning("no MDoc for mentity `{name}`")
- return
- end
- v.add "<h3>"
- if not opts.has_key("no-link") then
- v.write_mentity_link(mentity)
- end
- if not opts.has_key("no-link") and not opts.has_key("no-synopsis") then
- v.add " - "
- end
- if not opts.has_key("no-synopsis") then
- v.emit_text mdoc.html_synopsis.write_to_string
- end
- v.add "</h3>"
- if not opts.has_key("no-comment") then
- v.add v.process(mdoc.comment).write_to_string
- end
- end
-end
-
-redef class ListCommand
- redef fun render(v, token, model) do
- var name = arg
- var mentity = v.find_mentity(model, name)
- if mentity == null then return
- if mentity isa MPackage then
- v.write_mentity_list(mentity.mgroups)
- else if mentity isa MGroup then
- var res = new Array[MEntity]
- res.add_all mentity.in_nesting.smallers
- res.add_all mentity.mmodules
- v.write_mentity_list(res)
- else if mentity isa MModule then
- v.write_mentity_list(mentity.mclassdefs)
- else if mentity isa MClass then
- v.write_mentity_list(mentity.collect_intro_mproperties(model))
- else if mentity isa MClassDef then
- v.write_mentity_list(mentity.mpropdefs)
- else if mentity isa MProperty then
- v.write_mentity_list(mentity.mpropdefs)
- else
- v.write_error("no list found for name `{name}`")
- end
- end
-end
-
-redef class CodeCommand
- redef fun render(v, token, model) do
- var name = arg
- var mentity = v.find_mentity(model, name)
- if mentity == null then return
- if mentity isa MClass then mentity = mentity.intro
- if mentity isa MProperty then mentity = mentity.intro
- var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
- if source == null then
- v.write_error("no source for MEntity `{name}`")
- return
- end
- v.add "<pre>"
- v.add source
- v.add "</pre>"
- end
-
- # Highlight `mentity` source code.
- private fun render_source(mentity: MEntity, modelbuilder: ModelBuilder): nullable HTMLTag do
- var node = modelbuilder.mentity2node(mentity)
- if node == null then return null
- var hl = new HtmlightVisitor
- hl.highlight_node node
- return hl.html
- end
-end
-
-redef class GraphCommand
- redef fun render(v, token, model) do
- var name = arg
- var mentity = v.find_mentity(model, name)
- if mentity == null then return
- var g = new InheritanceGraph(mentity, model)
- var pdepth = if opts.has_key("pdepth") and opts["pdepth"].is_int then
- opts["pdepth"].to_i else 3
- var cdepth = if opts.has_key("cdepth") and opts["cdepth"].is_int then
- opts["cdepth"].to_i else 3
- v.add g.draw(pdepth, cdepth).to_svg
- end
-end
-
-redef class Text
- # Read `self` between `nstart` and `nend` (excluded) and writte chars to `out`.
- private fun read(out: FlatBuffer, nstart, nend: Int): Int do
- var pos = nstart
- while pos < length and pos < nend do
- out.add self[pos]
- pos += 1
- end
- if pos == length then return -1
- return pos
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Render various graph from a model.
-module api_graph
-
-import api_base
-import dot
-import uml
-
-redef class APIRouter
- init do
- super
- use("/graph/inheritance/:id", new APIInheritanceGraph(config))
- end
-end
-
-# Render a hierarchy graph for `mentity` if any.
-class APIInheritanceGraph
- super APIHandler
-
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- var pdepth = req.int_arg("pdepth")
- var cdepth = req.int_arg("cdepth")
- var g = new InheritanceGraph(mentity, config.view)
- res.send g.draw(pdepth, cdepth).to_svg
- end
-end
-
-# Graph for mentity hierarchies
-#
-# Recursively build parents and children list from a `center`.
-class InheritanceGraph
- super ModelVisitor
-
- autoinit center, view, filter
-
- # MEntity at the center of this graph
- var center: MEntity
-
- # ModelView used to filter graph
- var view: ModelView
-
- # Graph generated
- var graph: DotGraph is lazy do
- var graph = new DotGraph("package_diagram", "digraph")
-
- graph["compound"] = "true"
- graph["rankdir"] = "BT"
- graph["ranksep"] = 0.3
- graph["nodesep"] = 0.3
-
- graph.nodes_attrs["margin"] = 0.1
- graph.nodes_attrs["width"] = 0
- graph.nodes_attrs["height"] = 0
- graph.nodes_attrs["fontsize"] = 10
- graph.nodes_attrs["fontname"] = "helvetica"
-
- graph.edges_attrs["dir"] = "none"
- graph.edges_attrs["color"] = "gray"
-
- return graph
- end
-
- # Build the graph
- fun draw(parents_depth, children_depth: nullable Int): DotGraph do
- draw_node center
- draw_parents(center, parents_depth)
- draw_children(center, children_depth)
- return graph
- end
-
- private var nodes = new HashMap[MEntity, DotElement]
- private var done_parents = new HashSet[MEntity]
- private var done_children = new HashSet[MEntity]
-
- # Recursively draw parents of mentity
- fun draw_parents(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
- if done_parents.has(mentity) then return
- done_parents.add mentity
- current_depth = current_depth or else 0
- if max_depth != null and current_depth >= max_depth then
- from_dotdotdot(mentity)
- return
- end
- var parents = mentity.collect_parents(view)
- if parents.length > 10 then
- from_dotdotdot(mentity)
- return
- end
- for parent in parents do
- if parent isa MModule then
- var mgroup = parent.mgroup
- if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
- end
- if parent isa MGroup then
- if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
- end
- draw_edge(mentity, parent)
- end
- for parent in parents do
- if parent isa MModule then
- var mgroup = parent.mgroup
- if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
- end
- if parent isa MGroup then
- if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
- end
- draw_parents(parent, max_depth, current_depth + 1)
- end
- end
-
- # Recursively draw children of mentity
- fun draw_children(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
- if done_children.has(mentity) then return
- done_children.add mentity
- current_depth = current_depth or else 0
- if max_depth != null and current_depth >= max_depth then
- to_dotdotdot(mentity)
- return
- end
- var children = mentity.collect_children(view)
- if children.length > 10 then
- to_dotdotdot(mentity)
- return
- end
- for child in children do
- if child isa MGroup then
- if child.mpackage.mgroups.first == child then child = child.mpackage
- end
- draw_edge(child, mentity)
- end
- for child in children do
- if child isa MGroup then
- if child.mpackage.mgroups.first == child then child = child.mpackage
- end
- draw_children(child, max_depth, current_depth + 1)
- end
- end
-
- # Draw a node from a `mentity`
- fun draw_node(mentity: MEntity): DotElement do
- if nodes.has_key(mentity) then return nodes[mentity]
- var node: DotElement = mentity.to_dot_node
- if mentity == center then node = highlight(node)
- nodes[mentity] = node
- graph.add node
- return node
- end
-
- private var edges = new HashMap2[MEntity, MEntity, DotEdge]
-
- # Draw a edges between two mentities
- fun draw_edge(from, to: MEntity): DotEdge do
- if edges.has(from, to) then return edges[from, to].as(not null)
- if edges.has(to, from) then return edges[to, from].as(not null)
- var nfrom = draw_node(from)
- var nto = draw_node(to)
- var edge = new DotEdge(nfrom, nto)
- edges[from, to] = edge
- graph.add edge
- return edge
- end
-
- private var to_dots = new HashMap[MEntity, DotElement]
-
- # Create a link from `mentity` to a `...` node
- fun to_dotdotdot(mentity: MEntity): DotEdge do
- var nto = draw_node(mentity)
- var dots = to_dots.get_or_null(mentity)
- if dots == null then
- dots = dotdotdot("{nto.id}...")
- to_dots[mentity] = dots
- end
- graph.add dots
- var edge = new DotEdge(dots, nto)
- graph.add edge
- return edge
- end
-
- private var from_dots = new HashMap[MEntity, DotElement]
-
- # Create a link from a `...` node to a `mentity`
- fun from_dotdotdot(mentity: MEntity): DotEdge do
- var nfrom = draw_node(mentity)
- var dots = to_dots.get_or_null(mentity)
- if dots == null then
- dots = dotdotdot("...{nfrom.id}")
- from_dots[mentity] = dots
- end
- graph.add dots
- var edge = new DotEdge(dots, nfrom)
- graph.add edge
- return edge
- end
-
- # Change the border color of the node
- fun highlight(dot: DotElement): DotElement do
- dot["color"] = "#1E9431"
- return dot
- end
-
- # Generate a `...` node
- fun dotdotdot(id: String): DotNode do
- var node = new DotNode(id)
- node["label"] = "..."
- node["shape"] = "none"
- return node
- end
-end
-
-redef class MEntity
- private fun to_dot_node: DotNode do
- var node = new DotNode(full_name)
- node["URL"] = web_url
- node["label"] = name
- return node
- end
-end
-
-redef class MPackage
- redef fun to_dot_node do
- var node = super
- node["shape"] = "tab"
- return node
- end
-end
-
-redef class MGroup
- redef fun to_dot_node do
- var node = super
- node["shape"] = "folder"
- return node
- end
-end
-
-redef class MModule
- redef fun to_dot_node do
- var node = super
- node["shape"] = "note"
- return node
- end
-end
-
-redef class MClass
- redef fun to_dot_node do
- var node = super
- node["shape"] = "box"
- return node
- end
-end
module api_light
import api_base
-import htmlight
redef class APIRouter
redef init do
module api_model
import api_base
-import htmlight
-import uml
-import model::model_index
redef class APIRouter
redef init do
super
use("/list", new APIList(config))
- use("/search", new APISearch(config))
use("/random", new APIRandom(config))
+ use("/search", new APISearch(config))
+
use("/entity/:id", new APIEntity(config))
- use("/entity/:id/doc", new APIEntityDoc(config))
+ use("/doc/:id", new APIEntityDoc(config))
use("/code/:id", new APIEntityCode(config))
- use("/uml/:id", new APIEntityUML(config))
- use("/linearization/:id", new APIEntityLinearization(config))
+ use("/lin/:id", new APIEntityLinearization(config))
use("/defs/:id", new APIEntityDefs(config))
- use("/inheritance/:id", new APIEntityInheritance(config))
+ use("/intros/:id", new APIEntityIntros(config))
+ use("/redefs/:id", new APIEntityRedefs(config))
+ use("/meta/:id", new APIEntityMetadata(config))
+ use("/all/:id", new APIEntityAll(config))
+
+ use("/ancestors/:id", new APIEntityAncestors(config))
+ use("/parents/:id", new APIEntityParents(config))
+ use("/children/:id", new APIEntityChildren(config))
+ use("/descendants/:id", new APIEntityDescendants(config))
+
+ use("/uml/:id", new APIEntityUML(config))
+ use("/graph/inheritance/:id", new APIInheritanceGraph(config))
+
+ use("/catalog/packages/", new APICatalogPackages(config))
+ use("/catalog/stats", new APICatalogStats(config))
+ use("/catalog/tags", new APICatalogTags(config))
+ use("/catalog/tag/:tid", new APICatalogTag(config))
+ use("/catalog/person/:pid", new APICatalogPerson(config))
+ use("/catalog/person/:pid/maintaining", new APICatalogMaintaining(config))
+ use("/catalog/person/:pid/contributing", new APICatalogContributing(config))
end
end
-# List all mentities.
-#
-# MEntities can be filtered on their kind using the `k` parameter.
-# Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
-#
-# List size can be limited with the `n` parameter.
-#
-# Example: `GET /list?k=module?n=10`
-class APIList
+# An API Handler that use a DocCommand to respond
+abstract class APICommand
super APIHandler
- # List mentities depending on the `k` kind parameter.
- fun list_mentities(req: HttpRequest): Array[MEntity] do
- var k = req.string_arg("k")
- var mentities = new Array[MEntity]
- if k == "package" then
- for mentity in config.view.mpackages do mentities.add mentity
- else if k == "group" then
- for mentity in config.view.mgroups do mentities.add mentity
- else if k == "module" then
- for mentity in config.view.mmodules do mentities.add mentity
- else if k == "class" then
- for mentity in config.view.mclasses do mentities.add mentity
- else if k == "classdef" then
- for mentity in config.view.mclassdefs do mentities.add mentity
- else if k == "property" then
- for mentity in config.view.mproperties do mentities.add mentity
- else if k == "propdef" then
- for mentity in config.view.mpropdefs do mentities.add mentity
- else
- for mentity in config.view.mentities do mentities.add mentity
- end
- return mentities
- end
+ # Return the doc command to apply for self
+ fun command: DocCommand is abstract
- # Filter mentities based on the config view filters
- fun filter_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
- var res = new Array[MEntity]
- for mentity in mentities do
- if config.view.filter.accept_mentity(mentity) then res.add mentity
+ redef fun get(req, res) do
+ var command = self.command
+ var status = command.http_init(req)
+ if status isa CmdError then
+ res.api_error(status.http_status_code, status.to_s)
+ return
end
- return res
+ res.api_json(req, command.to_json)
end
+end
- # Sort mentities by lexicographic order
- #
- # TODO choose order from request
- fun sort_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
- var sorted = mentities.to_a
- var sorter = new MEntityNameSorter
- sorter.sort(sorted)
- return sorted
- end
+# CmdModel
- # Limit mentities depending on the `n` parameter.
- fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
- var n = req.int_arg("n")
- if n != null then
- return mentities.sub(0, n)
- end
- return mentities
- end
+# List all mentities.
+#
+# Example: `GET /list?kind=modules?limit=10`
+class APIList
+ super APICommand
- redef fun get(req, res) do
- var mentities = list_mentities(req)
- mentities = sort_mentities(req, mentities)
- mentities = limit_mentities(req, mentities)
- res.api_json(req, new JsonArray.from(mentities))
- end
+ redef fun command do return new CmdModelEntities(config.view)
end
-# Search mentities from a query string.
+# Return a random list of MEntities.
#
-# Example: `GET /search?q=Arr`
-class APISearch
- super APIList
-
- redef fun get(req, res) do
- var query = req.string_arg("q")
- if query == null then
- res.api_error(400, "Missing search string")
- return
- end
- var page = req.int_arg("p")
- var limit = req.int_arg("n")
- var response = new JsonArray.from(search(query, limit))
- res.api_json(req, paginate(response, response.length, page, limit))
- end
+# Example: `GET /random?kind=modules&limit=10`
+class APIRandom
+ super APICommand
- fun search(query: String, limit: nullable Int): Array[MEntity] do
- return config.view.find(query)
- end
+ redef fun command do return new CmdRandomEntities(config.view)
end
-# Return a random list of MEntities.
+# Search mentities from a cmd string.
#
-# Example: `GET /random?n=10&k=module`
-class APIRandom
+# Example: `GET /search?q=Arr`
+class APISearch
super APIList
- # Randomize mentities order.
- fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
- var res = mentities.to_a
- res.shuffle
- return res
- end
-
- redef fun get(req, res) do
- var mentities = list_mentities(req)
- mentities = filter_mentities(req, mentities)
- mentities = randomize_mentities(req, mentities)
- mentities = limit_mentities(req, mentities)
- res.api_json(req, new JsonArray.from(mentities))
- end
+ redef fun command do return new CmdCatalogSearch(config.view, config.catalog)
end
+# CmdEntity
+
# Return the JSON representation of a MEntity.
#
# Example: `GET /entity/core::Array`
class APIEntity
- super APIHandler
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- res.api_full_json(req, mentity)
- end
+ redef fun command do return new CmdEntity(config.view)
end
# Return the full MDoc of a MEntity.
#
# Example: `GET /entity/core::Array/doc`
class APIEntityDoc
- super APIHandler
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
-
- var obj = new JsonObject
- var mdoc = mentity.mdoc_or_fallback
- if mdoc != null then
- obj["documentation"] = mdoc.html_documentation.write_to_string
- obj["location"] = mdoc.location
- end
- res.api_json(req, obj)
- end
+ redef fun command do return new CmdComment(config.view)
end
-# List ancestors, parents, child and descendants of MEntity
+# List MEntity ancestors
#
-# Example: `GET /entity/core::Array/inheritance`
-class APIEntityInheritance
- super APIHandler
+# Example: `GET /ancestors/core::Array`
+class APIEntityAncestors
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- res.api_json(req, mentity.hierarchy_poset(config.view)[mentity])
- end
+ redef fun command do return new CmdAncestors(config.view)
+end
+
+# List MEntity parents
+#
+# Example: `GET /parents/core::Array`
+class APIEntityParents
+ super APICommand
+
+ redef fun command do return new CmdParents(config.view)
+end
+
+# List MEntity children
+#
+# Example: `GET /children/core::Array`
+class APIEntityChildren
+ super APICommand
+
+ redef fun command do return new CmdChildren(config.view)
+end
+
+# List MEntity descendants
+#
+# Example: `GET /descendants/core::Array`
+class APIEntityDescendants
+ super APICommand
+
+ redef fun command do return new CmdDescendants(config.view)
end
# Linearize super definitions of a MClassDef or a MPropDef if any.
#
-# Example: `GET /entity/core::Array/linearization`
+# Example: `GET /linearization/core::Array`
class APIEntityLinearization
- super APIHandler
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- var lin = mentity.collect_linearization(config.mainmodule)
- if lin == null then
- res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
- return
- end
- var mentities = new JsonArray
- for e in lin do mentities.add e
- res.api_json(req, mentities)
- end
+ redef fun command do return new CmdLinearization(config.view)
end
# List definitions of a MEntity.
#
# Example: `GET /defs/core::Array`
class APIEntityDefs
- super APIList
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- var mentities = new Array[MEntity]
- if mentity isa MPackage then
- mentities.add_all mentity.collect_mgroups(config.view)
- mentities.add_all mentity.collect_mmodules(config.view)
- else if mentity isa MGroup then
- mentities.add_all mentity.collect_mgroups(config.view)
- mentities.add_all mentity.collect_mmodules(config.view)
- else if mentity isa MModule then
- mentities.add_all mentity.collect_local_mclassdefs(config.view)
- else if mentity isa MClass then
- mentities.add_all mentity.collect_mclassdefs(config.view)
- else if mentity isa MClassDef then
- mentities.add_all mentity.collect_mpropdefs(config.view)
- else if mentity isa MProperty then
- mentities.add_all mentity.collect_mpropdefs(config.view)
- else
- res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
- return
- end
- mentities = filter_mentities(req, mentities)
- mentities = sort_mentities(req, mentities)
- mentities = limit_mentities(req, mentities)
- res.api_json(req, new JsonArray.from(mentities))
- end
+ redef fun command do return new CmdFeatures(config.view)
end
-abstract class SVGHandler
- super APIHandler
+# List intro definitions of a MEntity.
+#
+# Example: `GET /intros/core::Array`
+class APIEntityIntros
+ super APICommand
- # Render a `dot` string as a svg image.
- fun render_dot(dot: Text): String do
- var proc = new ProcessDuplex("dot", "-Tsvg")
- var svg = proc.write_and_read(dot)
- proc.close
- proc.wait
- return svg
- end
+ redef fun command do return new CmdIntros(config.view)
end
-# Return a UML representation of MEntity.
+# List redef definitions of a MEntity.
#
-# Example: `GET /entity/core::Array/uml`
-class APIEntityUML
- super SVGHandler
+# Example: `GET /redefs/core::Array`
+class APIEntityRedefs
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- var dot
- if mentity isa MClassDef then mentity = mentity.mclass
- if mentity isa MClass then
- var uml = new UMLModel(config.view, config.mainmodule)
- dot = uml.generate_class_uml.write_to_string
- else if mentity isa MModule then
- var uml = new UMLModel(config.view, mentity)
- dot = uml.generate_package_uml.write_to_string
- else
- res.api_error(404, "No UML for mentity `{mentity.full_name}`")
- return
- end
- res.send render_dot(dot)
- end
+ redef fun command do return new CmdRedefs(config.view)
+end
+
+# List all definitions accessible from a MEntity.
+#
+# Example: `GET /all/core::Array`
+class APIEntityAll
+ super APICommand
+
+ redef fun command do return new CmdAllProps(config.view)
end
# Return the source code of MEntity.
#
-# Example: `GET /entity/core::Array/code`
+# Example: `GET /code/core::Array`
class APIEntityCode
- super APIHandler
+ super APICommand
- redef fun get(req, res) do
- var mentity = mentity_from_uri(req, res)
- if mentity == null then return
- var source = render_source(mentity)
- if source == null then
- res.api_error(404, "No code for mentity `{mentity.full_name}`")
- return
- end
- res.send source
- end
+ redef fun command do return new CmdCode(config.view, config.modelbuilder)
+end
- # Highlight `mentity` source code.
- private fun render_source(mentity: MEntity): nullable HTMLTag do
- var node = config.modelbuilder.mentity2node(mentity)
- if node == null then return null
- var hl = new HtmlightVisitor
- hl.highlight_node node
- return hl.html
- end
+# Return the UML diagram for MEntity.
+#
+# Example: `GET /uml/core::Array`
+class APIEntityUML
+ super APICommand
+
+ redef fun command do return new CmdUML(config.view)
+end
+
+# Return the inheritance graph for MEntity.
+#
+# Example: `GET /inheritance/core::Array`
+class APIInheritanceGraph
+ super APICommand
+
+ redef fun command do return new CmdInheritanceGraph(config.view)
+end
+
+# CmdCatalog
+
+# Get all the packages from the catalog using pagination
+#
+# `GET /packages?p=1&n=10`: get the list of catalog by page
+class APICatalogPackages
+ super APICommand
+
+ redef fun command do return new CmdCatalogPackages(config.view, config.catalog)
+end
+
+# Get the catalog statistics
+#
+# `GET /stats`: return the catalog statistics
+class APICatalogStats
+ super APICommand
+
+ redef fun command do return new CmdCatalogStats(config.view, config.catalog)
+end
+
+# Get the package metadata
+#
+# `GET /:id/metadata`: return a paginated list of packages
+class APIEntityMetadata
+ super APICommand
+
+ redef fun command do return new CmdMetadata(config.view)
+end
+
+# Get all the tags from the catalog
+#
+# `GET /tags`: the list of tags associated with their number of packages
+class APICatalogTags
+ super APICommand
+
+ redef fun command do return new CmdCatalogTags(config.view, config.catalog)
+end
+
+# Get the packages related to a tag
+#
+# `GET /tag/:tid?p=1&n=10`: return a paginated list of packages
+class APICatalogTag
+ super APICommand
+
+ redef fun command do return new CmdCatalogTag(config.view, config.catalog)
+end
+
+# Get a person existing in the catalog
+#
+# `GET /person/:pid`: get the person with `pid`
+class APICatalogPerson
+ super APICommand
+
+ redef fun command do return new CmdCatalogPerson(config.view, config.catalog)
+end
+
+# Get the list of mpackages maintained by a person
+#
+# `GET /person/:pid/maintaining?p=1&n=10`: return a paginated list of packages
+class APICatalogMaintaining
+ super APICommand
+
+ redef fun command do return new CmdCatalogMaintaining(config.view, config.catalog)
+end
+
+# Get the list of mpackages contributed by a person
+#
+# `GET /person/:pid/contributing?p=1&n=10`: return a paginated list of packages
+class APICatalogContributing
+ super APICommand
+
+ redef fun command do return new CmdCatalogContributing(config.view, config.catalog)
end