1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Nit object oriented interface to Github api.
17 # This modules reifies Github API elements as Nit classes.
19 # For most use-cases you need to use the `GithubAPI` client.
24 # Interface to Github REST API.
26 # Used by all `GithubEntity` to perform requests.
31 # # Get Github authentification token.
32 # var token = get_github_oauth
33 # assert not token.is_empty
36 # var api = new GithubAPI(token)
39 # The API client allows to get Github API entities:
42 # var repo = api.load_repo("privat/nit")
44 # assert repo.name == "nit"
46 # var user = api.load_user("Morriar")
48 # assert user.login == "Morriar"
52 # Github API OAuth token.
54 # This token is used to authenticate the application on Github API.
55 # Be aware that there is [rate limits](https://developer.github.com/v3/rate_limit/)
56 # associated to the key.
59 # Github API base url.
61 # Default is `https://api.github.com` and should not be changed.
62 var api_url
= "https://api.github.com"
64 # User agent used for HTTP requests.
66 # Default is `nit_github_api`.
68 # See <https://developer.github.com/v3/#user-agent-required>
69 var user_agent
= "nit_github_api"
73 # Internal Curl instance used to perform API calls.
74 private var ghcurl
: GithubCurl is noinit
78 # * `0`: only errors (default)
80 var verbose_lvl
= 0 is public
writable
83 ghcurl
= new GithubCurl(auth
, user_agent
)
86 # Execute a GET request on Github API.
88 # This method returns raw json data.
89 # See other `load_*` methods to use more expressive types.
91 # var api = new GithubAPI(get_github_oauth)
92 # var obj = api.get("repos/privat/nit")
93 # assert obj isa JsonObject
94 # assert obj["name"] == "nit"
96 # Returns `null` in case of `error`.
98 # obj = api.get("foo/bar/baz")
100 # assert api.was_error
101 # var err = api.last_error
102 # assert err isa GithubError
103 # assert err.name == "GithubAPIError"
104 # assert err.message == "Not Found"
105 fun get
(path
: String): nullable Jsonable do
106 path
= sanitize_uri
(path
)
107 var res
= ghcurl
.get_and_parse
("{api_url}/{path}")
108 if res
isa Error then
117 # Display a message depending on `verbose_lvl`.
118 fun message
(lvl
: Int, message
: String) do
119 if lvl
<= verbose_lvl
then print message
122 # Escape `uri` in an acceptable format for Github.
123 private fun sanitize_uri
(uri
: String): String do
124 # TODO better URI escape.
125 return uri
.replace
(" ", "%20")
128 # Last error occured during Github API communications.
129 var last_error
: nullable Error = null is public
writable
131 # Does the last request provoqued an error?
132 var was_error
= false is protected writable
134 # Load the json object from Github.
135 # See `GithubEntity::load_from_github`.
136 protected fun load_from_github
(key
: String): JsonObject do
137 message
(1, "Get {key} (github)")
139 if was_error
then return new JsonObject
140 return res
.as(JsonObject)
143 # Get the Github user with `login`.
145 # Returns `null` if the user cannot be found.
147 # var api = new GithubAPI(get_github_oauth)
148 # var user = api.load_user("Morriar")
149 # assert user.login == "Morriar"
150 fun load_user
(login
: String): nullable User do
151 var user
= new User(self, login
)
152 return user
.load_from_github
155 # Get the Github repo with `full_name`.
157 # Returns `null` if the repo cannot be found.
159 # var api = new GithubAPI(get_github_oauth)
160 # var repo = api.load_repo("privat/nit")
161 # assert repo.name == "nit"
162 # assert repo.owner.login == "privat"
163 # assert repo.default_branch.name == "master"
164 fun load_repo
(full_name
: String): nullable Repo do
165 var repo
= new Repo(self, full_name
)
166 return repo
.load_from_github
169 # Get the Github branch with `name`.
171 # Returns `null` if the branch cannot be found.
173 # var api = new GithubAPI(get_github_oauth)
174 # var repo = api.load_repo("privat/nit")
175 # assert repo != null
176 # var branch = api.load_branch(repo, "master")
177 # assert branch.name == "master"
178 # assert branch.commit isa Commit
179 fun load_branch
(repo
: Repo, name
: String): nullable Branch do
180 var branch
= new Branch(self, repo
, name
)
181 return branch
.load_from_github
184 # Get the Github commit with `sha`.
186 # Returns `null` if the commit cannot be found.
188 # var api = new GithubAPI(get_github_oauth)
189 # var repo = api.load_repo("privat/nit")
190 # assert repo != null
191 # var commit = api.load_commit(repo, "64ce1f")
192 # assert commit isa Commit
193 fun load_commit
(repo
: Repo, sha
: String): nullable Commit do
194 var commit
= new Commit(self, repo
, sha
)
195 return commit
.load_from_github
198 # Get the Github issue #`number`.
200 # Returns `null` if the issue cannot be found.
202 # var api = new GithubAPI(get_github_oauth)
203 # var repo = api.load_repo("privat/nit")
204 # assert repo != null
205 # var issue = api.load_issue(repo, 1)
206 # assert issue.title == "Doc"
207 fun load_issue
(repo
: Repo, number
: Int): nullable Issue do
208 var issue
= new Issue(self, repo
, number
)
209 return issue
.load_from_github
212 # Get the Github pull request #`number`.
214 # Returns `null` if the pull request cannot be found.
216 # var api = new GithubAPI(get_github_oauth)
217 # var repo = api.load_repo("privat/nit")
218 # assert repo != null
219 # var pull = api.load_pull(repo, 1)
220 # assert pull.title == "Doc"
221 # assert pull.user.login == "Morriar"
222 fun load_pull
(repo
: Repo, number
: Int): nullable PullRequest do
223 var pull
= new PullRequest(self, repo
, number
)
224 return pull
.load_from_github
227 # Get the Github label with `name`.
229 # Returns `null` if the label cannot be found.
231 # var api = new GithubAPI(get_github_oauth)
232 # var repo = api.load_repo("privat/nit")
233 # assert repo != null
234 # var labl = api.load_label(repo, "ok_will_merge")
235 # assert labl != null
236 fun load_label
(repo
: Repo, name
: String): nullable Label do
237 var labl
= new Label(self, repo
, name
)
238 return labl
.load_from_github
241 # Get the Github milestone with `id`.
243 # Returns `null` if the milestone cannot be found.
245 # var api = new GithubAPI(get_github_oauth)
246 # var repo = api.load_repo("privat/nit")
247 # assert repo != null
248 # var stone = api.load_milestone(repo, 4)
249 # assert stone.title == "v1.0prealpha"
250 fun load_milestone
(repo
: Repo, id
: Int): nullable Milestone do
251 var milestone
= new Milestone(self, repo
, id
)
252 return milestone
.load_from_github
255 # Get the Github issue event with `id`.
257 # Returns `null` if the event cannot be found.
259 # var api = new GithubAPI(get_github_oauth)
260 # var repo = api.load_repo("privat/nit")
261 # assert repo isa Repo
262 # var event = api.load_issue_event(repo, 199674194)
263 # assert event.actor.login == "privat"
264 # assert event.event == "labeled"
265 # assert event.labl.name == "need_review"
266 # assert event.issue.number == 945
267 fun load_issue_event
(repo
: Repo, id
: Int): nullable IssueEvent do
268 var event
= new IssueEvent(self, repo
, id
)
269 return event
.load_from_github
272 # Get the Github commit comment with `id`.
274 # Returns `null` if the comment cannot be found.
276 # var api = new GithubAPI(get_github_oauth)
277 # var repo = api.load_repo("privat/nit")
278 # assert repo != null
279 # var comment = api.load_commit_comment(repo, 8982707)
280 # assert comment.user.login == "Morriar"
281 # assert comment.body == "For testing purposes..."
282 # assert comment.commit.sha == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
283 fun load_commit_comment
(repo
: Repo, id
: Int): nullable CommitComment do
284 var comment
= new CommitComment(self, repo
, id
)
285 return comment
.load_from_github
288 # Get the Github issue comment with `id`.
290 # Returns `null` if the comment cannot be found.
292 # var api = new GithubAPI(get_github_oauth)
293 # var repo = api.load_repo("privat/nit")
294 # assert repo != null
295 # var comment = api.load_issue_comment(repo, 6020149)
296 # assert comment.user.login == "privat"
297 # assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
298 # assert comment.issue.number == 10
299 fun load_issue_comment
(repo
: Repo, id
: Int): nullable IssueComment do
300 var comment
= new IssueComment(self, repo
, id
)
301 return comment
.load_from_github
304 # Get the Github diff comment with `id`.
306 # Returns `null` if the comment cannot be found.
308 # var api = new GithubAPI(get_github_oauth)
309 # var repo = api.load_repo("privat/nit")
310 # assert repo != null
311 # var comment = api.load_review_comment(repo, 21010363)
312 # assert comment.path == "src/modelize/modelize_property.nit"
313 # assert comment.original_position == 26
314 # assert comment.pull.number == 945
315 fun load_review_comment
(repo
: Repo, id
: Int): nullable ReviewComment do
316 var comment
= new ReviewComment(self, repo
, id
)
317 return comment
.load_from_github
321 # Something returned by the Github API.
323 # Mainly a Nit wrapper around a JSON objet.
324 abstract class GithubEntity
326 # Github API instance.
329 # FIXME constructor should be private
331 # Key used to access this entity from Github api base.
332 fun key
: String is abstract
334 # JSON representation of `self`.
336 # This is the same json structure than used by Github API.
337 var json
: JsonObject is noinit
, protected writable
339 # Load `json` from Github API.
340 private fun load_from_github
: nullable SELF do
341 json
= api
.load_from_github
(key
)
342 if api
.was_error
then return null
346 redef fun to_s
do return json
.to_json
349 fun html_url
: String do return json
["html_url"].to_s
354 # Should be accessed from `GithubAPI::load_user`.
356 # See <https://developer.github.com/v3/users/>.
360 redef var key
is lazy
do return "users/{login}"
365 # Init `self` from a `json` object.
366 init from_json
(api
: GithubAPI, json
: JsonObject) do
367 init(api
, json
["login"].to_s
)
371 # Avatar image url for this user.
372 fun avatar_url
: String do return json
["avatar_url"].to_s
375 # A Github repository.
377 # Should be accessed from `GithubAPI::load_repo`.
379 # See <https://developer.github.com/v3/repos/>.
383 redef var key
is lazy
do return "repos/{full_name}"
385 # Repo full name on Github.
386 var full_name
: String
388 # Init `self` from a `json` object.
389 init from_json
(api
: GithubAPI, json
: JsonObject) do
390 init(api
, json
["full_name"].to_s
)
394 # Repo short name on Github.
395 fun name
: String do return json
["name"].to_s
397 # Get the repo owner.
399 return new User.from_json
(api
, json
["owner"].as(JsonObject))
402 # List of branches associated with their names.
403 fun branches
: Map[String, Branch] do
404 api
.message
(1, "Get branches for {full_name}")
405 var array
= api
.get
("repos/{full_name}/branches")
406 var res
= new HashMap[String, Branch]
407 if not array
isa JsonArray then return res
409 if not obj
isa JsonObject then continue
410 var name
= obj
["name"].to_s
411 res
[name
] = new Branch.from_json
(api
, self, obj
)
416 # List of issues associated with their ids.
417 fun issues
: Map[Int, Issue] do
418 api
.message
(1, "Get issues for {full_name}")
419 var res
= new HashMap[Int, Issue]
420 var issue
= last_issue
421 if issue
== null then return res
422 res
[issue
.number
] = issue
423 while issue
.number
> 1 do
424 issue
= api
.load_issue
(self, issue
.number
- 1)
425 assert issue
isa Issue
426 res
[issue
.number
] = issue
431 # Search issues in this repo form an advanced query.
436 # var issues = repo.search_issues("is:open label:need_review")
439 # See <https://developer.github.com/v3/search/#search-issues>.
440 fun search_issues
(query
: String): nullable Array[Issue] do
441 query
= "search/issues?q={query} repo:{full_name}"
442 var response
= api
.get
(query
)
443 if api
.was_error
then return null
444 var arr
= response
.as(JsonObject)["items"].as(JsonArray)
445 var res
= new Array[Issue]
447 res
.add
new Issue.from_json
(api
, self, obj
.as(JsonObject))
452 # Get the last published issue.
453 fun last_issue
: nullable Issue do
454 var array
= api
.get
("repos/{full_name}/issues")
455 if not array
isa JsonArray then return null
456 if array
.is_empty
then return null
457 var obj
= array
.first
458 if not obj
isa JsonObject then return null
459 return new Issue.from_json
(api
, self, obj
)
462 # List of labels associated with their names.
463 fun labels
: Map[String, Label] do
464 api
.message
(1, "Get labels for {full_name}")
465 var array
= api
.get
("repos/{full_name}/labels")
466 var res
= new HashMap[String, Label]
467 if not array
isa JsonArray then return res
469 if not obj
isa JsonObject then continue
470 var name
= obj
["name"].to_s
471 res
[name
] = new Label.from_json
(api
, self, obj
)
476 # List of milestones associated with their ids.
477 fun milestones
: Map[Int, Milestone] do
478 api
.message
(1, "Get milestones for {full_name}")
479 var array
= api
.get
("repos/{full_name}/milestones")
480 var res
= new HashMap[Int, Milestone]
481 if array
isa JsonArray then
483 if not obj
isa JsonObject then continue
484 var number
= obj
["number"].as(Int)
485 res
[number
] = new Milestone.from_json
(api
, self, obj
)
491 # List of pull-requests associated with their ids.
493 # Implementation notes: because PR numbers are not consecutive,
494 # PR are loaded from pages.
495 # See: https://developer.github.com/v3/pulls/#list-pull-requests
496 fun pulls
: Map[Int, PullRequest] do
497 api
.message
(1, "Get pulls for {full_name}")
498 var res
= new HashMap[Int, PullRequest]
500 var array
= api
.get
("{key}/pulls?page={page}").as(JsonArray)
501 while not array
.is_empty
do
503 if not obj
isa JsonObject then continue
504 var number
= obj
["number"].as(Int)
505 res
[number
] = new PullRequest.from_json
(api
, self, obj
)
508 array
= api
.get
("{key}/pulls?page={page}").as(JsonArray)
513 # List of contributor related statistics.
514 fun contrib_stats
: Array[ContributorStats] do
515 api
.message
(1, "Get contributor stats for {full_name}")
516 var res
= new Array[ContributorStats]
517 var array
= api
.get
("{key}/stats/contributors")
518 if array
isa JsonArray then
520 res
.add
new ContributorStats.from_json
(api
, obj
.as(JsonObject))
526 # Repo default branch.
527 fun default_branch
: Branch do
528 var name
= json
["default_branch"].to_s
529 var branch
= api
.load_branch
(self, name
)
530 assert branch
isa Branch
535 # A `RepoEntity` is something contained in a `Repo`.
536 abstract class RepoEntity
539 # Repo that contains `self`.
542 # Init `self` from a `json` object.
543 init from_json
(api
: GithubAPI, repo
: Repo, json
: JsonObject) do
552 # Should be accessed from `GithubAPI::load_branch`.
554 # See <https://developer.github.com/v3/repos/#list-branches>.
558 redef var key
is lazy
do return "{repo.key}/branches/{name}"
563 redef init from_json
(api
, repo
, json
) do
564 self.name
= json
["name"].to_s
568 # Get the last commit of `self`.
569 fun commit
: Commit do
570 return new Commit.from_json
(api
, repo
, json
["commit"].as(JsonObject))
573 # List all commits in `self`.
575 # This can be long depending on the branch size.
576 # Commit are returned in an unspecified order.
577 fun commits
: Array[Commit] do
578 var res
= new Array[Commit]
579 var done
= new HashSet[String]
580 var todos
= new Array[Commit]
582 while not todos
.is_empty
do
583 var commit
= todos
.pop
584 if done
.has
(commit
.sha
) then continue
587 for parent
in commit
.parents
do
597 # Should be accessed from `GithubAPI::load_commit`.
599 # See <https://developer.github.com/v3/commits/>.
603 redef var key
is lazy
do return "{repo.key}/commits/{sha}"
608 redef init from_json
(api
, repo
, json
) do
609 self.sha
= json
["sha"].to_s
613 # Parent commits of `self`.
614 fun parents
: Array[Commit] do
615 var res
= new Array[Commit]
616 var parents
= json
["parents"]
617 if not parents
isa JsonArray then return res
618 for obj
in parents
do
619 if not obj
isa JsonObject then continue
620 res
.add
(api
.load_commit
(repo
, obj
["sha"].to_s
).as(not null))
625 # Author of the commit.
626 fun author
: nullable User do
627 if not json
.has_key
("author") then return null
628 var user
= json
["author"]
629 if not user
isa JsonObject then return null
630 return new User.from_json
(api
, user
)
633 # Committer of the commit.
634 fun committer
: nullable User do
635 if not json
.has_key
("committer") then return null
636 var user
= json
["author"]
637 if not user
isa JsonObject then return null
638 return new User.from_json
(api
, user
)
641 # Authoring date as ISODate.
642 fun author_date
: ISODate do
643 var commit
= json
["commit"].as(JsonObject)
644 var author
= commit
["author"].as(JsonObject)
645 return new ISODate.from_string
(author
["date"].to_s
)
648 # Commit date as ISODate.
649 fun commit_date
: ISODate do
650 var commit
= json
["commit"].as(JsonObject)
651 var author
= commit
["committer"].as(JsonObject)
652 return new ISODate.from_string
(author
["date"].to_s
)
655 # List files staged in this commit.
656 fun files
: Array[GithubFile] do
657 var res
= new Array[GithubFile]
658 var files
= json
["files"]
659 if not files
isa JsonArray then return res
661 res
.add
(new GithubFile(obj
.as(JsonObject)))
667 fun message
: String do return json
["commit"].as(JsonObject)["message"].to_s
672 # Should be accessed from `GithubAPI::load_issue`.
674 # See <https://developer.github.com/v3/issues/>.
678 redef var key
is lazy
do return "{repo.key}/issues/{number}"
683 redef init from_json
(api
, repo
, json
) do
684 self.number
= json
["number"].as(Int)
689 fun title
: String do return json
["title"].to_s
691 # User that created this issue.
693 return new User.from_json
(api
, json
["user"].as(JsonObject))
696 # List of labels on this issue associated to their names.
697 fun labels
: Map[String, Label] do
698 var res
= new HashMap[String, Label]
699 if not json
.has_key
("labels") then return res
700 for obj
in json
["labels"].as(JsonArray) do
701 if not obj
isa JsonObject then continue
702 var name
= obj
["name"].to_s
703 res
[name
] = new Label.from_json
(api
, repo
, obj
)
708 # State of the issue on Github.
709 fun state
: String do return json
["state"].to_s
711 # Is the issue locked?
712 fun locked
: Bool do return json
["locked"].as(Bool)
714 # Assigned `User` (if any).
715 fun assignee
: nullable User do
716 var assignee
= json
["assignee"]
717 if not assignee
isa JsonObject then return null
718 return new User.from_json
(api
, assignee
)
721 # `Milestone` (if any).
722 fun milestone
: nullable Milestone do
723 var milestone
= json
["milestone"]
724 if not milestone
isa JsonObject then return null
725 return new Milestone.from_json
(api
, repo
, milestone
)
728 # List of comments made on this issue.
729 fun comments
: Array[IssueComment] do
730 var res
= new Array[IssueComment]
731 var count
= comments_count
733 var array
= api
.get
("{key}/comments?page={page}")
734 if not array
isa JsonArray then
737 while not array
.is_empty
and res
.length
< count
do
739 if not obj
isa JsonObject then continue
740 var id
= obj
["id"].as(Int)
741 res
.add
(api
.load_issue_comment
(repo
, id
).as(not null))
744 array
= api
.get
("{key}/comments?page={page}").as(JsonArray)
749 # Number of comments on this issue.
750 fun comments_count
: Int do return json
["comments"].to_s
.to_i
752 # Creation time in ISODate format.
753 fun created_at
: ISODate do
754 return new ISODate.from_string
(json
["created_at"].to_s
)
757 # Last update time in ISODate format (if any).
758 fun updated_at
: nullable ISODate do
759 var res
= json
["updated_at"]
760 if res
== null then return null
761 return new ISODate.from_string
(res
.to_s
)
764 # Close time in ISODate format (if any).
765 fun closed_at
: nullable ISODate do
766 var res
= json
["closed_at"]
767 if res
== null then return null
768 return new ISODate.from_string
(res
.to_s
)
771 # TODO link to pull request
773 # Full description of the issue.
774 fun body
: String do return json
["body"].to_s
776 # List of events on this issue.
777 fun events
: Array[IssueEvent] do
778 var res
= new Array[IssueEvent]
780 var array
= api
.get
("{key}/events?page={page}").as(JsonArray)
781 while not array
.is_empty
do
783 if not obj
isa JsonObject then continue
784 res
.add
new IssueEvent.from_json
(api
, repo
, obj
)
787 array
= api
.get
("{key}/events?page={page}").as(JsonArray)
792 # User that closed this issue (if any).
793 fun closed_by
: nullable User do
794 var closer
= json
["closed_by"]
795 if not closer
isa JsonObject then return null
796 return new User.from_json
(api
, closer
)
800 # A Github pull request.
802 # Should be accessed from `GithubAPI::load_pull`.
804 # PullRequest are basically Issues with more data.
805 # See <https://developer.github.com/v3/pulls/>.
809 redef var key
is lazy
do return "{repo.key}/pulls/{number}"
811 # Merge time in ISODate format (if any).
812 fun merged_at
: nullable ISODate do
813 var res
= json
["merged_at"]
814 if res
== null then return null
815 return new ISODate.from_string
(res
.to_s
)
819 fun merge_commit_sha
: String do return json
["merge_commit_sha"].to_s
821 # Count of comments made on the pull request diff.
822 fun review_comments
: Int do return json
["review_comments"].to_s
.to_i
824 # Pull request head (can be a commit SHA or a branch name).
826 var json
= json
["head"].as(JsonObject)
827 return new PullRef(api
, json
)
830 # Pull request base (can be a commit SHA or a branch name).
832 var json
= json
["base"].as(JsonObject)
833 return new PullRef(api
, json
)
836 # Is this pull request merged?
837 fun merged
: Bool do return json
["merged"].as(Bool)
839 # Is this pull request mergeable?
840 fun mergeable
: Bool do return json
["mergeable"].as(Bool)
842 # Mergeable state of this pull request.
844 # See <https://developer.github.com/v3/pulls/#list-pull-requests>.
845 fun mergeable_state
: Int do return json
["mergeable_state"].to_s
.to_i
847 # User that merged this pull request (if any).
848 fun merged_by
: nullable User do
849 var merger
= json
["merged_by"]
850 if not merger
isa JsonObject then return null
851 return new User.from_json
(api
, merger
)
854 # Count of commits in this pull request.
855 fun commits
: Int do return json
["commits"].to_s
.to_i
858 fun additions
: Int do return json
["additions"].to_s
.to_i
860 # Deleted line count.
861 fun deletions
: Int do return json
["deletions"].to_s
.to_i
863 # Changed files count.
864 fun changed_files
: Int do return json
["changed_files"].to_s
.to_i
867 # A pull request reference (used for head and base).
870 # Api instance that maintains self.
873 # JSON representation.
876 # Label pointed by `self`.
877 fun labl
: String do return json
["label"].to_s
879 # Reference pointed by `self`.
880 fun ref
: String do return json
["ref"].to_s
882 # Commit SHA pointed by `self`.
883 fun sha
: String do return json
["sha"].to_s
885 # User pointed by `self`.
887 return new User.from_json
(api
, json
["user"].as(JsonObject))
890 # Repo pointed by `self`.
892 return new Repo.from_json
(api
, json
["repo"].as(JsonObject))
898 # Should be accessed from `GithubAPI::load_label`.
900 # See <https://developer.github.com/v3/issues/labels/>.
904 redef var key
is lazy
do return "{repo.key}/labels/{name}"
909 redef init from_json
(api
, repo
, json
) do
910 self.name
= json
["name"].to_s
915 fun color
: String do return json
["color"].to_s
918 # A Github milestone.
920 # Should be accessed from `GithubAPI::load_milestone`.
922 # See <https://developer.github.com/v3/issues/milestones/>.
926 redef var key
is lazy
do return "{repo.key}/milestones/{number}"
928 # The milestone id on Github.
931 redef init from_json
(api
, repo
, json
) do
933 self.number
= json
["number"].as(Int)
937 fun title
: String do return json
["title"].to_s
939 # Milestone long description.
940 fun description
: String do return json
["description"].to_s
942 # Count of opened issues linked to this milestone.
943 fun open_issues
: Int do return json
["open_issues"].to_s
.to_i
945 # Count of closed issues linked to this milestone.
946 fun closed_issues
: Int do return json
["closed_issues"].to_s
.to_i
949 fun state
: String do return json
["state"].to_s
951 # Creation time in ISODate format.
952 fun created_at
: ISODate do
953 return new ISODate.from_string
(json
["created_at"].to_s
)
956 # User that created this milestone.
958 return new User.from_json
(api
, json
["creator"].as(JsonObject))
961 # Due time in ISODate format (if any).
962 fun due_on
: nullable ISODate do
963 var res
= json
["updated_at"]
964 if res
== null then return null
965 return new ISODate.from_string
(res
.to_s
)
968 # Update time in ISODate format (if any).
969 fun updated_at
: nullable ISODate do
970 var res
= json
["updated_at"]
971 if res
== null then return null
972 return new ISODate.from_string
(res
.to_s
)
975 # Close time in ISODate format (if any).
976 fun closed_at
: nullable ISODate do
977 var res
= json
["closed_at"]
978 if res
== null then return null
979 return new ISODate.from_string
(res
.to_s
)
985 # There is two kinds of comments:
987 # * `CommitComment` are made on a commit page.
988 # * `IssueComment` are made on an issue or pull request page.
989 # * `ReviewComment` are made on the diff associated to a pull request.
990 abstract class Comment
993 # Identifier of this comment.
996 redef init from_json
(api
, repo
, json
) do
997 self.id
= json
["id"].as(Int)
1001 # User that made this comment.
1003 return new User.from_json
(api
, json
["user"].as(JsonObject))
1006 # Creation time in ISODate format.
1007 fun created_at
: ISODate do
1008 return new ISODate.from_string
(json
["created_at"].to_s
)
1011 # Last update time in ISODate format (if any).
1012 fun updated_at
: nullable ISODate do
1013 if not json
.has_key
("updated_at") then return null
1014 return new ISODate.from_string
(json
["updated_at"].to_s
)
1017 # Comment body text.
1018 fun body
: String do return json
["body"].to_s
1020 # Does the comment contain an acknowledgement (+1)
1023 return body
.has
("\\+1\\b".to_re
) or body
.has
(":+1:") or body
.has
(":shipit:")
1027 # A comment made on a commit.
1031 redef var key
is lazy
do return "{repo.key}/comments/{id}"
1034 fun commit
: Commit do
1035 return api
.load_commit
(repo
, json
["commit_id"].to_s
).as(not null)
1038 # Position of the comment on the line.
1039 fun position
: nullable String do
1040 if not json
.has_key
("position") then return null
1041 var res
= json
["position"]
1042 if res
== null then return null
1046 # Line of the comment.
1047 fun line
: nullable String do
1048 if not json
.has_key
("line") then return null
1049 var res
= json
["line"]
1050 if res
== null then return null
1054 # Path of the commented file.
1055 fun path
: String do return json
["path"].to_s
1058 # Comments made on Github issue and pull request pages.
1060 # Should be accessed from `GithubAPI::load_issue_comment`.
1062 # See <https://developer.github.com/v3/issues/comments/>.
1066 redef var key
is lazy
do return "{repo.key}/issues/comments/{id}"
1068 # Issue that contains `self`.
1070 var number
= issue_url
.split
("/").last
.to_i
1071 return api
.load_issue
(repo
, number
).as(not null)
1074 # Link to the issue document on API.
1075 fun issue_url
: String do return json
["issue_url"].to_s
1078 # Comments made on Github pull request diffs.
1080 # Should be accessed from `GithubAPI::load_diff_comment`.
1082 # See <https://developer.github.com/v3/pulls/comments/>.
1086 redef var key
is lazy
do return "{repo.key}/pulls/comments/{id}"
1088 # Pull request that contains `self`.
1089 fun pull
: PullRequest do
1090 var number
= pull_request_url
.split
("/").last
.to_i
1091 return api
.load_pull
(repo
, number
).as(not null)
1094 # Link to the pull request on API.
1095 fun pull_request_url
: String do return json
["pull_request_url"].to_s
1098 fun diff_hunk
: String do return json
["diff_hunk"].to_s
1100 # Path of commented file.
1101 fun path
: String do return json
["path"].to_s
1103 # Position of the comment on the file.
1104 fun position
: Int do return json
["position"].to_s
.to_i
1106 # Original position in the diff.
1107 fun original_position
: Int do return json
["original_position"].to_s
.to_i
1109 # Commit referenced by this comment.
1110 fun commit_id
: String do return json
["commit_id"].to_s
1112 # Original commit id.
1113 fun original_commit_id
: String do return json
["original_commit_id"].to_s
1116 # An event that occurs on a Github `Issue`.
1118 # Should be accessed from `GithubAPI::load_issue_event`.
1120 # See <https://developer.github.com/v3/issues/events/>.
1124 redef var key
is lazy
do return "{repo.key}/issues/events/{id}"
1126 # Event id on Github.
1129 redef init from_json
(api
, repo
, json
) do
1130 self.id
= json
["id"].as(Int)
1134 # Issue that contains `self`.
1136 return new Issue.from_json
(api
, repo
, json
["issue"].as(JsonObject))
1139 # User that initiated the event.
1141 return new User.from_json
(api
, json
["actor"].as(JsonObject))
1144 # Creation time in ISODate format.
1145 fun created_at
: ISODate do
1146 return new ISODate.from_string
(json
["created_at"].to_s
)
1150 fun event
: String do return json
["event"].to_s
1152 # Commit linked to this event (if any).
1153 fun commit_id
: nullable String do
1154 var res
= json
["commit_id"]
1155 if res
== null then return null
1159 # Label linked to this event (if any).
1160 fun labl
: nullable Label do
1161 var res
= json
["label"]
1162 if not res
isa JsonObject then return null
1163 return new Label.from_json
(api
, repo
, res
)
1166 # User linked to this event (if any).
1167 fun assignee
: nullable User do
1168 var res
= json
["assignee"]
1169 if not res
isa JsonObject then return null
1170 return new User.from_json
(api
, res
)
1173 # Milestone linked to this event (if any).
1174 fun milestone
: nullable Milestone do
1175 var res
= json
["milestone"]
1176 if not res
isa JsonObject then return null
1177 return new Milestone.from_json
(api
, repo
, res
)
1180 # Rename linked to this event (if any).
1181 fun rename
: nullable RenameAction do
1182 var res
= json
["rename"]
1183 if res
== null then return null
1184 return new RenameAction(res
.as(JsonObject))
1188 # A rename action maintains the name before and after a renaming action.
1192 var json
: JsonObject
1194 # Name before renaming.
1195 fun from
: String do return json
["from"].to_s
1197 # Name after renaming.
1198 fun to
: String do return json
["to"].to_s
1201 # Contributors list with additions, deletions, and commit counts.
1203 # Should be accessed from `Repo::contrib_stats`.
1205 # See <https://developer.github.com/v3/repos/statistics/>.
1206 class ContributorStats
1209 redef type OTHER: ContributorStats
1211 # Github API client.
1215 var json
: JsonObject
1217 # Init `self` from a `json` object.
1218 init from_json
(api
: GithubAPI, json
: JsonObject) do
1223 # User these statistics are about.
1225 return new User.from_json
(api
, json
["author"].as(JsonObject))
1228 # Total number of commit.
1229 fun total
: Int do return json
["total"].to_s
.to_i
1231 # Are of weeks of activity with detailed statistics.
1232 fun weeks
: JsonArray do return json
["weeks"].as(JsonArray)
1234 # ContributorStats can be compared on the total amount of commits.
1235 redef fun <(o
) do return total
< o
.total
1238 # A Github file representation.
1240 # Mostly a wrapper around a json object.
1244 var json
: JsonObject
1247 fun filename
: String do return json
["filename"].to_s