*: update redefs of `to_json`
[nit.git] / src / web / web_base.nit
index 06a0ff5..60f7f2b 100644 (file)
@@ -36,45 +36,12 @@ class NitwebConfig
 
        # Modelbuilder used to access sources.
        var modelbuilder: ModelBuilder
-end
-
-# Specific nitcorn Action that uses a Model
-class ModelHandler
-       super Handler
-
-       # App config.
-       var config: NitwebConfig
-
-       # Find the MEntity ` with `full_name`.
-       fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
-               if full_name == null then return null
-               return model.mentity_by_full_name(full_name.from_percent_encoding)
-       end
-
-       # Init the model view from the `req` uri parameters.
-       fun init_model_view(req: HttpRequest): ModelView do
-               var view = new ModelView(config.model)
-               var show_private = req.bool_arg("private") or else false
-               if not show_private then view.min_visibility = protected_visibility
-
-               view.include_fictive = req.bool_arg("fictive") or else false
-               view.include_empty_doc = req.bool_arg("empty-doc") or else true
-               view.include_test_suite = req.bool_arg("test-suite") or else false
-               view.include_attribute = req.bool_arg("attributes") or else true
-
-               return view
-       end
-end
-
-# Specific handler for nitweb API.
-abstract class APIHandler
-       super ModelHandler
 
        # The JSON API does not filter anything by default.
        #
        # So we can cache the model view.
        var view: ModelView is lazy do
-               var view = new ModelView(config.model)
+               var view = new ModelView(model)
                view.min_visibility = private_visibility
                view.include_fictive = true
                view.include_empty_doc = true
@@ -82,6 +49,20 @@ abstract class APIHandler
                view.include_test_suite = true
                return view
        end
+end
+
+# Specific handler for the nitweb API.
+abstract class APIHandler
+       super Handler
+
+       # App config.
+       var config: NitwebConfig
+
+       # Find the MEntity ` with `full_name`.
+       fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+               if full_name == null then return null
+               return model.mentity_by_full_name(full_name.from_percent_encoding)
+       end
 
        # Try to load the mentity from uri with `/:id`.
        #
@@ -91,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)
+               var mentity = find_mentity(config.view, id)
                if mentity == null then
-                       res.error 404
+                       res.api_error(404, "MEntity `{id}` not found")
                end
                return mentity
        end
@@ -110,6 +91,91 @@ 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 serialize_to(v) do json.serialize_to(v)
+end
+
+# Fullname representation that can be used to build decorated links
+#
+# * MPackage: `mpackage_name`
+# * MGroup: `(mpackage_name::)mgroup_name`
+class Namespace
+       super Array[nullable NSEntity]
+       super NSEntity
+       serialize
+
+       redef fun to_s do return self.join("")
+       redef fun serialize_to(v) do to_a.serialize_to(v)
+end
+
+# Something that goes in a Namespace
+#
+# Can be either:
+# * a `NSToken` for tokens like `::`, `>` and `$`
+# * a `MSRef` for references to mentities
+interface NSEntity
+       super Jsonable
+end
+
+# A reference to a MEntity that can be rendered as a link.
+#
+# We do not reuse `MEntityRef` ref since NSRef can be found in a ref and create
+# an infinite loop.
+class NSRef
+       super NSEntity
+       serialize
+
+       # The mentity to link to/
+       var mentity: MEntity
+
+       redef fun serialize_to(v) do
+               var obj = new JsonObject
+               obj["web_url"] = mentity.web_url
+               obj["api_url"] = mentity.api_url
+               obj["name"] = mentity.name
+               obj.serialize_to(v)
+       end
+end
+
+# A namespace token representation
+#
+# Used for namespace tokens like `::`, `>` and `$`
+redef class String
+       super NSEntity
+end
+
 redef class MEntity
 
        # URL to `self` within the web interface.
@@ -120,24 +186,31 @@ redef class MEntity
 
        redef fun json do
                var obj = super
+               obj["namespace"] = namespace
                obj["web_url"] = web_url
                obj["api_url"] = api_url
                return obj
        end
 
        # Get the full json repesentation of `self` with MEntityRefs resolved.
-       fun api_json(handler: ModelHandler): JsonObject do return json
+       fun api_json(handler: APIHandler): JsonObject do return full_json
+
+       # Return `self.full_name` as an object that can be serialized to json.
+       fun namespace: nullable Namespace do return null
+
+       # Return a new NSRef to `self`.
+       fun to_ns_ref: NSRef do return new NSRef(self)
 end
 
 redef class MEntityRef
        redef fun json do
                var obj = super
+               obj["namespace"] = mentity.namespace
                obj["web_url"] = mentity.web_url
                obj["api_url"] = mentity.api_url
                obj["name"] = mentity.name
                obj["mdoc"] = mentity.mdoc_or_fallback
                obj["visibility"] = mentity.visibility
-               obj["location"] = mentity.location
                var modifiers = new JsonArray
                for modifier in mentity.collect_modifiers do
                        modifiers.add modifier
@@ -155,78 +228,111 @@ redef class MEntityRef
                end
                return obj
        end
+
+       redef fun full_json do
+               var obj = super
+               obj["location"] = mentity.location
+               return obj
+       end
 end
 
 redef class MDoc
 
        # Add doc down processing
        redef fun json do
-               var obj = super
-               obj["synopsis"] = synopsis
-               obj["documentation"] = documentation
-               obj["comment"] = comment
+               var obj = new JsonObject
                obj["html_synopsis"] = html_synopsis.write_to_string
                obj["html_documentation"] = html_documentation.write_to_string
-               obj["html_comment"] = html_comment.write_to_string
                return obj
        end
 end
 
+redef class MPackage
+       redef fun namespace do return new Namespace.from([to_ns_ref])
+end
+
+redef class MGroup
+       redef fun namespace do
+               var p = parent
+               if p == null then
+                       return new Namespace.from([to_ns_ref, ">": nullable NSEntity])
+               end
+               return new Namespace.from([p.namespace, to_ns_ref, ">": nullable NSEntity])
+       end
+end
+
 redef class MModule
-       redef fun api_json(handler) do
-               var obj = super
-               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
-               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
-               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
-               return obj
+       redef fun namespace do
+               var mgroup = self.mgroup
+               if mgroup == null then
+                       return new Namespace.from([to_ns_ref])
+               end
+               return new Namespace.from([mgroup.mpackage.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+       end
+
+       private fun ns_for(visibility: MVisibility): nullable Namespace do
+               if visibility <= private_visibility then return namespace
+               var mgroup = self.mgroup
+               if mgroup == null then return namespace
+               return mgroup.mpackage.namespace
        end
 end
 
 redef class MClass
-       redef fun api_json(handler) do
-               var obj = super
-               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
-               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
-               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
-               obj["parents"] = to_mentity_refs(collect_parents(private_view))
-               return obj
+       redef fun namespace do
+               return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity])
        end
 end
 
 redef class MClassDef
-       redef fun json do
-               var obj = super
-               obj["intro"] = to_mentity_ref(mclass.intro)
-               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
-               return obj
-       end
-
-       redef fun api_json(handler) do
-               var obj = super
-               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
-               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
-               return obj
+       redef fun namespace do
+               if is_intro then
+                       return new Namespace.from([mmodule.ns_for(mclass.visibility), "$", to_ns_ref: nullable NSEntity])
+               else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
+                       return new Namespace.from([mmodule.namespace, "$", mclass.namespace: nullable NSEntity])
+               else if mclass.visibility > private_visibility then
+                       return new Namespace.from([mmodule.namespace, "$", mclass.to_ns_ref: nullable NSEntity])
+               end
+               return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
        end
 end
 
 redef class MProperty
-       redef fun json do
-               var obj = super
-               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
-               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
-               return obj
+       redef fun namespace do
+               if intro_mclassdef.is_intro then
+                       return new Namespace.from([intro_mclassdef.mmodule.ns_for(visibility), "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+               else
+                       return new Namespace.from([intro_mclassdef.mmodule.namespace, "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+               end
        end
 end
 
 redef class MPropDef
-       redef fun json do
-               var obj = super
-               obj["intro"] = to_mentity_ref(mproperty.intro)
-               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
-               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
-               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
-               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
-               return obj
+       redef fun namespace do
+               var res = new Namespace
+               res.add mclassdef.namespace
+               res.add "$"
+
+               if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
+                       res.add to_ns_ref
+               else
+                       if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
+                               res.add mproperty.intro_mclassdef.mmodule.ns_for(mproperty.visibility)
+                               res.add "::"
+                       else if mproperty.visibility <= private_visibility then
+                               if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
+                                       res.add "::"
+                                       res.add mproperty.intro_mclassdef.mmodule.to_ns_ref
+                                       res.add "::"
+                               end
+                       end
+                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+                               res.add mproperty.intro_mclassdef.to_ns_ref
+                               res.add "::"
+                       end
+                       res.add to_ns_ref
+               end
+               return res
        end
 end
 
@@ -260,5 +366,5 @@ redef class POSetElement[E]
                return obj
        end
 
-       redef fun to_json do return json.to_json
+       redef fun serialize_to(v) do json.serialize_to(v)
 end