nitweb: move `web` group to `doc::api`
[nit.git] / src / doc / api / api_model.nit
diff --git a/src/doc/api/api_model.nit b/src/doc/api/api_model.nit
new file mode 100644 (file)
index 0000000..0829648
--- /dev/null
@@ -0,0 +1,318 @@
+# 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_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("/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.
+#
+# 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
+       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
+
+       # 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
+               end
+               return res
+       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")
+               if n != null then
+                       return mentities.sub(0, n)
+               end
+               return mentities
+       end
+
+       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
+end
+
+# Search mentities from a query string.
+#
+# 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
+
+       fun search(query: String, limit: nullable Int): Array[MEntity] do
+               return config.view.find(query)
+       end
+end
+
+# Return a random list of MEntities.
+#
+# Example: `GET /random?n=10&k=module`
+class APIRandom
+       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
+end
+
+# Return the JSON representation of a MEntity.
+#
+# Example: `GET /entity/core::Array`
+class APIEntity
+       super APIHandler
+
+       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
+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.api_json(req, obj)
+       end
+end
+
+# List ancestors, parents, child and descendants of MEntity
+#
+# Example: `GET /entity/core::Array/inheritance`
+class APIEntityInheritance
+       super APIHandler
+
+       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
+end
+
+# Linearize super definitions of a MClassDef or a MPropDef if any.
+#
+# Example: `GET /entity/core::Array/linearization`
+class APIEntityLinearization
+       super APIHandler
+
+       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
+end
+
+# List definitions of a MEntity.
+#
+# Example: `GET /defs/core::Array`
+class APIEntityDefs
+       super APIList
+
+       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
+end
+
+abstract class SVGHandler
+       super APIHandler
+
+       # 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
+end
+
+# Return a UML representation of MEntity.
+#
+# Example: `GET /entity/core::Array/uml`
+class APIEntityUML
+       super SVGHandler
+
+       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
+end
+
+# Return the source code of MEntity.
+#
+# Example: `GET /entity/core::Array/code`
+class APIEntityCode
+       super APIHandler
+
+       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
+
+       # 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
+end