nitweb: catalog api serves tags related data
[nit.git] / src / web / api_catalog.nit
index c5b5d62..4d4ca75 100644 (file)
@@ -34,17 +34,27 @@ end
 redef class APIRouter
        redef init do
                super
+               use("/catalog/packages/", new APICatalogPackages(config))
+
                use("/catalog/highlighted", new APICatalogHighLighted(config))
                use("/catalog/required", new APICatalogMostRequired(config))
                use("/catalog/bytags", new APICatalogByTags(config))
                use("/catalog/contributors", new APICatalogContributors(config))
                use("/catalog/stats", new APICatalogStats(config))
+
+               use("/catalog/tags", new APICatalogTags(config))
+               use("/catalog/tag/:tid", new APICatalogTag(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
+
        # List the 10 best packages from `cpt`
        fun list_best(cpt: Counter[MPackage]): JsonArray do
                var res = new JsonArray
@@ -70,19 +80,19 @@ abstract class APICatalogHandler
        end
 end
 
-class APICatalogStats
+# 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 obj = new JsonObject
-               obj["packages"] = config.model.mpackages.length
-               obj["maintainers"] = config.catalog.maint2proj.length
-               obj["contributors"] = config.catalog.contrib2proj.length
-               obj["modules"] = config.catalog.mmodules.sum
-               obj["classes"] = config.catalog.mclasses.sum
-               obj["methods"] = config.catalog.mmethods.sum
-               obj["loc"] = config.catalog.loc.sum
-               res.json obj
+               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.json paginate(response, response.length, page, limit)
        end
 end
 
@@ -125,6 +135,68 @@ class APICatalogContributors
        end
 end
 
+# Get the catalog statistics
+#
+# `GET /stats`: return the catalog statistics
+class APICatalogStats
+       super APICatalogHandler
+
+       redef fun get(req, res) do
+               res.json 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.json 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.json obj
+       end
+end
 redef class Catalog
 
        # Build the catalog from `mpackages`
@@ -219,3 +291,59 @@ redef class Person
                v.serialize_attribute("gravatar", gravatar)
        end
 end
+
+redef class MPackage
+       # Serialize the full catalog version of `self` to JSON
+       #
+       # See: `FullCatalogSerializer`
+       fun to_full_catalog_json(catalog: Catalog, plain, pretty: nullable Bool): String do
+               var stream = new StringWriter
+               var serializer = new FullCatalogSerializer(stream, 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