Merge: nitrpg: Move `nitrpg` to its own repository
[nit.git] / lib / github / api.nit
index 8bd1ee3..fb6fff9 100644 (file)
@@ -26,7 +26,7 @@ intrude import json::serialization_read
 #
 # To access the API you need an instance of a `GithubAPI` client.
 #
-# ~~~
+# ~~~nitish
 # # Get Github authentification token.
 # var token = get_github_oauth
 # assert not token.is_empty
@@ -37,7 +37,7 @@ intrude import json::serialization_read
 #
 # The API client allows you to get Github API entities.
 #
-# ~~~
+# ~~~nitish
 # var repo = api.load_repo("nitlang/nit")
 # assert repo != null
 # assert repo.name == "nit"
@@ -102,21 +102,25 @@ class GithubAPI
        # This method returns raw json data.
        # See other `load_*` methods to use more expressive types.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var obj = api.get("/repos/nitlang/nit")
-       #     assert obj isa JsonObject
-       #     assert obj["name"] == "nit"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var obj = api.get("/repos/nitlang/nit")
+       # assert obj isa JsonObject
+       # assert obj["name"] == "nit"
+       # ~~~
        #
        # Returns `null` in case of `error`.
        #
-       #     obj = api.get("/foo/bar/baz")
-       #     assert obj == null
-       #     assert api.was_error
-       #     var err = api.last_error
-       #     assert err isa GithubError
-       #     assert err.name == "GithubAPIError"
-       #     assert err.message == "Not Found"
-       fun get(path: String): nullable Jsonable do
+       # ~~~nitish
+       # obj = api.get("/foo/bar/baz")
+       # assert obj == null
+       # assert api.was_error
+       # var err = api.last_error
+       # assert err isa GithubError
+       # assert err.name == "GithubAPIError"
+       # assert err.message == "Not Found"
+       # ~~~
+       fun get(path: String): nullable Serializable do
                path = sanitize_uri(path)
                var res = ghcurl.get_and_parse("{api_url}{path}")
                if res isa Error then
@@ -173,10 +177,12 @@ class GithubAPI
        #
        # Loads the `User` from the API or returns `null` if the user cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var user = api.load_user("Morriar")
-       #     print user or else "null"
-       #     assert user.login == "Morriar"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var user = api.load_user("Morriar")
+       # print user or else "null"
+       # assert user.login == "Morriar"
+       # ~~~
        fun load_user(login: String): nullable User do
                return load_from_github("/users/{login}").as(nullable User)
        end
@@ -185,11 +191,13 @@ class GithubAPI
        #
        # Loads the `Repo` from the API or returns `null` if the repo cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo.name == "nit"
-       #     assert repo.owner.login == "nitlang"
-       #     assert repo.default_branch == "master"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo.name == "nit"
+       # assert repo.owner.login == "nitlang"
+       # assert repo.default_branch == "master"
+       # ~~~
        fun load_repo(full_name: String): nullable Repo do
                return load_from_github("/repos/{full_name}").as(nullable Repo)
        end
@@ -200,7 +208,13 @@ class GithubAPI
                var array = get("/repos/{repo.full_name}/branches")
                var res = new Array[Branch]
                if not array isa JsonArray then return res
-               return deserialize(array.to_json).as(Array[Branch])
+               var deser = deserialize(array.to_json)
+               if not deser isa Array[Object] then return res # empty array
+               for branch in deser do
+                       if not branch isa Branch then continue
+                       res.add branch
+               end
+               return res
        end
 
        # List of issues associated with their ids.
@@ -299,12 +313,14 @@ class GithubAPI
        #
        # Returns `null` if the branch cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var branch = api.load_branch(repo, "master")
-       #     assert branch.name == "master"
-       #     assert branch.commit isa Commit
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var branch = api.load_branch(repo, "master")
+       # assert branch.name == "master"
+       # assert branch.commit isa Commit
+       # ~~~
        fun load_branch(repo: Repo, name: String): nullable Branch do
                return load_from_github("/repos/{repo.full_name}/branches/{name}").as(nullable Branch)
        end
@@ -337,11 +353,13 @@ class GithubAPI
        #
        # Returns `null` if the commit cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var commit = api.load_commit(repo, "64ce1f")
-       #     assert commit isa Commit
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var commit = api.load_commit(repo, "64ce1f")
+       # assert commit isa Commit
+       # ~~~
        fun load_commit(repo: Repo, sha: String): nullable Commit do
                return load_from_github("/repos/{repo.full_name}/commits/{sha}").as(nullable Commit)
        end
@@ -350,11 +368,13 @@ class GithubAPI
        #
        # Returns `null` if the issue cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var issue = api.load_issue(repo, 1)
-       #     assert issue.title == "Doc"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var issue = api.load_issue(repo, 1)
+       # assert issue.title == "Doc"
+       # ~~~
        fun load_issue(repo: Repo, number: Int): nullable Issue do
                return load_from_github("/repos/{repo.full_name}/issues/{number}").as(nullable Issue)
        end
@@ -404,12 +424,14 @@ class GithubAPI
        #
        # Returns `null` if the pull request cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var pull = api.load_pull(repo, 1)
-       #     assert pull.title == "Doc"
-       #     assert pull.user.login == "Morriar"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var pull = api.load_pull(repo, 1)
+       # assert pull.title == "Doc"
+       # assert pull.user.login == "Morriar"
+       # ~~~
        fun load_pull(repo: Repo, number: Int): nullable PullRequest do
                return load_from_github("/repos/{repo.full_name}/pulls/{number}").as(nullable PullRequest)
        end
@@ -418,11 +440,13 @@ class GithubAPI
        #
        # Returns `null` if the label cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var labl = api.load_label(repo, "ok_will_merge")
-       #     assert labl != null
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var labl = api.load_label(repo, "ok_will_merge")
+       # assert labl != null
+       # ~~~
        fun load_label(repo: Repo, name: String): nullable Label do
                return load_from_github("/repos/{repo.full_name}/labels/{name}").as(nullable Label)
        end
@@ -431,11 +455,13 @@ class GithubAPI
        #
        # Returns `null` if the milestone cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var stone = api.load_milestone(repo, 4)
-       #     assert stone.title == "v1.0prealpha"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var stone = api.load_milestone(repo, 4)
+       # assert stone.title == "v1.0prealpha"
+       # ~~~
        fun load_milestone(repo: Repo, id: Int): nullable Milestone do
                return load_from_github("/repos/{repo.full_name}/milestones/{id}").as(nullable Milestone)
        end
@@ -444,15 +470,17 @@ class GithubAPI
        #
        # Returns `null` if the event cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo isa Repo
-       #     var event = api.load_issue_event(repo, 199674194)
-       #     assert event isa IssueEvent
-       #     assert event.actor.login == "privat"
-       #     assert event.event == "labeled"
-       #     assert event.labl isa Label
-       #     assert event.labl.name == "need_review"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo isa Repo
+       # var event = api.load_issue_event(repo, 199674194)
+       # assert event isa IssueEvent
+       # assert event.actor.login == "privat"
+       # assert event.event == "labeled"
+       # assert event.labl isa Label
+       # assert event.labl.name == "need_review"
+       # ~~~
        fun load_issue_event(repo: Repo, id: Int): nullable IssueEvent do
                return load_from_github("/repos/{repo.full_name}/issues/events/{id}").as(nullable IssueEvent)
        end
@@ -461,13 +489,15 @@ class GithubAPI
        #
        # Returns `null` if the comment cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var comment = api.load_commit_comment(repo, 8982707)
-       #     assert comment.user.login == "Morriar"
-       #     assert comment.body == "For testing purposes..."
-       #     assert comment.commit_id == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var comment = api.load_commit_comment(repo, 8982707)
+       # assert comment.user.login == "Morriar"
+       # assert comment.body == "For testing purposes...\n"
+       # assert comment.commit_id == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
+       # ~~~
        fun load_commit_comment(repo: Repo, id: Int): nullable CommitComment do
                return load_from_github("/repos/{repo.full_name}/comments/{id}").as(nullable CommitComment)
        end
@@ -476,13 +506,15 @@ class GithubAPI
        #
        # Returns `null` if the comment cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var comment = api.load_issue_comment(repo, 6020149)
-       #     assert comment.user.login == "privat"
-       #     assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
-       #     assert comment.issue_number == 10
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var comment = api.load_issue_comment(repo, 6020149)
+       # assert comment.user.login == "privat"
+       # assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
+       # assert comment.issue_number == 10
+       # ~~~
        fun load_issue_comment(repo: Repo, id: Int): nullable IssueComment do
                return load_from_github("/repos/{repo.full_name}/issues/comments/{id}").as(nullable IssueComment)
        end
@@ -491,13 +523,15 @@ class GithubAPI
        #
        # Returns `null` if the comment cannot be found.
        #
-       #     var api = new GithubAPI(get_github_oauth)
-       #     var repo = api.load_repo("nitlang/nit")
-       #     assert repo != null
-       #     var comment = api.load_review_comment(repo, 21010363)
-       #     assert comment.path == "src/modelize/modelize_property.nit"
-       #     assert comment.original_position == 26
-       #     assert comment.pull_number == 945
+       # ~~~nitish
+       # var api = new GithubAPI(get_github_oauth)
+       # var repo = api.load_repo("nitlang/nit")
+       # assert repo != null
+       # var comment = api.load_review_comment(repo, 21010363)
+       # assert comment.path == "src/modelize/modelize_property.nit"
+       # assert comment.original_position == 26
+       # assert comment.pull_number == 945
+       # ~~~
        fun load_review_comment(repo: Repo, id: Int): nullable ReviewComment do
                return load_from_github("/repos/{repo.full_name}/pulls/comments/{id}").as(nullable ReviewComment)
        end
@@ -507,7 +541,6 @@ end
 #
 # Mainly a Nit wrapper around a JSON objet.
 abstract class GithubEntity
-       super Jsonable
        serialize
 
        # Github page url.
@@ -519,7 +552,7 @@ end
 # Provides access to [Github user data](https://developer.github.com/v3/users/).
 # Should be accessed from `GithubAPI::load_user`.
 class User
-       super GithubEntity
+       super GitUser
        serialize
 
        # Github login.
@@ -527,6 +560,15 @@ class User
 
        # Avatar image url for this user.
        var avatar_url: nullable String is writable
+
+       # User public name if any.
+       var name: nullable String is writable
+
+       # User public email if any.
+       var email: nullable String is writable
+
+       # User public blog if any.
+       var blog: nullable String is writable
 end
 
 # A Github repository.
@@ -582,10 +624,10 @@ class Commit
        var parents: nullable Array[Commit] = null is writable
 
        # Author of the commit.
-       var author: nullable User is writable
+       var author: nullable GitUser is writable
 
        # Committer of the commit.
-       var committer: nullable User is writable
+       var committer: nullable GitUser is writable
 
        # Authoring date as String.
        var author_date: nullable String is writable
@@ -612,6 +654,46 @@ class Commit
 
        # Commit message.
        var message: nullable String is writable
+
+       # Git commit representation linked to this commit.
+       var commit: nullable GitCommit
+end
+
+# A Git Commit representation
+class GitCommit
+       super GithubEntity
+       serialize
+
+       # Commit SHA.
+       var sha: nullable String is writable
+
+       # Parent commits of `self`.
+       var parents: nullable Array[GitCommit] = null is writable
+
+       # Author of the commit.
+       var author: nullable GitUser is writable
+
+       # Committer of the commit.
+       var committer: nullable GitUser is writable
+
+       # Commit message.
+       var message: nullable String is writable
+end
+
+# Git user authoring data
+class GitUser
+       super GithubEntity
+       serialize
+
+       # Authoring date.
+       var date: nullable String = null is writable
+
+       # Authoring date as ISODate.
+       fun iso_date: nullable ISODate do
+               var date = self.date
+               if date == null then return null
+               return new ISODate.from_string(date)
+       end
 end
 
 # A Github issue.
@@ -688,7 +770,7 @@ class Issue
        var closed_by: nullable User is writable
 
        # Is this issue linked to a pull request?
-       var is_pull_request: Bool = false is writable, noserialize
+       var is_pull_request: Bool = false is writable
 end
 
 # A Github pull request.
@@ -748,6 +830,9 @@ class PullRequest
 
        # Changed files count.
        var changed_files: Int is writable
+
+       # URL to patch file
+       var patch_url: nullable String is writable
 end
 
 # A pull request reference (used for head and base).
@@ -798,31 +883,35 @@ class Milestone
        serialize
 
        # The milestone id on Github.
-       var number: Int is writable
+       var number: nullable Int = null is writable
 
        # Milestone title.
        var title: String is writable
 
        # Milestone long description.
-       var description: String is writable
+       var description: nullable String is writable
 
        # Count of opened issues linked to this milestone.
-       var open_issues: Int is writable
+       var open_issues: nullable Int = null is writable
 
        # Count of closed issues linked to this milestone.
-       var closed_issues: Int is writable
+       var closed_issues: nullable Int = null is writable
 
        # Milestone state.
-       var state: String is writable
+       var state: nullable String is writable
 
        # Creation time as String.
-       var created_at: String is writable
+       var created_at: nullable String is writable
 
        # Creation time as ISODate.
-       fun iso_created_at: nullable ISODate do return new ISODate.from_string(created_at)
+       fun iso_created_at: nullable ISODate do
+               var created_at = self.created_at
+               if created_at == null then return null
+               return new ISODate.from_string(created_at)
+       end
 
        # User that created this milestone.
-       var creator: User is writable
+       var creator: nullable User is writable
 
        # Due time as String (if any).
        var due_on: nullable String is writable
@@ -1058,7 +1147,6 @@ end
 
 # Make ISO Datew serilizable
 redef class ISODate
-       super Jsonable
        serialize
 end
 
@@ -1067,7 +1155,7 @@ class GithubDeserializer
        super JsonDeserializer
 
        redef fun class_name_heuristic(json_object) do
-               if json_object.has_key("login") or json_object.has_key("email") then
+               if json_object.has_key("login") then
                        return "User"
                else if json_object.has_key("full_name") then
                        return "Repo"
@@ -1075,8 +1163,12 @@ class GithubDeserializer
                        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") or (json_object.has_key("id") and json_object.has_key("tree_id")) then
+               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
@@ -1104,6 +1196,11 @@ class GithubDeserializer
                                issue.is_pull_request = true
                        end
                        return issue
+               else if name == "Commit" then
+                       var commit = super.as(Commit)
+                       var git_commit = commit.commit
+                       if git_commit != null then commit.message = git_commit.message
+                       return commit
                end
                return super
        end