X-Git-Url: http://nitlanguage.org diff --git a/src/web/web_base.nit b/src/web/web_base.nit index 06a0ff5..60f7f2b 100644 --- a/src/web/web_base.nit +++ b/src/web/web_base.nit @@ -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