github: Clean GithubAPI deserialization
authorAlexandre Terrasa <alexandre@moz-code.org>
Thu, 11 Jul 2019 01:24:37 +0000 (21:24 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Thu, 11 Jul 2019 02:07:01 +0000 (22:07 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

lib/github/api.nit
lib/github/hooks.nit
lib/github/tests/test_api.nit

index 26ea8c5..8684c29 100644 (file)
@@ -19,7 +19,6 @@
 # For most use-cases you need to use the `GithubAPI` client.
 module api
 
-# TODO to remove
 intrude import json::serialization_read
 import json::static
 
@@ -1029,38 +1028,47 @@ end
 class GithubDeserializer
        super JsonDeserializer
 
-       redef fun class_name_heuristic(json_object) do
-               if json_object.has_key("login") then
-                       return "User"
-               else if json_object.has_key("full_name") then
-                       return "Repo"
-               else if json_object.has_key("name") and json_object.has_key("commit") then
+       private var pattern_base = "https://api.github.com"
+
+       # Url patterns to class names
+       var url_patterns: Map[Regex, String] is lazy do
+               var map = new HashMap[Regex, String]
+               map["{pattern_base}/users/[^/]*$".to_re] = "User"
+               map["{pattern_base}/repos/[^/]*/[^/]*$".to_re] = "Repo"
+               map["{pattern_base}/repos/[^/]*/[^/]*/labels/[^/]+$".to_re] = "Label"
+               map["{pattern_base}/repos/[^/]*/[^/]*/milestones/[0-9]+$".to_re] = "Milestone"
+               map["{pattern_base}/repos/[^/]*/[^/]*/issues/[0-9]+$".to_re] = "Issue"
+               map["{pattern_base}/repos/[^/]*/[^/]*/issues/comments/[0-9]+$".to_re] = "IssueComment"
+               map["{pattern_base}/repos/[^/]*/[^/]*/issues/events/[0-9]+$".to_re] = "IssueEvent"
+               map["{pattern_base}/repos/[^/]*/[^/]*/pulls/[0-9]+$".to_re] = "PullRequest"
+               map["{pattern_base}/repos/[^/]*/[^/]*/pulls/comments/[0-9]+$".to_re] = "ReviewComment"
+               map["{pattern_base}/repos/[^/]*/[^/]*/comments/[0-9]+$".to_re] = "CommitComment"
+               map["{pattern_base}/repos/[^/]*/[^/]*/commits/[a-f0-9]+$".to_re] = "Commit"
+               return map
+       end
+
+       # Match `url` property in object to a class name
+       fun url_heuristic(raw: Map[String, nullable Object]): nullable String do
+               if not raw.has_key("url") then return null
+
+               var url = raw["url"].as(String)
+               for re, class_name in url_patterns do
+                       if url.has(re) then return class_name
+               end
+               return null
+       end
+
+       redef fun class_name_heuristic(raw) do
+               # Try with url
+               var class_name = url_heuristic(raw)
+               if class_name != null then return class_name
+
+               # print raw.serialize_to_json(true, true) # debug
+
+               # Use properties heuristics
+               if raw.has_key("name") and raw.has_key("commit") then
                        return "Branch"
-               else if json_object.has_key("sha") and json_object.has_key("ref") then
-                       return "PullRef"
-               else if (json_object.has_key("sha") and json_object.has_key("commit")) or (json_object.has_key("id") and json_object.has_key("tree_id")) then
-                       return "Commit"
-               else if json_object.has_key("sha") and json_object.has_key("tree") then
-                       return "GitCommit"
-               else if json_object.has_key("name") and json_object.has_key("date") then
-                       return "GitUser"
-               else if json_object.has_key("number") and json_object.has_key("patch_url") then
-                       return "PullRequest"
-               else if json_object.has_key("open_issues") and json_object.has_key("closed_issues") then
-                       return "Milestone"
-               else if json_object.has_key("number") and json_object.has_key("title") then
-                       return "Issue"
-               else if json_object.has_key("color") then
-                       return "Label"
-               else if json_object.has_key("event") then
-                       return "IssueEvent"
-               else if json_object.has_key("original_commit_id") then
-                       return "ReviewComment"
-               else if json_object.has_key("commit_id") then
-                       return "CommitComment"
-               else if json_object.has_key("issue_url") then
-                       return "IssueComment"
-               else if json_object.has_key("total_count") then
+               else if raw.has_key("total_count") and raw.has_key("items") then
                        return "SearchResults"
                end
                return null
index 70f16c2..bfd076d 100644 (file)
@@ -88,36 +88,35 @@ abstract class HookListener
        end
 
        # How to build events from received json objects.
-       fun event_factory(kind: String, json: JsonObject): GithubEvent do
+       fun event_factory(kind: String, json: String): nullable GithubEvent do
                if kind == "commit_comment" then
-                       return api.deserialize(json.to_json).as(CommitCommentEvent)
+                       return api.deserialize(json).as(CommitCommentEvent)
                else if kind == "create" then
-                       return api.deserialize(json.to_json).as(CreateEvent)
+                       return api.deserialize(json).as(CreateEvent)
                else if kind == "delete" then
-                       return api.deserialize(json.to_json).as(DeleteEvent)
+                       return api.deserialize(json).as(DeleteEvent)
                else if kind == "deployment" then
-                       return api.deserialize(json.to_json).as(DeploymentEvent)
+                       return api.deserialize(json).as(DeploymentEvent)
                else if kind == "deployment_status" then
-                       return api.deserialize(json.to_json).as(DeploymentStatusEvent)
+                       return api.deserialize(json).as(DeploymentStatusEvent)
                else if kind == "fork" then
-                       return api.deserialize(json.to_json).as(ForkEvent)
+                       return api.deserialize(json).as(ForkEvent)
                else if kind == "issues" then
-                       return api.deserialize(json.to_json).as(IssuesEvent)
+                       return api.deserialize(json).as(IssuesEvent)
                else if kind == "issue_comment" then
-                       return api.deserialize(json.to_json).as(IssueCommentEvent)
+                       return api.deserialize(json).as(IssueCommentEvent)
                else if kind == "member" then
-                       return api.deserialize(json.to_json).as(MemberEvent)
+                       return api.deserialize(json).as(MemberEvent)
                else if kind == "pull_request" then
-                       return api.deserialize(json.to_json).as(PullRequestEvent)
+                       return api.deserialize(json).as(PullRequestEvent)
                else if kind == "pull_request_review_comment" then
-                       return api.deserialize(json.to_json).as(PullRequestReviewCommentEvent)
+                       return api.deserialize(json).as(PullRequestReviewCommentEvent)
                else if kind == "push" then
-                       return api.deserialize(json.to_json).as(PushEvent)
+                       return api.deserialize(json).as(PushEvent)
                else if kind == "status" then
-                       return api.deserialize(json.to_json).as(StatusEvent)
-               else
-                       return api.deserialize(json.to_json).as(GithubEvent)
+                       return api.deserialize(json).as(StatusEvent)
                end
+               return null
        end
 
        # What to do when we receive an event from a hook?
@@ -139,11 +138,9 @@ private class HookAction
                # get event type
                var kind = request.header.get_or_null("X-GitHub-Event")
                if kind == null then return new HttpResponse(403)
-               # get POST object
-               var obj = request.body.parse_json
-               if not obj isa JsonObject then return new HttpResponse(403)
                # parse event
-               var event = listener.event_factory(kind, obj)
+               var event = listener.event_factory(kind, request.body)
+               if event == null then return new HttpResponse(403)
                listener.apply_event(event)
                return new HttpResponse(200)
        end
index dd53cee..0b0fcb8 100644 (file)
@@ -307,7 +307,7 @@ class TestGithubAPI
 
        fun test_get_pull is test do
                var pull = api.get_pull("nitlang/nit", 1000)
-               assert pull isa Issue
+               assert pull isa PullRequest
                assert pull.number == 1000
                assert pull.title == "Raise nitc from the dead"
                assert pull.user.as(User).login == "privat"