nitweb: better error responses from API
authorAlexandre Terrasa <alexandre@moz-code.org>
Thu, 25 Aug 2016 04:44:43 +0000 (00:44 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Thu, 25 Aug 2016 04:44:43 +0000 (00:44 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/web/api_feedback.nit
src/web/api_graph.nit
src/web/api_metrics.nit
src/web/api_model.nit
src/web/web_base.nit

index 221a25e..e2bfa9a 100644 (file)
@@ -36,28 +36,21 @@ class APIStars
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
-
+               if mentity == null then return
                res.json mentity_ratings(mentity)
        end
 
        redef fun post(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
+               if mentity == null then return
                var obj = req.body.parse_json
                if not obj isa JsonObject then
-                       res.error 400
+                       res.api_error(400, "Expected a JSON object")
                        return
                end
                var rating = obj["rating"]
                if not rating isa Int then
-                       res.error 400
+                       res.api_error(400, "Expected a key `rating`")
                        return
                end
 
index 851da48..cf6e105 100644 (file)
@@ -31,13 +31,10 @@ 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 mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
                var g = new InheritanceGraph(mentity, view)
                res.send g.draw(pdepth, cdepth).to_svg
        end
index af95f53..1683cbb 100644 (file)
@@ -69,13 +69,10 @@ class APIStructuralMetrics
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
+               if mentity == null then return
                var metrics = mentity.collect_metrics(self)
                if metrics == null then
-                       res.error 404
+                       res.api_error(404, "No metric for mentity `{mentity.full_name}`")
                        return
                end
                res.json metrics
index 2b39462..4c9fb8e 100644 (file)
@@ -143,10 +143,7 @@ class APIEntityInheritance
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
+               if mentity == null then return
                res.json mentity.hierarchy_poset(view)[mentity]
        end
 end
@@ -159,13 +156,10 @@ class APIEntityLinearization
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
-               if mentity == null then
-                       res.error 404
-                       return
-               end
+               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)
@@ -180,6 +174,7 @@ class APIEntityDefs
 
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
                var arr = new JsonArray
                if mentity isa MModule then
                        for mclassdef in mentity.mclassdefs do arr.add mclassdef
@@ -190,7 +185,7 @@ class APIEntityDefs
                else if mentity isa MProperty then
                        for mpropdef in mentity.mpropdefs do arr.add mpropdef
                else
-                       res.error 404
+                       res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
                        return
                end
                res.json arr
@@ -218,6 +213,7 @@ 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
@@ -227,7 +223,7 @@ class APIEntityUML
                        var uml = new UMLModel(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)
@@ -245,7 +241,7 @@ class APIEntityCode
                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
index 94bfd70..043bd6c 100644 (file)
@@ -72,12 +72,12 @@ abstract class APIHandler
        fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
                var id = req.param("id")
                if id == null then
-                       res.error 400
+                       res.api_error(400, "Expected mentity full name")
                        return null
                end
                var mentity = find_mentity(view, id)
                if mentity == null then
-                       res.error 404
+                       res.api_error(404, "MEntity `{id}` not found")
                end
                return mentity
        end
@@ -91,6 +91,42 @@ class APIRouter
        var config: NitwebConfig
 end
 
+redef class HttpResponse
+
+       # Return an HTTP error response with `status`
+       #
+       # Like the rest of the API, errors are formated as JSON:
+       # ~~~json
+       # { "status": 404, "message": "Not found" }
+       # ~~~
+       fun api_error(status: Int, message: String) do
+               json(new APIError(status, message), status)
+       end
+end
+
+# An error returned by the API.
+#
+# Can be serialized to json.
+class APIError
+       super Jsonable
+
+       # Reponse status
+       var status: Int
+
+       # Response error message
+       var message: String
+
+       # Json Object for this error
+       var json: JsonObject is lazy do
+               var obj = new JsonObject
+               obj["status"] = status
+               obj["message"] = message
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
 redef class MEntity
 
        # URL to `self` within the web interface.