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](https://developer.github.com/v3/).
17 # This modules reifies Github API elements as Nit classes.
19 # For most use-cases you need to use the `GithubAPI` client.
24 # Client to Github API
26 # To access the API you need an instance of a `GithubAPI` client.
29 # # Get Github authentification token.
30 # var token = get_github_oauth
31 # assert not token.is_empty
34 # var api = new GithubAPI(token)
37 # The API client allows you to get Github API entities.
40 # var repo = api.load_repo("nitlang/nit")
42 # assert repo.name == "nit"
44 # var user = api.load_user("Morriar")
46 # assert user.login == "Morriar"
50 # Github API OAuth token
52 # To access your private ressources, you must
53 # [authenticate](https://developer.github.com/guides/basics-of-authentication/).
55 # For client applications, Github recommands to use the
56 # [OAuth tokens](https://developer.github.com/v3/oauth/) authentification method.
60 # Be aware that there is [rate limits](https://developer.github.com/v3/rate_limit/)
61 # associated to the key.
64 # Github API base url.
66 # Default is `https://api.github.com` and should not be changed.
67 var api_url
= "https://api.github.com"
69 # User agent used for HTTP requests.
71 # Default is `nit_github_api`.
73 # See <https://developer.github.com/v3/#user-agent-required>
74 var user_agent
= "nit_github_api"
78 # Internal Curl instance used to perform API calls.
79 private var ghcurl
: GithubCurl is noinit
83 # * `0`: only errors (default)
85 var verbose_lvl
= 0 is public
writable
88 ghcurl
= new GithubCurl(auth
, user_agent
)
91 # Execute a GET request on Github API.
93 # This method returns raw json data.
94 # See other `load_*` methods to use more expressive types.
96 # var api = new GithubAPI(get_github_oauth)
97 # var obj = api.get("repos/nitlang/nit")
98 # assert obj isa JsonObject
99 # assert obj["name"] == "nit"
101 # Returns `null` in case of `error`.
103 # obj = api.get("foo/bar/baz")
105 # assert api.was_error
106 # var err = api.last_error
107 # assert err isa GithubError
108 # assert err.name == "GithubAPIError"
109 # assert err.message == "Not Found"
110 fun get
(path
: String): nullable Jsonable do
111 path
= sanitize_uri
(path
)
112 var res
= ghcurl
.get_and_parse
("{api_url}/{path}")
113 if res
isa Error then
122 # Display a message depending on `verbose_lvl`.
123 fun message
(lvl
: Int, message
: String) do
124 if lvl
<= verbose_lvl
then print message
127 # Escape `uri` in an acceptable format for Github.
128 private fun sanitize_uri
(uri
: String): String do
129 # TODO better URI escape.
130 return uri
.replace
(" ", "%20")
133 # Last error occured during Github API communications.
134 var last_error
: nullable Error = null is public
writable
136 # Does the last request provoqued an error?
137 var was_error
= false is protected writable
139 # Load the json object from Github.
140 # See `GithubEntity::load_from_github`.
141 protected fun load_from_github
(key
: String): JsonObject do
142 message
(1, "Get {key} (github)")
144 if was_error
then return new JsonObject
145 return res
.as(JsonObject)
148 # Get the Github logged user from `auth` token.
150 # Loads the `User` from the API or returns `null` if the user cannot be found.
153 # var api = new GithubAPI(get_github_oauth)
154 # var user = api.load_auth_user
155 # assert user.login == "Morriar"
157 fun load_auth_user
: nullable User do
158 var json
= load_from_github
("user")
159 if was_error
then return null
160 return new User.from_json
(self, json
)
163 # Get the Github user with `login`
165 # Loads the `User` from the API or returns `null` if the user cannot be found.
167 # var api = new GithubAPI(get_github_oauth)
168 # var user = api.load_user("Morriar")
169 # assert user.login == "Morriar"
170 fun load_user
(login
: String): nullable User do
171 var user
= new User(self, login
)
172 return user
.load_from_github
175 # Get the Github repo with `full_name`.
177 # Loads the `Repo` from the API or returns `null` if the repo cannot be found.
179 # var api = new GithubAPI(get_github_oauth)
180 # var repo = api.load_repo("nitlang/nit")
181 # assert repo.name == "nit"
182 # assert repo.owner.login == "nitlang"
183 # assert repo.default_branch.name == "master"
184 fun load_repo
(full_name
: String): nullable Repo do
185 var repo
= new Repo(self, full_name
)
186 return repo
.load_from_github
189 # Get the Github branch with `name`.
191 # Returns `null` if the branch cannot be found.
193 # var api = new GithubAPI(get_github_oauth)
194 # var repo = api.load_repo("nitlang/nit")
195 # assert repo != null
196 # var branch = api.load_branch(repo, "master")
197 # assert branch.name == "master"
198 # assert branch.commit isa Commit
199 fun load_branch
(repo
: Repo, name
: String): nullable Branch do
200 var branch
= new Branch(self, repo
, name
)
201 return branch
.load_from_github
204 # Get the Github commit with `sha`.
206 # Returns `null` if the commit cannot be found.
208 # var api = new GithubAPI(get_github_oauth)
209 # var repo = api.load_repo("nitlang/nit")
210 # assert repo != null
211 # var commit = api.load_commit(repo, "64ce1f")
212 # assert commit isa Commit
213 fun load_commit
(repo
: Repo, sha
: String): nullable Commit do
214 var commit
= new Commit(self, repo
, sha
)
215 return commit
.load_from_github
218 # Get the Github issue #`number`.
220 # Returns `null` if the issue cannot be found.
222 # var api = new GithubAPI(get_github_oauth)
223 # var repo = api.load_repo("nitlang/nit")
224 # assert repo != null
225 # var issue = api.load_issue(repo, 1)
226 # assert issue.title == "Doc"
227 fun load_issue
(repo
: Repo, number
: Int): nullable Issue do
228 var issue
= new Issue(self, repo
, number
)
229 return issue
.load_from_github
232 # Get the Github pull request #`number`.
234 # Returns `null` if the pull request cannot be found.
236 # var api = new GithubAPI(get_github_oauth)
237 # var repo = api.load_repo("nitlang/nit")
238 # assert repo != null
239 # var pull = api.load_pull(repo, 1)
240 # assert pull.title == "Doc"
241 # assert pull.user.login == "Morriar"
242 fun load_pull
(repo
: Repo, number
: Int): nullable PullRequest do
243 var pull
= new PullRequest(self, repo
, number
)
244 return pull
.load_from_github
247 # Get the Github label with `name`.
249 # Returns `null` if the label cannot be found.
251 # var api = new GithubAPI(get_github_oauth)
252 # var repo = api.load_repo("nitlang/nit")
253 # assert repo != null
254 # var labl = api.load_label(repo, "ok_will_merge")
255 # assert labl != null
256 fun load_label
(repo
: Repo, name
: String): nullable Label do
257 var labl
= new Label(self, repo
, name
)
258 return labl
.load_from_github
261 # Get the Github milestone with `id`.
263 # Returns `null` if the milestone cannot be found.
265 # var api = new GithubAPI(get_github_oauth)
266 # var repo = api.load_repo("nitlang/nit")
267 # assert repo != null
268 # var stone = api.load_milestone(repo, 4)
269 # assert stone.title == "v1.0prealpha"
270 fun load_milestone
(repo
: Repo, id
: Int): nullable Milestone do
271 var milestone
= new Milestone(self, repo
, id
)
272 return milestone
.load_from_github
275 # Get the Github issue event with `id`.
277 # Returns `null` if the event cannot be found.
279 # var api = new GithubAPI(get_github_oauth)
280 # var repo = api.load_repo("nitlang/nit")
281 # assert repo isa Repo
282 # var event = api.load_issue_event(repo, 199674194)
283 # assert event.actor.login == "privat"
284 # assert event.event == "labeled"
285 # assert event.labl.name == "need_review"
286 # assert event.issue.number == 945
287 fun load_issue_event
(repo
: Repo, id
: Int): nullable IssueEvent do
288 var event
= new IssueEvent(self, repo
, id
)
289 return event
.load_from_github
292 # Get the Github commit comment with `id`.
294 # Returns `null` if the comment cannot be found.
296 # var api = new GithubAPI(get_github_oauth)
297 # var repo = api.load_repo("nitlang/nit")
298 # assert repo != null
299 # var comment = api.load_commit_comment(repo, 8982707)
300 # assert comment.user.login == "Morriar"
301 # assert comment.body == "For testing purposes..."
302 # assert comment.commit.sha == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
303 fun load_commit_comment
(repo
: Repo, id
: Int): nullable CommitComment do
304 var comment
= new CommitComment(self, repo
, id
)
305 return comment
.load_from_github
308 # Get the Github issue comment with `id`.
310 # Returns `null` if the comment cannot be found.
312 # var api = new GithubAPI(get_github_oauth)
313 # var repo = api.load_repo("nitlang/nit")
314 # assert repo != null
315 # var comment = api.load_issue_comment(repo, 6020149)
316 # assert comment.user.login == "privat"
317 # assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
318 # assert comment.issue.number == 10
319 fun load_issue_comment
(repo
: Repo, id
: Int): nullable IssueComment do
320 var comment
= new IssueComment(self, repo
, id
)
321 return comment
.load_from_github
324 # Get the Github diff comment with `id`.
326 # Returns `null` if the comment cannot be found.
328 # var api = new GithubAPI(get_github_oauth)
329 # var repo = api.load_repo("nitlang/nit")
330 # assert repo != null
331 # var comment = api.load_review_comment(repo, 21010363)
332 # assert comment.path == "src/modelize/modelize_property.nit"
333 # assert comment.original_position == 26
334 # assert comment.pull.number == 945
335 fun load_review_comment
(repo
: Repo, id
: Int): nullable ReviewComment do
336 var comment
= new ReviewComment(self, repo
, id
)
337 return comment
.load_from_github
341 # Something returned by the Github API.
343 # Mainly a Nit wrapper around a JSON objet.
344 abstract class GithubEntity
346 # Github API instance.
349 # FIXME constructor should be private
351 # Key used to access this entity from Github api base.
352 fun key
: String is abstract
354 # JSON representation of `self`.
356 # This is the same json structure than used by Github API.
357 var json
: JsonObject is noinit
, protected writable
359 # Load `json` from Github API.
360 private fun load_from_github
: nullable SELF do
361 json
= api
.load_from_github
(key
)
362 if api
.was_error
then return null
366 redef fun to_s
do return json
.to_json
369 fun html_url
: String do return json
["html_url"].as(String)
372 fun html_url
=(url
: String) do json
["html_url"] = url
377 # Provides access to [Github user data](https://developer.github.com/v3/users/).
378 # Should be accessed from `GithubAPI::load_user`.
382 redef var key
is lazy
do return "users/{login}"
387 # Init `self` from a `json` object.
388 init from_json
(api
: GithubAPI, json
: JsonObject) do
389 init(api
, json
["login"].as(String))
393 # Avatar image url for this user.
394 fun avatar_url
: String do return json
["avatar_url"].as(String)
397 fun avatar_url
=(url
: String) do json
["avatar_url"] = url
400 # A Github repository.
402 # Provides access to [Github repo data](https://developer.github.com/v3/repos/).
403 # Should be accessed from `GithubAPI::load_repo`.
407 redef var key
is lazy
do return "repos/{full_name}"
409 # Repo full name on Github.
410 var full_name
: String
412 # Init `self` from a `json` object.
413 init from_json
(api
: GithubAPI, json
: JsonObject) do
414 init(api
, json
["full_name"].as(String))
418 # Repo short name on Github.
419 fun name
: String do return json
["name"].as(String)
422 fun name
=(name
: String) do json
["name"] = name
424 # Get the repo owner.
425 fun owner
: User do return new User.from_json
(api
, json
["owner"].as(JsonObject))
428 fun owner
=(owner
: User) do json
["owner"] = owner
.json
430 # List of branches associated with their names.
431 fun branches
: Map[String, Branch] do
432 api
.message
(1, "Get branches for {full_name}")
433 var array
= api
.get
("repos/{full_name}/branches")
434 var res
= new HashMap[String, Branch]
435 if not array
isa JsonArray then return res
437 if not obj
isa JsonObject then continue
438 var name
= obj
["name"].as(String)
439 res
[name
] = new Branch.from_json
(api
, self, obj
)
444 # List of issues associated with their ids.
445 fun issues
: Map[Int, Issue] do
446 api
.message
(1, "Get issues for {full_name}")
447 var res
= new HashMap[Int, Issue]
448 var issue
= last_issue
449 if issue
== null then return res
450 res
[issue
.number
] = issue
451 while issue
.number
> 1 do
452 issue
= api
.load_issue
(self, issue
.number
- 1)
453 assert issue
isa Issue
454 res
[issue
.number
] = issue
459 # Search issues in this repo form an advanced query.
464 # var issues = repo.search_issues("is:open label:need_review")
467 # See <https://developer.github.com/v3/search/#search-issues>.
468 fun search_issues
(query
: String): nullable Array[Issue] do
469 query
= "search/issues?q={query} repo:{full_name}"
470 var response
= api
.get
(query
)
471 if api
.was_error
then return null
472 var arr
= response
.as(JsonObject)["items"].as(JsonArray)
473 var res
= new Array[Issue]
475 res
.add
new Issue.from_json
(api
, self, obj
.as(JsonObject))
480 # Get the last published issue.
481 fun last_issue
: nullable Issue do
482 var array
= api
.get
("repos/{full_name}/issues")
483 if not array
isa JsonArray then return null
484 if array
.is_empty
then return null
485 var obj
= array
.first
486 if not obj
isa JsonObject then return null
487 return new Issue.from_json
(api
, self, obj
)
490 # List of labels associated with their names.
491 fun labels
: Map[String, Label] do
492 api
.message
(1, "Get labels for {full_name}")
493 var array
= api
.get
("repos/{full_name}/labels")
494 var res
= new HashMap[String, Label]
495 if not array
isa JsonArray then return res
497 if not obj
isa JsonObject then continue
498 var name
= obj
["name"].as(String)
499 res
[name
] = new Label.from_json
(api
, self, obj
)
504 # List of milestones associated with their ids.
505 fun milestones
: Map[Int, Milestone] do
506 api
.message
(1, "Get milestones for {full_name}")
507 var array
= api
.get
("repos/{full_name}/milestones")
508 var res
= new HashMap[Int, Milestone]
509 if array
isa JsonArray then
511 if not obj
isa JsonObject then continue
512 var number
= obj
["number"].as(Int)
513 res
[number
] = new Milestone.from_json
(api
, self, obj
)
519 # List of pull-requests associated with their ids.
521 # Implementation notes: because PR numbers are not consecutive,
522 # PR are loaded from pages.
523 # See: https://developer.github.com/v3/pulls/#list-pull-requests
524 fun pulls
: Map[Int, PullRequest] do
525 api
.message
(1, "Get pulls for {full_name}")
526 var res
= new HashMap[Int, PullRequest]
528 var array
= api
.get
("{key}/pulls?page={page}").as(JsonArray)
529 while not array
.is_empty
do
531 if not obj
isa JsonObject then continue
532 var number
= obj
["number"].as(Int)
533 res
[number
] = new PullRequest.from_json
(api
, self, obj
)
536 array
= api
.get
("{key}/pulls?page={page}").as(JsonArray)
541 # List of contributor related statistics.
542 fun contrib_stats
: Array[ContributorStats] do
543 api
.message
(1, "Get contributor stats for {full_name}")
544 var res
= new Array[ContributorStats]
545 var array
= api
.get
("{key}/stats/contributors")
546 if array
isa JsonArray then
548 res
.add
new ContributorStats.from_json
(api
, obj
.as(JsonObject))
554 # Repo default branch.
555 fun default_branch
: Branch do
556 var name
= json
["default_branch"].as(String)
557 var branch
= api
.load_branch
(self, name
)
558 assert branch
isa Branch
562 # Set the default branch
563 fun default_branch
=(branch
: Branch) do json
["default_branch"] = branch
.json
566 # A `RepoEntity` is something contained in a `Repo`.
567 abstract class RepoEntity
570 # Repo that contains `self`.
573 # Init `self` from a `json` object.
574 init from_json
(api
: GithubAPI, repo
: Repo, json
: JsonObject) do
582 # Should be accessed from `GithubAPI::load_branch`.
584 # See <https://developer.github.com/v3/repos/#list-branches>.
588 redef var key
is lazy
do return "{repo.key}/branches/{name}"
593 redef init from_json
(api
, repo
, json
) do
594 self.name
= json
["name"].as(String)
598 # Get the last commit of `self`.
599 fun commit
: Commit do return new Commit.from_json
(api
, repo
, json
["commit"].as(JsonObject))
601 # Set the last commit
602 fun commit
=(commit
: Commit) do json
["commit"] = commit
.json
604 # List all commits in `self`.
606 # This can be long depending on the branch size.
607 # Commit are returned in an unspecified order.
608 fun commits
: Array[Commit] do
609 var res
= new Array[Commit]
610 var done
= new HashSet[String]
611 var todos
= new Array[Commit]
613 while not todos
.is_empty
do
614 var commit
= todos
.pop
615 if done
.has
(commit
.sha
) then continue
618 for parent
in commit
.parents
do
628 # Should be accessed from `GithubAPI::load_commit`.
630 # See <https://developer.github.com/v3/repos/commits/>.
634 redef var key
is lazy
do return "{repo.key}/commits/{sha}"
639 redef init from_json
(api
, repo
, json
) do
640 self.sha
= json
["sha"].as(String)
644 # Parent commits of `self`.
645 fun parents
: Array[Commit] do
646 var res
= new Array[Commit]
647 var parents
= json
.get_or_null
("parents")
648 if not parents
isa JsonArray then return res
649 for obj
in parents
do
650 if not obj
isa JsonObject then continue
651 res
.add
(api
.load_commit
(repo
, obj
["sha"].as(String)).as(not null))
656 # Set parent commits.
657 fun parents
=(parents
: Array[Commit]) do
658 var res
= new JsonArray
659 for parent
in parents
do res
.add parent
.json
660 json
["parents"] = res
663 # Author of the commit.
664 fun author
: nullable User do
665 var user
= json
.get_or_null
("author")
666 if user
isa JsonObject then return new User.from_json
(api
, user
)
671 fun author
=(user
: nullable User) do
673 json
["author"] = null
675 json
["author"] = user
.json
679 # Committer of the commit.
680 fun committer
: nullable User do
681 var user
= json
.get_or_null
("author")
682 if user
isa JsonObject then return new User.from_json
(api
, user
)
686 # Set commit committer.
687 fun committer
=(user
: nullable User) do
689 json
["committer"] = null
691 json
["committer"] = user
.json
695 # Authoring date as ISODate.
696 fun author_date
: ISODate do
697 var commit
= json
["commit"].as(JsonObject)
698 var author
= commit
["author"].as(JsonObject)
699 return new ISODate.from_string
(author
["date"].as(String))
702 # Commit date as ISODate.
703 fun commit_date
: ISODate do
704 var commit
= json
["commit"].as(JsonObject)
705 var author
= commit
["committer"].as(JsonObject)
706 return new ISODate.from_string
(author
["date"].as(String))
709 # List files staged in this commit.
710 fun files
: Array[GithubFile] do
711 var res
= new Array[GithubFile]
712 var files
= json
.get_or_null
("files")
713 if not files
isa JsonArray then return res
715 res
.add
(new GithubFile(obj
.as(JsonObject)))
721 fun files
=(files
: Array[GithubFile]) do
722 var res
= new JsonArray
723 for file
in files
do res
.add file
.json
728 fun message
: String do return json
["commit"].as(JsonObject)["message"].as(String)
733 # Should be accessed from `GithubAPI::load_issue`.
735 # See <https://developer.github.com/v3/issues/>.
739 redef var key
is lazy
do return "{repo.key}/issues/{number}"
744 redef init from_json
(api
, repo
, json
) do
745 self.number
= json
["number"].as(Int)
750 fun id
: Int do return json
["id"].as(Int)
753 fun id
=(id
: Int) do json
["id"] = id
756 fun title
: String do return json
["title"].as(String)
759 fun title
=(title
: String) do json
["title"] = title
761 # User that created this issue.
762 fun user
: User do return new User.from_json
(api
, json
["user"].as(JsonObject))
765 fun user
=(user
: User) do json
["user"] = user
.json
767 # List of labels on this issue associated to their names.
768 fun labels
: Map[String, Label] do
769 var res
= new HashMap[String, Label]
770 var lbls
= json
.get_or_null
("labels")
771 if not lbls
isa JsonArray then return res
773 if not obj
isa JsonObject then continue
774 var name
= obj
["name"].as(String)
775 res
[name
] = new Label.from_json
(api
, repo
, obj
)
780 # State of the issue on Github.
781 fun state
: String do return json
["state"].as(String)
783 # Set the state of this issue.
784 fun state
=(state
: String) do json
["state"] = state
786 # Is the issue locked?
787 fun locked
: Bool do return json
["locked"].as(Bool)
789 # Set issue locked state.
790 fun locked
=(locked
: Bool) do json
["locked"] = locked
792 # Assigned `User` (if any).
793 fun assignee
: nullable User do
794 var assignee
= json
.get_or_null
("assignee")
795 if assignee
isa JsonObject then return new User.from_json
(api
, assignee
)
799 # Set issue assignee.
800 fun assignee
=(user
: nullable User) do
802 json
["assignee"] = null
804 json
["assignee"] = user
.json
808 # `Milestone` (if any).
809 fun milestone
: nullable Milestone do
810 var milestone
= json
.get_or_null
("milestone")
811 if milestone
isa JsonObject then return new Milestone.from_json
(api
, repo
, milestone
)
815 # Set issue milestone.
816 fun milestone
=(milestone
: nullable Milestone) do
817 if milestone
== null then
818 json
["milestone"] = null
820 json
["milestone"] = milestone
.json
824 # List of comments made on this issue.
825 fun comments
: Array[IssueComment] do
826 var res
= new Array[IssueComment]
827 var count
= comments_count
829 var array
= api
.get
("{key}/comments?page={page}")
830 if not array
isa JsonArray then
833 while not array
.is_empty
and res
.length
< count
do
835 if not obj
isa JsonObject then continue
836 var id
= obj
["id"].as(Int)
837 var comment
= api
.load_issue_comment
(repo
, id
)
838 if comment
== null then continue
842 var json
= api
.get
("{key}/comments?page={page}")
843 if not json
isa JsonArray then
851 # Number of comments on this issue.
852 fun comments_count
: Int do return json
["comments"].as(Int)
854 # Creation time in ISODate format.
855 fun created_at
: ISODate do return new ISODate.from_string
(json
["created_at"].as(String))
857 # Set issue creation time.
858 fun created_at
=(created_at
: nullable ISODate) do
859 if created_at
== null then
860 json
["created_at"] = null
862 json
["created_at"] = created_at
.to_s
866 # Last update time in ISODate format (if any).
867 fun updated_at
: nullable ISODate do
868 var res
= json
.get_or_null
("updated_at")
869 if res
isa String then return new ISODate.from_string
(res
)
873 # Set issue last update time.
874 fun updated_at
=(updated_at
: nullable ISODate) do
875 if updated_at
== null then
876 json
["updated_at"] = null
878 json
["updated_at"] = updated_at
.to_s
882 # Close time in ISODate format (if any).
883 fun closed_at
: nullable ISODate do
884 var res
= json
.get_or_null
("closed_at")
885 if res
isa String then return new ISODate.from_string
(res
)
889 # Set issue close time.
890 fun closed_at
=(closed_at
: nullable ISODate) do
891 if closed_at
== null then
892 json
["closed_at"] = null
894 json
["closed_at"] = closed_at
.to_s
898 # TODO link to pull request
900 # Full description of the issue.
901 fun body
: String do return json
["body"].as(String)
903 # Set description body
904 fun body
=(body
: String) do json
["body"] = body
906 # List of events on this issue.
907 fun events
: Array[IssueEvent] do
908 var res
= new Array[IssueEvent]
910 var array
= api
.get
("{key}/events?page={page}")
911 if not array
isa JsonArray then return res
912 while not array
.is_empty
do
914 if not obj
isa JsonObject then continue
915 res
.add
new IssueEvent.from_json
(api
, repo
, obj
)
918 array
= api
.get
("{key}/events?page={page}").as(JsonArray)
923 # User that closed this issue (if any).
924 fun closed_by
: nullable User do
925 var closer
= json
.get_or_null
("closed_by")
926 if closer
isa JsonObject then return new User.from_json
(api
, closer
)
930 # Set user that closed the issue.
931 fun closed_by
=(user
: nullable User) do
933 json
["closed_by"] = null
935 json
["closed_by"] = user
.json
939 # Is this issue linked to a pull request?
940 fun is_pull_request
: Bool do return json
.has_key
("pull_request")
943 # A Github pull request.
945 # Should be accessed from `GithubAPI::load_pull`.
947 # PullRequest are basically Issues with more data.
948 # See <https://developer.github.com/v3/pulls/>.
952 redef var key
is lazy
do return "{repo.key}/pulls/{number}"
954 # Merge time in ISODate format (if any).
955 fun merged_at
: nullable ISODate do
956 var res
= json
.get_or_null
("merged_at")
957 if res
isa String then return new ISODate.from_string
(res
)
961 # Set pull request merge time.
962 fun merged_at
=(merged_at
: nullable ISODate) do
963 if merged_at
== null then
964 json
["merged_at"] = null
966 json
["merged_at"] = merged_at
.to_s
971 fun merge_commit_sha
: String do return json
["merge_commit_sha"].as(String)
973 # Set merge_commit_sha
974 fun merge_commit_sha
=(sha
: String) do json
["merge_commit_sha"] = sha
976 # Count of comments made on the pull request diff.
977 fun review_comments
: Int do return json
["review_comments"].as(Int)
979 # Set review_comments
980 fun review_comments
=(count
: Int) do json
["review_comments"] = count
982 # Pull request head (can be a commit SHA or a branch name).
984 var json
= json
["head"].as(JsonObject)
985 return new PullRef(api
, json
)
989 fun head
=(head
: PullRef) do json
["head"] = head
.json
991 # Pull request base (can be a commit SHA or a branch name).
993 var json
= json
["base"].as(JsonObject)
994 return new PullRef(api
, json
)
998 fun base
=(base
: PullRef) do json
["base"] = base
.json
1000 # Is this pull request merged?
1001 fun merged
: Bool do return json
["merged"].as(Bool)
1004 fun merged
=(merged
: Bool) do json
["merged"] = merged
1006 # Is this pull request mergeable?
1007 fun mergeable
: Bool do return json
["mergeable"].as(Bool)
1010 fun mergeable
=(mergeable
: Bool) do json
["mergeable"] = mergeable
1012 # Mergeable state of this pull request.
1014 # See <https://developer.github.com/v3/pulls/#list-pull-requests>.
1015 fun mergeable_state
: Int do return json
["mergeable_state"].as(Int)
1017 # Set mergeable_state
1018 fun mergeable_state
=(mergeable_state
: Int) do json
["mergeable_state"] = mergeable_state
1020 # User that merged this pull request (if any).
1021 fun merged_by
: nullable User do
1022 var merger
= json
.get_or_null
("merged_by")
1023 if merger
isa JsonObject then return new User.from_json
(api
, merger
)
1028 fun merged_by
=(merged_by
: nullable User) do
1029 if merged_by
== null then
1030 json
["merged_by"] = null
1032 json
["merged_by"] = merged_by
.json
1036 # Count of commits in this pull request.
1037 fun commits
: Int do return json
["commits"].as(Int)
1040 fun commits
=(commits
: Int) do json
["commits"] = commits
1043 fun additions
: Int do return json
["additions"].as(Int)
1046 fun additions
=(additions
: Int) do json
["additions"] = additions
1048 # Deleted line count.
1049 fun deletions
: Int do return json
["deletions"].as(Int)
1052 fun deletions
=(deletions
: Int) do json
["deletions"] = deletions
1054 # Changed files count.
1055 fun changed_files
: Int do return json
["changed_files"].as(Int)
1058 fun changed_files
=(changed_files
: Int) do json
["changed_files"] = changed_files
1061 # A pull request reference (used for head and base).
1064 # Api instance that maintains self.
1067 # JSON representation.
1068 var json
: JsonObject
1070 # Label pointed by `self`.
1071 fun labl
: String do return json
["label"].as(String)
1074 fun labl
=(labl
: String) do json
["label"] = labl
1076 # Reference pointed by `self`.
1077 fun ref
: String do return json
["ref"].as(String)
1080 fun ref
=(ref
: String) do json
["ref"] = ref
1082 # Commit SHA pointed by `self`.
1083 fun sha
: String do return json
["sha"].as(String)
1086 fun sha
=(sha
: String) do json
["sha"] = sha
1088 # User pointed by `self`.
1090 return new User.from_json
(api
, json
["user"].as(JsonObject))
1094 fun user
=(user
: User) do json
["user"] = user
.json
1096 # Repo pointed by `self`.
1098 return new Repo.from_json
(api
, json
["repo"].as(JsonObject))
1102 fun repo
=(repo
: Repo) do json
["repo"] = repo
.json
1107 # Should be accessed from `GithubAPI::load_label`.
1109 # See <https://developer.github.com/v3/issues/labels/>.
1113 redef var key
is lazy
do return "{repo.key}/labels/{name}"
1118 redef init from_json
(api
, repo
, json
) do
1119 self.name
= json
["name"].as(String)
1124 fun color
: String do return json
["color"].as(String)
1127 fun color
=(color
: String) do json
["color"] = color
1130 # A Github milestone.
1132 # Should be accessed from `GithubAPI::load_milestone`.
1134 # See <https://developer.github.com/v3/issues/milestones/>.
1138 redef var key
is lazy
do return "{repo.key}/milestones/{number}"
1140 # The milestone id on Github.
1143 redef init from_json
(api
, repo
, json
) do
1145 self.number
= json
["number"].as(Int)
1149 fun title
: String do return json
["title"].as(String)
1152 fun title
=(title
: String) do json
["title"] = title
1154 # Milestone long description.
1155 fun description
: String do return json
["description"].as(String)
1158 fun description
=(description
: String) do json
["description"] = description
1160 # Count of opened issues linked to this milestone.
1161 fun open_issues
: Int do return json
["open_issues"].as(Int)
1164 fun open_issues
=(open_issues
: Int) do json
["open_issues"] = open_issues
1166 # Count of closed issues linked to this milestone.
1167 fun closed_issues
: Int do return json
["closed_issues"].as(Int)
1170 fun closed_issues
=(closed_issues
: Int) do json
["closed_issues"] = closed_issues
1173 fun state
: String do return json
["state"].as(String)
1176 fun state
=(state
: String) do json
["state"] = state
1178 # Creation time in ISODate format.
1179 fun created_at
: ISODate do
1180 return new ISODate.from_string
(json
["created_at"].as(String))
1184 fun created_at
=(created_at
: ISODate) do json
["created_at"] = created_at
.to_s
1186 # User that created this milestone.
1187 fun creator
: User do
1188 return new User.from_json
(api
, json
["creator"].as(JsonObject))
1192 fun creator
=(creator
: User) do json
["creator"] = creator
.json
1194 # Due time in ISODate format (if any).
1195 fun due_on
: nullable ISODate do
1196 var res
= json
.get_or_null
("updated_at")
1197 if res
isa String then return new ISODate.from_string
(res
)
1202 fun due_on
=(due_on
: nullable ISODate) do
1203 if due_on
== null then
1204 json
["due_on"] = null
1206 json
["due_on"] = due_on
.to_s
1210 # Update time in ISODate format (if any).
1211 fun updated_at
: nullable ISODate do
1212 var res
= json
.get_or_null
("updated_at")
1213 if res
isa String then return new ISODate.from_string
(res
)
1218 fun updated_at
=(updated_at
: nullable ISODate) do
1219 if updated_at
== null then
1220 json
["updated_at"] = null
1222 json
["updated_at"] = updated_at
.to_s
1226 # Close time in ISODate format (if any).
1227 fun closed_at
: nullable ISODate do
1228 var res
= json
.get_or_null
("closed_at")
1229 if res
isa String then return new ISODate.from_string
(res
)
1234 fun closed_at
=(closed_at
: nullable ISODate) do
1235 if closed_at
== null then
1236 json
["closed_at"] = null
1238 json
["closed_at"] = closed_at
.to_s
1245 # There is two kinds of comments:
1247 # * `CommitComment` are made on a commit page.
1248 # * `IssueComment` are made on an issue or pull request page.
1249 # * `ReviewComment` are made on the diff associated to a pull request.
1250 abstract class Comment
1253 # Identifier of this comment.
1256 redef init from_json
(api
, repo
, json
) do
1257 self.id
= json
["id"].as(Int)
1261 # User that made this comment.
1263 return new User.from_json
(api
, json
["user"].as(JsonObject))
1267 fun user
=(user
: User) do json
["user"] = user
.json
1269 # Creation time in ISODate format.
1270 fun created_at
: ISODate do
1271 return new ISODate.from_string
(json
["created_at"].as(String))
1275 fun created_at
=(created_at
: ISODate) do json
["created_at"] = created_at
.to_s
1277 # Last update time in ISODate format (if any).
1278 fun updated_at
: nullable ISODate do
1279 var res
= json
.get_or_null
("updated_at")
1280 if res
isa String then return new ISODate.from_string
(res
)
1285 fun updated_at
=(updated_at
: nullable ISODate) do
1286 if updated_at
== null then
1287 json
["updated_at"] = null
1289 json
["updated_at"] = updated_at
.to_s
1293 # Comment body text.
1294 fun body
: String do return json
["body"].as(String)
1297 fun body
=(body
: String) do json
["body"] = body
1299 # Does the comment contain an acknowledgement (+1)
1302 return body
.has
("\\+1\\b".to_re
) or body
.has
(":+1:") or body
.has
(":shipit:")
1306 # A comment made on a commit.
1310 redef var key
is lazy
do return "{repo.key}/comments/{id}"
1313 fun commit
: Commit do
1314 return api
.load_commit
(repo
, json
["commit_id"].as(String)).as(not null)
1318 fun commit
=(commit
: Commit) do json
["commit_id"] = commit
.json
1320 # Position of the comment on the line.
1321 fun position
: nullable String do
1322 var res
= json
.get_or_null
("position")
1323 if res
isa String then return res
1328 fun position
=(position
: nullable String) do json
["position"] = position
1330 # Line of the comment.
1331 fun line
: nullable String do
1332 var res
= json
.get_or_null
("line")
1333 if res
isa String then return res
1338 fun line
=(line
: nullable String) do json
["line"] = line
1340 # Path of the commented file.
1341 fun path
: String do return json
["path"].as(String)
1344 fun path
=(path
: String) do json
["path"] = path
1347 # Comments made on Github issue and pull request pages.
1349 # Should be accessed from `GithubAPI::load_issue_comment`.
1351 # See <https://developer.github.com/v3/issues/comments/>.
1355 redef var key
is lazy
do return "{repo.key}/issues/comments/{id}"
1357 # Issue that contains `self`.
1359 var number
= issue_url
.split
("/").last
.to_i
1360 return api
.load_issue
(repo
, number
).as(not null)
1363 # Link to the issue document on API.
1364 fun issue_url
: String do return json
["issue_url"].as(String)
1367 fun issue_url
=(issue_url
: String) do json
["issue_url"] = issue_url
1370 # Comments made on Github pull request diffs.
1372 # Should be accessed from `GithubAPI::load_diff_comment`.
1374 # See <https://developer.github.com/v3/pulls/comments/>.
1378 redef var key
is lazy
do return "{repo.key}/pulls/comments/{id}"
1380 # Pull request that contains `self`.
1381 fun pull
: PullRequest do
1382 var number
= pull_request_url
.split
("/").last
.to_i
1383 return api
.load_pull
(repo
, number
).as(not null)
1386 # Link to the pull request on API.
1387 fun pull_request_url
: String do return json
["pull_request_url"].as(String)
1389 # Set pull_request_url.
1390 fun pull_request_url
=(pull_request_url
: String) do json
["pull_request_url"] = pull_request_url
1393 fun diff_hunk
: String do return json
["diff_hunk"].as(String)
1396 fun diff_hunk
=(diff_hunk
: String) do json
["diff_hunk"] = diff_hunk
1398 # Path of commented file.
1399 fun path
: String do return json
["path"].as(String)
1402 fun path
=(path
: String) do json
["path"] = path
1404 # Position of the comment on the file.
1405 fun position
: Int do return json
["position"].as(Int)
1408 fun position
=(position
: Int) do json
["position"] = position
1410 # Original position in the diff.
1411 fun original_position
: Int do return json
["original_position"].as(Int)
1413 # Set original_position.
1414 fun original_position
=(original_position
: Int) do json
["original_position"] = original_position
1416 # Commit referenced by this comment.
1417 fun commit_id
: String do return json
["commit_id"].as(String)
1420 fun commit_id
=(commit_id
: String) do json
["commit_id"] = commit_id
1422 # Original commit id.
1423 fun original_commit_id
: String do return json
["original_commit_id"].as(String)
1425 # Set original_commit_id.
1426 fun original_commit_id
=(commit_id
: String) do json
["original_commit_id"] = commit_id
1429 # An event that occurs on a Github `Issue`.
1431 # Should be accessed from `GithubAPI::load_issue_event`.
1433 # See <https://developer.github.com/v3/issues/events/>.
1437 redef var key
is lazy
do return "{repo.key}/issues/events/{id}"
1439 # Event id on Github.
1442 redef init from_json
(api
, repo
, json
) do
1443 self.id
= json
["id"].as(Int)
1447 # Issue that contains `self`.
1449 return new Issue.from_json
(api
, repo
, json
["issue"].as(JsonObject))
1453 fun issue
=(issue
: Issue) do json
["issue"] = issue
.json
1455 # User that initiated the event.
1457 return new User.from_json
(api
, json
["actor"].as(JsonObject))
1461 fun actor
=(actor
: User) do json
["actor"] = actor
.json
1463 # Creation time in ISODate format.
1464 fun created_at
: ISODate do
1465 return new ISODate.from_string
(json
["created_at"].as(String))
1469 fun created_at
=(created_at
: ISODate) do json
["created_at"] = created_at
.to_s
1472 fun event
: String do return json
["event"].as(String)
1475 fun event
=(event
: String) do json
["event"] = event
1477 # Commit linked to this event (if any).
1478 fun commit_id
: nullable String do
1479 var res
= json
.get_or_null
("commit_id")
1480 if res
isa String then return res
1485 fun commit_id
=(commit_id
: nullable String) do json
["commit_id"] = commit_id
1487 # Label linked to this event (if any).
1488 fun labl
: nullable Label do
1489 var res
= json
.get_or_null
("label")
1490 if res
isa JsonObject then return new Label.from_json
(api
, repo
, res
)
1495 fun labl
=(labl
: nullable Label) do
1496 if labl
== null then
1499 json
["labl"] = labl
.json
1503 # User linked to this event (if any).
1504 fun assignee
: nullable User do
1505 var res
= json
.get_or_null
("assignee")
1506 if res
isa JsonObject then return new User.from_json
(api
, res
)
1511 fun assignee
=(assignee
: nullable User) do
1512 if assignee
== null then
1513 json
["assignee"] = null
1515 json
["assignee"] = assignee
.json
1519 # Milestone linked to this event (if any).
1520 fun milestone
: nullable Milestone do
1521 var res
= json
.get_or_null
("milestone")
1522 if res
isa JsonObject then return new Milestone.from_json
(api
, repo
, res
)
1527 fun milestone
=(milestone
: nullable User) do
1528 if milestone
== null then
1529 json
["milestone"] = null
1531 json
["milestone"] = milestone
.json
1535 # Rename linked to this event (if any).
1536 fun rename
: nullable RenameAction do
1537 var res
= json
.get_or_null
("rename")
1538 if res
isa JsonObject then return new RenameAction(res
)
1543 fun rename
=(rename
: nullable User) do
1544 if rename
== null then
1545 json
["rename"] = null
1547 json
["rename"] = rename
.json
1552 # A rename action maintains the name before and after a renaming action.
1556 var json
: JsonObject
1558 # Name before renaming.
1559 fun from
: String do return json
["from"].as(String)
1562 fun from
=(from
: String) do json
["from"] = from
1564 # Name after renaming.
1565 fun to
: String do return json
["to"].as(String)
1568 fun to
=(to
: String) do json
["to"] = to
1571 # Contributors list with additions, deletions, and commit counts.
1573 # Should be accessed from `Repo::contrib_stats`.
1575 # See <https://developer.github.com/v3/repos/statistics/>.
1576 class ContributorStats
1579 redef type OTHER: ContributorStats
1581 # Github API client.
1585 var json
: JsonObject
1587 # Init `self` from a `json` object.
1588 init from_json
(api
: GithubAPI, json
: JsonObject) do
1592 # User these statistics are about.
1594 return new User.from_json
(api
, json
["author"].as(JsonObject))
1598 fun author
=(author
: User) do json
["author"] = author
.json
1600 # Total number of commit.
1601 fun total
: Int do return json
["total"].as(Int)
1604 fun total
=(total
: Int) do json
["total"] = total
1606 # Are of weeks of activity with detailed statistics.
1607 fun weeks
: JsonArray do return json
["weeks"].as(JsonArray)
1610 fun weeks
=(weeks
: JsonArray) do json
["weeks"] = weeks
1612 # ContributorStats can be compared on the total amount of commits.
1613 redef fun <(o
) do return total
< o
.total
1616 # A Github file representation.
1618 # Mostly a wrapper around a json object.
1622 var json
: JsonObject
1625 fun filename
: String do return json
["filename"].as(String)
1628 fun filename
=(filename
: String) do json
["filename"] = filename