Skip to content

Commit d0e6856

Browse files
committed
Merge branch 'master' of https://github.com/code-check/github-api-scala into 30-UserEnv
2 parents 86cdd56 + 95a9367 commit d0e6856

File tree

13 files changed

+409
-47
lines changed

13 files changed

+409
-47
lines changed

.editorconfig

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# EditorConfig (http://EditorConfig.org)
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
max_line_length = 80
12+
13+
indent_style = space
14+
indent_size = 2
15+
16+
17+
[*.md]
18+
trim_trailing_whitespace = false
19+
20+
[COMMIT_EDITMSG]
21+
max_line_length = 0
22+
23+
[*.scala]
24+
max_line_length = 120
25+
26+
[*.php]
27+
indent_size = 4
28+
29+
[*.html]
30+
31+
[*.css]
32+
33+
[*.js]
34+
35+
# Reactjs
36+
[*.jsx]

data/milestones.json

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11
[
22
{
3-
"title": "Sprint1",
4-
"description": "From 2015/04/13 to 2015/04/26",
5-
"due_on": "2015-04-26"
3+
"title": "Sprint4",
4+
"description": "2015/05/25 to 2015/06/07",
5+
"due_on": "2015-06-07"
66
},
77
{
8-
"title": "Sprint2",
9-
"description": "2015/04/27 to 2015/05/10",
10-
"due_on": "2015-05-10"
8+
"title": "Sprint5",
9+
"description": "2015/06/08 to 2015/06/21",
10+
"due_on": "2015-06-21"
1111
},
1212
{
13-
"title": "Sprint3",
14-
"description": "From 2015/05/11 to 2015/05/24",
15-
"due_on": "2015-05-24"
13+
"title": "Sprint6",
14+
"description": "2015/06/22 to 2015/07/05",
15+
"due_on": "2015-07-05"
1616
},
1717
{
18-
"title": "Sprint4",
19-
"description": "2015/05/25 to 2015/06/07",
20-
"due_on": "2015-06-07"
18+
"title": "Sprint7",
19+
"description": "2015/07/06 to 2015/07/19",
20+
"due_on": "2015-07-19"
21+
},
22+
{
23+
"title": "Sprint8",
24+
"description": "2015/07/20 to 2015/08/02",
25+
"due_on": "2015-08-02"
26+
},
27+
{
28+
"title": "Sprint9",
29+
"description": "2015/08/03 to 2015/08/16",
30+
"due_on": "2015-08-16"
31+
},
32+
{
33+
"title": "Sprint10",
34+
"description": "2015/08/17 to 2015/08/30",
35+
"due_on": "2015-08-30"
36+
},
37+
{
38+
"title": "Sprint11",
39+
"description": "2015/08/31 to 2015/09/13",
40+
"due_on": "2015-09-13"
41+
},
42+
{
43+
"title": "Sprint12",
44+
"description": "2015/09/14 to 2015/09/27",
45+
"due_on": "2015-09-27"
2146
}
2247
]

src/main/scala/codecheck/github/api/GitHubAPI.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class GitHubAPI(token: String, client: AsyncHttpClient, tokenType: String = "tok
2525
with LabelOp
2626
with IssueOp
2727
with MilestoneOp
28+
with WebhookOp
2829
with CollaboratorOp
2930
{
3031

src/main/scala/codecheck/github/api/OAuthAPI.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.json4s.jackson.JsonMethods
1111
import org.json4s.DefaultFormats
1212
import java.util.UUID
1313
import codecheck.github.models.AccessToken
14+
import codecheck.github.exceptions.OAuthAPIException
1415

1516
class OAuthAPI(clientId: String, clientSecret: String, redirectUri: String, client: AsyncHttpClient) {
1617
private implicit val format = DefaultFormats
@@ -48,7 +49,10 @@ class OAuthAPI(clientId: String, clientSecret: String, redirectUri: String, clie
4849
client.prepareRequest(builder.build).execute(new AsyncCompletionHandler[Response]() {
4950
def onCompleted(res: Response) = {
5051
val json = JsonMethods.parse(res.getResponseBody("utf-8"))
51-
deferred.success(AccessToken(json))
52+
(json \ "error").toOption match {
53+
case Some(_) => deferred.failure(new OAuthAPIException(json))
54+
case None => deferred.success(AccessToken(json))
55+
}
5256
res
5357
}
5458
override def onThrowable(t: Throwable) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package codecheck.github.exceptions
2+
3+
import org.json4s.JValue
4+
import codecheck.github.models.OAuthErrorResponse
5+
6+
class OAuthAPIException(body: JValue) extends Exception {
7+
lazy val error = OAuthErrorResponse(body)
8+
9+
override def getMessage = error.toString
10+
11+
}

src/main/scala/codecheck/github/models/AbstractJson.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.json4s.JValue
44
import org.json4s.JNothing
55
import org.json4s.JNull
66
import org.json4s.JObject
7+
import org.json4s.JArray
78
import org.json4s.Formats
89
import org.json4s.DefaultFormats
910
import org.json4s.jackson.JsonMethods
@@ -59,4 +60,16 @@ class AbstractJson(value: JValue) {
5960
}
6061

6162
override def toString = JsonMethods.pretty(value)
63+
64+
def seqOpt[T](path: String): Seq[T] = {
65+
path.split("\\.").foldLeft(value) { (v, s) =>
66+
v \ s
67+
} match {
68+
case JNothing => Nil
69+
case JNull => Nil
70+
case v: JArray => v.values.map(_.asInstanceOf[T])
71+
}
72+
}
73+
74+
def seq(path: String): Seq[String] = seqOpt(path)
6275
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package codecheck.github.models
2+
3+
import org.json4s.JValue
4+
5+
case class OAuthErrorResponse(value: JValue) extends AbstractJson(value) {
6+
def error = get("error")
7+
def error_description = get("error_description")
8+
def error_uri = get("error_uri")
9+
}
10+

src/main/scala/codecheck/github/models/Organization.scala

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,66 @@ package codecheck.github.models
33
import org.json4s.JValue
44
import org.json4s.JsonDSL._
55

6+
/*
7+
## Github API response differently to organization requests depending on your involvement
8+
9+
# listOwnOrganization and listUserOrganization returns only fields in the Organization class
10+
11+
# updateOrganization will return all fields as you must be a member to update
12+
13+
# getOrganization will return the fields in OrganizationDetail if you are a member
14+
If you are not a member of the organization, the following fields will NOT be returned
15+
- total_private_repos (int)
16+
- owned_private_repos (int)
17+
- private_gists (int)
18+
- disk_usage (long)
19+
- collaborators (int)
20+
- billing_email (string)
21+
- plan (object -> name, space, private_repos)
22+
23+
## The following fields are optional (can be empty)
24+
- name (string)
25+
- email (string)
26+
- description (string)
27+
- location (string)
28+
- blog (string)
29+
- company (string)
30+
*/
31+
32+
633
class Organization(value: JValue) extends AbstractJson(value) {
734
def login = get("login")
835
def id = get("id").toLong
936
def url = get("url")
37+
def repos_url = get("repos_url")
38+
def events_url = get("events_url")
39+
def members_url = get("members_url")
40+
def public_members_url = get("public_members_url")
1041
def avatar_url = get("avatar_url")
1142
def description = get("description")
1243
}
1344

1445
case class OrganizationDetail(value: JValue) extends Organization(value) {
1546
def name = get("name")
1647
def company = opt("company")
17-
def email = get("email")
18-
def billing_email = get("billing_email")
48+
def blog = get("blog")
1949
def location = get("location")
50+
def email = get("email")
2051
def public_repos = get("public_repos").toInt
2152
def public_gists = get("public_gists").toInt
53+
def followers = get("followers").toInt
54+
def following = get("following").toInt
55+
def html_url = get("html_url")
56+
def created_at = date("created_at")
57+
def updated_at = date("updated_at")
58+
def `type` = get("type")
2259
}
60+
2361
case class OrganizationInput(
2462
name: Option[String] = None,
2563
company: Option[String] = None,
2664
description: Option[String] = None,
2765
location: Option[String] = None,
2866
email: Option[String] = None,
2967
billing_email: Option[String] = None
30-
) extends AbstractInput
31-
32-
/*
33-
object OrganizationInput {
34-
import scala.language.implicitConversions
35-
//implicit def fromString(s: String): Option[String] = Some(s)
36-
} */
68+
) extends AbstractInput
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package codecheck.github.models
2+
3+
import org.json4s.JValue
4+
5+
class Webhook(value: JValue) extends AbstractJson(value) {
6+
def id: Long = get("id").toLong
7+
def url = get("url")
8+
def test_url = get("test_url")
9+
def ping_url = get("ping_url")
10+
def name = get("name")
11+
def events = seq("events")
12+
def active = boolean("active")
13+
def config = new WebhookConfig(get("config.url"), get("config.content_type"), get("config.secret"), get("config.insecure_ssl"));
14+
def last_response = new WebhookResponse(value \ "last_response")
15+
def updated_at = date("updated_at")
16+
def created_at = date("created_at")
17+
}
18+
19+
case class WebhookConfig(
20+
url: String,
21+
content_type: String = "json",
22+
secret: String = "",
23+
insecure_ssl: String = "0"
24+
) extends AbstractInput
25+
26+
case class WebhookCreateInput(
27+
name: String,
28+
config: WebhookConfig,
29+
active: Boolean = true,
30+
events: Seq[String] = Seq("push"),
31+
add_events: Seq[String] = Nil,
32+
remove_events: Seq[String] = Nil
33+
) extends AbstractInput
34+
35+
case class WebhookUpdateInput(
36+
config: Option[WebhookConfig] = None,
37+
events: Option[Seq[String]] = None,
38+
add_events: Option[Seq[String]] = None,
39+
remove_events: Option[Seq[String]] = None,
40+
active: Option[Boolean] = None
41+
) extends AbstractInput
42+
43+
case class WebhookResponse(value: JValue) extends AbstractJson(value) {
44+
def code = get("code")
45+
def status = get("status")
46+
def message = get("message")
47+
}

src/main/scala/codecheck/github/operations/OrganizationOp.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,9 @@ trait OrganizationOp {
4040
}
4141
}
4242

43-
def updateOrganization(org: String, input: OrganizationInput): Future[Option[OrganizationDetail]] = {
44-
self.exec("PATCH", s"/orgs/${org}", input.value, true).map { res =>
45-
res.statusCode match {
46-
case 404 => None
47-
case 200 => Some(new OrganizationDetail(res.body))
48-
}
43+
def updateOrganization(org: String, input: OrganizationInput): Future[OrganizationDetail] = {
44+
self.exec("PATCH", s"/orgs/${org}", input.value).map { res =>
45+
new OrganizationDetail(res.body)
4946
}
5047
}
5148
}

0 commit comments

Comments
 (0)