1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
20 redef class NitwebConfig
22 # Catalog to pass to handlers.
23 var catalog
: Catalog is noinit
27 # This method should be called at nitweb startup.
29 self.catalog
= new Catalog(modelbuilder
)
30 self.catalog
.build_catalog
(model
.mpackages
)
37 use
("/catalog/packages/", new APICatalogPackages(config
))
38 use
("/catalog/stats", new APICatalogStats(config
))
40 use
("/catalog/tags", new APICatalogTags(config
))
41 use
("/catalog/tag/:tid", new APICatalogTag(config
))
43 use
("/catalog/person/:pid", new APICatalogPerson(config
))
44 use
("/catalog/person/:pid/maintaining", new APICatalogMaintaining(config
))
45 use
("/catalog/person/:pid/contributing", new APICatalogContributing(config
))
49 abstract class APICatalogHandler
52 # Sorter used to sort packages
54 # Sorting is based on mpackage score.
55 var mpackages_sorter
= new CatalogScoreSorter(config
.catalog
) is lazy
58 # Get all the packages from the catalog using pagination
60 # `GET /packages?p=1&n=10`: get the list of catalog by page
61 class APICatalogPackages
62 super APICatalogHandler
64 redef fun get
(req
, res
) do
65 var page
= req
.int_arg
("p")
66 var limit
= req
.int_arg
("n")
67 var mpackages
= config
.catalog
.mpackages
.values
.to_a
68 mpackages_sorter
.sort
(mpackages
)
69 var response
= new JsonArray.from
(mpackages
)
70 res
.json paginate
(response
, response
.length
, page
, limit
)
74 # Get the catalog statistics
76 # `GET /stats`: return the catalog statistics
78 super APICatalogHandler
80 redef fun get
(req
, res
) do
81 res
.json config
.catalog
.catalog_stats
85 # Get all the tags from the catalog
87 # `GET /tags`: the list of tags associated with their number of packages
89 super APICatalogHandler
91 # Sorter to sort tags alphabetically
92 var tags_sorter
= new CatalogTagsSorter
94 redef fun get
(req
, res
) do
95 var obj
= new JsonObject
97 var tags
= config
.catalog
.tag2proj
.keys
.to_a
98 tags_sorter
.sort
(tags
)
101 if not config
.catalog
.tag2proj
.has_key
(tag
) then continue
102 obj
[tag
] = config
.catalog
.tag2proj
[tag
].length
108 # Get the packages related to a tag
110 # `GET /tag/:tid?p=1&n=10`: return a paginated list of packages
112 super APICatalogHandler
114 redef fun get
(req
, res
) do
115 var page
= req
.int_arg
("p")
116 var limit
= req
.int_arg
("n")
117 var id
= req
.param
("tid")
119 res
.api_error
(400, "Missing tag")
122 id
= id
.from_percent_encoding
123 if not config
.catalog
.tag2proj
.has_key
(id
) then
124 res
.api_error
(404, "Tag not found")
127 var obj
= new JsonObject
129 var mpackages
= config
.catalog
.tag2proj
[id
]
130 mpackages_sorter
.sort
(mpackages
)
131 var response
= new JsonArray.from
(mpackages
)
132 obj
["packages"] = paginate
(response
, response
.length
, page
, limit
)
137 # Get a person existing in the catalog
139 # `GET /person/:pid`: get the person with `pid`
140 class APICatalogPerson
141 super APICatalogHandler
143 # Get the person with `:pid` or throw a 404 error
144 fun get_person
(req
: HttpRequest, res
: HttpResponse): nullable Person do
145 var id
= req
.param
("pid")
147 res
.api_error
(400, "Missing package full_name")
150 id
= id
.from_percent_encoding
151 if not config
.catalog
.name2person
.has_key
(id
) then
152 res
.api_error
(404, "Person not found")
155 return config
.catalog
.name2person
[id
]
158 redef fun get
(req
, res
) do
159 var person
= get_person
(req
, res
)
160 if person
== null then return
165 # Get the list of mpackages maintained by a person
167 # `GET /person/:pid/maintaining?p=1&n=10`: return a paginated list of packages
168 class APICatalogMaintaining
169 super APICatalogPerson
171 redef fun get
(req
, res
) do
172 var person
= get_person
(req
, res
)
173 if person
== null then return
175 var page
= req
.int_arg
("p")
176 var limit
= req
.int_arg
("n")
177 var array
= new Array[MPackage]
178 if config
.catalog
.maint2proj
.has_key
(person
) then
179 array
= config
.catalog
.maint2proj
[person
].to_a
181 mpackages_sorter
.sort
(array
)
182 var response
= new JsonArray.from
(array
)
183 res
.json paginate
(response
, response
.length
, page
, limit
)
187 # Get the list of mpackages contributed by a person
189 # `GET /person/:pid/contributing?p=1&n=10`: return a paginated list of packages
190 class APICatalogContributing
191 super APICatalogPerson
193 redef fun get
(req
, res
) do
194 var person
= get_person
(req
, res
)
195 if person
== null then return
197 var page
= req
.int_arg
("p")
198 var limit
= req
.int_arg
("n")
199 var array
= new Array[MPackage]
200 if config
.catalog
.contrib2proj
.has_key
(person
) then
201 array
= config
.catalog
.contrib2proj
[person
].to_a
203 mpackages_sorter
.sort
(array
)
204 var response
= new JsonArray.from
(array
)
205 res
.json paginate
(response
, response
.length
, page
, limit
)
209 redef class APIEntity
210 redef fun get
(req
, res
) do
211 var mentity
= mentity_from_uri
(req
, res
)
212 if mentity
== null then return
214 # Special case for packages (catalog view)
215 if mentity
isa MPackage then
216 res
.raw_json mentity
.to_full_catalog_json
(plain
=true, config
.catalog
)
218 res
.raw_json mentity
.to_full_json
225 # Build the catalog from `mpackages`
226 fun build_catalog
(mpackages
: Array[MPackage]) do
228 for p
in mpackages
do
231 modelbuilder
.scan_group
(g
)
234 for gg
in p
.mgroups
do for m
in gg
.mmodules
do
235 for im
in m
.in_importation
.direct_greaters
do
237 if ip
== null or ip
== p
then continue
243 for mpackage
in mpackages
do
244 package_page
(mpackage
)
246 mpackage_stats
(mpackage
)
251 redef class MPackageMetadata
254 redef fun core_serialize_to
(v
) do
256 v
.serialize_attribute
("license", license
)
257 v
.serialize_attribute
("maintainers", maintainers
)
258 v
.serialize_attribute
("contributors", contributors
)
259 v
.serialize_attribute
("tags", tags
)
260 v
.serialize_attribute
("tryit", tryit
)
261 v
.serialize_attribute
("apk", apk
)
262 v
.serialize_attribute
("homepage", homepage
)
263 v
.serialize_attribute
("browse", browse
)
264 v
.serialize_attribute
("git", git
)
265 v
.serialize_attribute
("issues", issues
)
266 v
.serialize_attribute
("first_date", first_date
)
267 v
.serialize_attribute
("last_date", last_date
)
272 redef class CatalogStats
275 redef fun core_serialize_to
(v
) do
277 v
.serialize_attribute
("packages", packages
)
278 v
.serialize_attribute
("maintainers", maintainers
)
279 v
.serialize_attribute
("contributors", contributors
)
280 v
.serialize_attribute
("tags", tags
)
281 v
.serialize_attribute
("modules", modules
)
282 v
.serialize_attribute
("classes", classes
)
283 v
.serialize_attribute
("methods", methods
)
284 v
.serialize_attribute
("loc", loc
)
288 # MPackage statistics for the catalog
289 redef class MPackageStats
292 redef fun core_serialize_to
(v
) do
294 v
.serialize_attribute
("mmodules", mmodules
)
295 v
.serialize_attribute
("mclasses", mclasses
)
296 v
.serialize_attribute
("mmethods", mmethods
)
297 v
.serialize_attribute
("loc", loc
)
298 v
.serialize_attribute
("errors", errors
)
299 v
.serialize_attribute
("warnings", warnings
)
300 v
.serialize_attribute
("warnings_per_kloc", warnings_per_kloc
)
301 v
.serialize_attribute
("documentation_score", documentation_score
)
302 v
.serialize_attribute
("commits", commits
)
303 v
.serialize_attribute
("score", score
)
310 redef fun core_serialize_to
(v
) do
312 v
.serialize_attribute
("name", name
)
313 v
.serialize_attribute
("email", email
)
314 v
.serialize_attribute
("gravatar", gravatar
)
319 # Serialize the full catalog version of `self` to JSON
321 # See: `FullCatalogSerializer`
322 fun to_full_catalog_json
(catalog
: Catalog, plain
, pretty
: nullable Bool): String do
323 var stream
= new StringWriter
324 var serializer
= new FullCatalogSerializer(stream
, catalog
)
325 serializer
.plain_json
= plain
or else false
326 serializer
.pretty_json
= pretty
or else false
327 serializer
.serialize
self
332 redef fun core_serialize_to
(v
) do
334 v
.serialize_attribute
("metadata", metadata
)
335 if v
isa FullCatalogSerializer then
336 v
.serialize_attribute
("stats", v
.catalog
.mpackages_stats
[self])
338 var parents
= v
.catalog
.deps
[self].direct_greaters
.to_a
339 v
.serialize_attribute
("dependencies", v
.deps_to_json
(parents
))
340 var children
= v
.catalog
.deps
[self].direct_smallers
.to_a
341 v
.serialize_attribute
("clients", v
.deps_to_json
(children
))
346 # CatalogSerializer decorate the Package JSON with full catalog metadata
348 # See MEntity::to_full_catalog_json.
349 class FullCatalogSerializer
350 super FullJsonSerializer
352 # Catalog used to decorate the MPackages
355 private fun deps_to_json
(mpackages
: Array[MPackage]): JsonArray do
356 var res
= new JsonArray
357 for mpackage
in mpackages
do
358 res
.add dep_to_json
(mpackage
)
363 private fun dep_to_json
(mpackage
: MPackage): JsonObject do
364 var obj
= new JsonObject
365 obj
["name"] = mpackage
.name
366 var mdoc
= mpackage
.mdoc_or_fallback
368 obj
["synopsis"] = mdoc
.synopsis
.write_to_string