nitweb: add catalog sidebar on package views
[nit.git] / src / web / api_model.nit
index f8c6eeb..4f1a148 100644 (file)
@@ -17,6 +17,23 @@ module api_model
 import web_base
 import highlight
 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("/entity/:id", new APIEntity(config))
+               use("/entity/:id/doc", new APIEntityDoc(config))
+               use("/code/:id", new APIEntityCode(config))
+               use("/uml/:id", new APIEntityUML(config))
+               use("/linearization/:id", new APIEntityLinearization(config))
+               use("/defs/:id", new APIEntityDefs(config))
+               use("/inheritance/:id", new APIEntityInheritance(config))
+       end
+end
 
 # List all mentities.
 #
@@ -34,25 +51,35 @@ class APIList
                var k = req.string_arg("k")
                var mentities = new Array[MEntity]
                if k == "package" then
-                       for mentity in view.mpackages do mentities.add mentity
+                       for mentity in config.view.mpackages do mentities.add mentity
                else if k == "group" then
-                       for mentity in view.mgroups do mentities.add mentity
+                       for mentity in config.view.mgroups do mentities.add mentity
                else if k == "module" then
-                       for mentity in view.mmodules do mentities.add mentity
+                       for mentity in config.view.mmodules do mentities.add mentity
                else if k == "class" then
-                       for mentity in view.mclasses do mentities.add mentity
+                       for mentity in config.view.mclasses do mentities.add mentity
                else if k == "classdef" then
-                       for mentity in view.mclassdefs do mentities.add mentity
+                       for mentity in config.view.mclassdefs do mentities.add mentity
                else if k == "property" then
-                       for mentity in view.mproperties do mentities.add mentity
+                       for mentity in config.view.mproperties do mentities.add mentity
                else if k == "propdef" then
-                       for mentity in view.mpropdefs do mentities.add mentity
+                       for mentity in config.view.mpropdefs do mentities.add mentity
                else
-                       for mentity in view.mentities do mentities.add mentity
+                       for mentity in config.view.mentities do mentities.add mentity
                end
                return mentities
        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
+
        # Limit mentities depending on the `n` parameter.
        fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
                var n = req.int_arg("n")
@@ -64,6 +91,7 @@ class APIList
 
        redef fun get(req, res) do
                var mentities = list_mentities(req)
+               mentities = sort_mentities(req, mentities)
                mentities = limit_mentities(req, mentities)
                res.json new JsonArray.from(mentities)
        end
@@ -75,14 +103,14 @@ end
 class APISearch
        super APIList
 
-       redef fun list_mentities(req) do
+       redef fun get(req, res) do
                var q = req.string_arg("q")
-               var mentities = new Array[MEntity]
-               if q == null then return mentities
-               for mentity in view.mentities do
-                       if mentity.name.has_prefix(q) then mentities.add mentity
+               if q == null then
+                       res.json new JsonArray
+                       return
                end
-               return mentities
+               var n = req.int_arg("n")
+               res.json new JsonArray.from(config.view.find(q, n))
        end
 end
 
@@ -101,8 +129,8 @@ class APIRandom
 
        redef fun get(req, res) do
                var mentities = list_mentities(req)
-               mentities = limit_mentities(req, mentities)
                mentities = randomize_mentities(req, mentities)
+               mentities = limit_mentities(req, mentities)
                res.json new JsonArray.from(mentities)
        end
 end
@@ -116,7 +144,27 @@ class APIEntity
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
                if mentity == null then return
-               res.json mentity.api_json(self)
+               res.raw_json mentity.to_full_json
+       end
+end
+
+# Return the full MDoc of a MEntity.
+#
+# Example: `GET /entity/core::Array/doc`
+class APIEntityDoc
+       super APIHandler
+
+       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.json obj
        end
 end
 
@@ -128,11 +176,8 @@ class APIEntityInheritance
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
-               res.json mentity.hierarchy_poset(view)[mentity]
+               if mentity == null then return
+               res.json mentity.hierarchy_poset(config.view)[mentity]
        end
 end
 
@@ -144,16 +189,15 @@ class APIEntityLinearization
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
-               var lin = mentity.collect_linearization(mainmodule)
+               if mentity == null then return
+               var lin = mentity.collect_linearization(config.mainmodule)
                if lin == null then
-                       res.error 404
+                       res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
                        return
                end
-               res.json new JsonArray.from(lin)
+               var mentities = new JsonArray
+               for e in lin do mentities.add e
+               res.json mentities
        end
 end
 
@@ -161,24 +205,33 @@ end
 #
 # Example: `GET /defs/core::Array`
 class APIEntityDefs
-       super APIHandler
+       super APIList
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               var arr = new JsonArray
-               if mentity isa MModule then
-                       for mclassdef in mentity.mclassdefs do arr.add mclassdef
+               if mentity == null then return
+               var mentities: Array[MEntity]
+               if mentity isa MPackage then
+                       mentities = mentity.mgroups.to_a
+               else if mentity isa MGroup then
+                       mentities = new Array[MEntity]
+                       mentities.add_all mentity.in_nesting.direct_smallers
+                       mentities.add_all mentity.mmodules
+               else if mentity isa MModule then
+                       mentities = mentity.mclassdefs
                else if mentity isa MClass then
-                       for mclassdef in mentity.mclassdefs do arr.add mclassdef
+                       mentities = mentity.mclassdefs
                else if mentity isa MClassDef then
-                       for mpropdef in mentity.mpropdefs do arr.add mpropdef
+                       mentities = mentity.mpropdefs
                else if mentity isa MProperty then
-                       for mpropdef in mentity.mpropdefs do arr.add mpropdef
+                       mentities = mentity.mpropdefs
                else
-                       res.error 404
+                       res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
                        return
                end
-               res.json arr
+               mentities = sort_mentities(req, mentities)
+               mentities = limit_mentities(req, mentities)
+               res.json new JsonArray.from(mentities)
        end
 end
 
@@ -203,16 +256,17 @@ class APIEntityUML
 
        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(view, mainmodule)
+                       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(view, mentity)
+                       var uml = new UMLModel(config.view, mentity)
                        dot = uml.generate_package_uml.write_to_string
                else
-                       res.error 404
+                       res.api_error(404, "No UML for mentity `{mentity.full_name}`")
                        return
                end
                res.send render_dot(dot)
@@ -225,15 +279,12 @@ end
 class APIEntityCode
        super APIHandler
 
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
        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.error 404
+                       res.api_error(404, "No code for mentity `{mentity.full_name}`")
                        return
                end
                res.send source
@@ -241,7 +292,7 @@ class APIEntityCode
 
        # Highlight `mentity` source code.
        private fun render_source(mentity: MEntity): nullable HTMLTag do
-               var node = modelbuilder.mentity2node(mentity)
+               var node = config.modelbuilder.mentity2node(mentity)
                if node == null then return null
                var hl = new HighlightVisitor
                hl.enter_visit node