Skip to content

Commit ce6ecb9

Browse files
author
Rodrigo Fernandes
committed
Merge pull request #1 from codacy/more-prs
More prs
2 parents 5e58fb4 + a0c71b2 commit ce6ecb9

14 files changed

+247
-56
lines changed

build.sbt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import Dependencies._
22

33
name := """bitbucket-scala-client"""
44

5-
version := "1.0"
5+
version := "1.1-SNAPSHOT"
66

7-
scalaVersion := "2.11.1"
7+
scalaVersion := "2.10.5"
8+
9+
crossScalaVersions := Seq("2.10.5", "2.11.6")
810

911
scalacOptions := Seq("-deprecation", "-feature", "-unchecked", "-Ywarn-adapted-args", "-Xlint")
1012

@@ -25,7 +27,7 @@ publishMavenStyle := true
2527

2628
publishArtifact in Test := false
2729

28-
pomIncludeRepository := { _ => false}
30+
pomIncludeRepository := { _ => false }
2931

3032
publishTo := {
3133
val nexus = "https://oss.sonatype.org/"

project/Dependencies.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import sbt._
33
object Dependencies {
44

55
// Generic
6-
lazy val jodaTime = "joda-time" % "joda-time" % "2.3"
6+
lazy val jodaTime = "joda-time" % "joda-time" % "2.7"
77

88
// Play framework
9-
lazy val playWS = "com.typesafe.play" %% "play-ws" % "2.3.2"
9+
lazy val playWS = "com.typesafe.play" %% "play-ws" % "2.3.8"
1010

1111
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.codacy.client.bitbucket
2+
3+
import org.joda.time.DateTime
4+
import play.api.libs.functional.syntax._
5+
import play.api.libs.json._
6+
7+
case class Commit(hash: String, authorName: String, parents: Option[Seq[String]], date: DateTime, message: String)
8+
9+
object Commit {
10+
val dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ"
11+
implicit val jodaDateTimeReads = Reads.jodaDateReads(dateFormat)
12+
13+
implicit val reader: Reads[Commit] = (
14+
(__ \ "hash").read[String] and
15+
(__ \ "author" \ "user" \ "username").read[String] and
16+
(__ \ "parents" \\ "hash").read[Option[Seq[String]]] and
17+
(__ \ "date").read[DateTime] and
18+
(__ \ "message").read[String]
19+
)(Commit.apply _)
20+
}

src/main/scala/com/codacy/client/bitbucket/PullRequest.scala

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,33 @@ import org.joda.time.DateTime
44
import play.api.libs.functional.syntax._
55
import play.api.libs.json._
66

7-
case class PullRequest(id: Long, title: String, description: String, author: String,
7+
case class PullRequest(id: Long, title: String, description: String,
8+
authorUsername: String, authorAvatar: Option[String],
89
state: String, created_on: DateTime, updated_on: DateTime,
9-
destBranch: String, destCommit: String)
10+
sourceRepository: String, sourceBranch: String, sourceCommit: String,
11+
destRepository: String, destBranch: String, destCommit: String,
12+
apiUrls: Seq[ApiUrl]) {
13+
val url = s"https://bitbucket.org/$destRepository/pull-request/$id"
14+
}
15+
16+
object ApiUrlType extends Enumeration {
17+
val Commits = Value("commits")
18+
val Decline = Value("decline")
19+
val Self = Value("self")
20+
val Comments = Value("comments")
21+
val Patch = Value("patch")
22+
val Merge = Value("merge")
23+
val Html = Value("html")
24+
val Activity = Value("activity")
25+
val Diff = Value("diff")
26+
val Approve = Value("approve")
27+
28+
def find(urlType: String): Option[Value] = {
29+
values.find(_.toString == urlType)
30+
}
31+
}
32+
33+
case class ApiUrl(urlType: ApiUrlType.Value, link: String)
1034

1135
object PullRequest {
1236
val dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ"
@@ -17,10 +41,24 @@ object PullRequest {
1741
(__ \ "title").read[String] and
1842
(__ \ "description").read[String] and
1943
(__ \ "author" \ "username").read[String] and
44+
(__ \ "author" \ "links" \ "avatar").read[Option[String]] and
2045
(__ \ "state").read[String] and
2146
(__ \ "created_on").read[DateTime] and
2247
(__ \ "updated_on").read[DateTime] and
48+
(__ \ "source" \ "repository" \ "full_name").read[String] and
49+
(__ \ "source" \ "branch" \ "name").read[String] and
50+
(__ \ "source" \ "commit" \ "hash").read[String] and
51+
(__ \ "destination" \ "repository" \ "full_name").read[String] and
2352
(__ \ "destination" \ "branch" \ "name").read[String] and
24-
(__ \ "destination" \ "commit" \ "hash").read[String]
53+
(__ \ "destination" \ "commit" \ "hash").read[String] and
54+
(__ \ "links").read[Map[String, Map[String, String]]].map(parseLinks)
2555
)(PullRequest.apply _)
56+
57+
private def parseLinks(links: Map[String, Map[String, String]]): Seq[ApiUrl] = {
58+
(for {
59+
(linkName, linkMap) <- links
60+
urlType <- ApiUrlType.find(linkName)
61+
linkUrl <- linkMap.get("href")
62+
} yield ApiUrl(urlType, linkUrl)).toSeq
63+
}
2664
}

src/main/scala/com/codacy/client/bitbucket/Repository.scala

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,61 @@ import org.joda.time.DateTime
44
import play.api.libs.functional.syntax._
55
import play.api.libs.json._
66

7-
case class Repository(name: String, full_name: String, description: String,
8-
scm: String, created_on: DateTime, updated_on: DateTime,
9-
httpsUrl: String, sshUrl: String, owner: String, size: Long,
10-
has_issues: Boolean, is_private: Boolean, language: String)
7+
case class Repository(name: String, full_name: String, description: String, scm: String,
8+
created_on: DateTime, updated_on: DateTime, owner: String, size: Long,
9+
has_issues: Boolean, is_private: Boolean, language: String,
10+
url: Seq[RepositoryUrl])
1111

1212
object Repository {
1313
val dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ"
1414
implicit val jodaDateTimeReads = Reads.jodaDateReads(dateFormat)
1515

1616
implicit val reader: Reads[Repository] = {
17-
val url1 = (__ \ "links" \ "clone")(0).read[Url]
18-
val url2 = (__ \ "links" \ "clone")(1).read[Url]
19-
20-
val httpUrl = url1.filter(_.name == "https").orElse(url2).map(_.href)
21-
val sshUrl = url2.filter(_.name == "ssh").orElse(url1).map(_.href)
22-
2317
((__ \ "name").read[String] and
2418
(__ \ "full_name").read[String] and
2519
(__ \ "description").read[String] and
2620
(__ \ "scm").read[String] and
2721
(__ \ "created_on").read[DateTime] and
2822
(__ \ "updated_on").read[DateTime] and
29-
httpUrl and
30-
sshUrl and
3123
(__ \ "owner" \ "username").read[String] and
3224
(__ \ "size").read[Long] and
3325
(__ \ "has_issues").read[Boolean] and
3426
(__ \ "is_private").read[Boolean] and
35-
(__ \ "language").read[String]
27+
(__ \ "language").read[String] and
28+
(__ \ "links").read[Map[String, JsValue]].map(parseLinks)
3629
)(Repository.apply _)
3730
}
31+
32+
private def parseLinks(links: Map[String, JsValue]): Seq[RepositoryUrl] = {
33+
links.flatMap {
34+
case (linkName, linkMap) =>
35+
36+
val simpleLinks = for {
37+
ref <- linkMap.asOpt[Map[String, String]]
38+
urlType <- RepositoryUrlType.find(linkName)
39+
linkUrl <- ref.get("href")
40+
} yield RepositoryUrl(urlType, linkUrl)
41+
42+
val complexLinks = for {
43+
refs <- linkMap.asOpt[Seq[Map[String, String]]].toSeq
44+
ref <- refs
45+
linkName <- ref.get("name")
46+
urlType <- RepositoryUrlType.find(linkName)
47+
linkUrl <- ref.get("href")
48+
} yield RepositoryUrl(urlType, linkUrl)
49+
50+
simpleLinks ++ complexLinks
51+
}.toSeq
52+
}
3853
}
3954

40-
case class Url(name: String, href: String)
55+
object RepositoryUrlType extends Enumeration {
56+
val Https = Value("https")
57+
val Ssh = Value("ssh")
4158

42-
object Url {
43-
implicit val reader: Reads[Url] = (
44-
(__ \ "name").read[String] and
45-
(__ \ "href").read[String]
46-
)(Url.apply _)
59+
def find(urlType: String): Option[Value] = {
60+
values.find(_.toString == urlType)
61+
}
4762
}
63+
64+
case class RepositoryUrl(urlType: RepositoryUrlType.Value, link: String)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.codacy.client.bitbucket
2+
3+
import play.api.libs.json.{Json, Reads}
4+
5+
case class FieldValue(name:String,value:String)
6+
case class ServiceValue(fields:Seq[FieldValue],`type`:String)
7+
case class Service(id:Long, service:ServiceValue)
8+
9+
object Service{
10+
implicit val reads: Reads[Service] = {
11+
implicit lazy val r0 = Json.reads[FieldValue]
12+
implicit lazy val r1 = Json.reads[ServiceValue]
13+
Json.reads[Service]
14+
}
15+
}

src/main/scala/com/codacy/client/bitbucket/client/BitbucketClient.scala

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
4747
/*
4848
* Does an API post
4949
*/
50-
def post[T](request: Request[T], values: Map[String, Seq[String]])(implicit reader: Reads[T]): RequestResponse[T] = {
50+
def post[T](request: Request[T], values: JsValue)(implicit reader: Reads[T]): RequestResponse[T] = {
5151
val client: WSClient = new NingWSClient(new AsyncHttpClient().getConfig)
5252

5353
val jpromise = client.url(request.url).sign(OAuthCalculator(KEY, TOKEN)).withFollowRedirects(follow = true).post(values)
@@ -56,13 +56,6 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
5656
if (Seq(HTTPStatusCodes.OK, HTTPStatusCodes.CREATED).contains(result.status)) {
5757
val body = result.body
5858

59-
/* TODO: remove this when not needed (only keep for debug purposes) */
60-
// println("\n\n")
61-
// println(s"STATUS: ${result.status}")
62-
// println("\n\n")
63-
// println(body)
64-
// println("\n\n")
65-
6659
val jsValue = parseJson(body)
6760
jsValue match {
6861
case Right(responseObj) =>
@@ -75,6 +68,20 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
7568
}
7669
}
7770

71+
/* copy paste from post ... */
72+
def delete[T](url: String): RequestResponse[Boolean] = {
73+
val client: WSClient = new NingWSClient(new AsyncHttpClient().getConfig)
74+
75+
val jpromise = client.url(url).sign(OAuthCalculator(KEY, TOKEN)).withFollowRedirects(follow = true).delete()
76+
val result = Await.result(jpromise, Duration(10, SECONDS))
77+
78+
if (Seq(HTTPStatusCodes.OK, HTTPStatusCodes.CREATED, HTTPStatusCodes.NO_CONTENT).contains(result.status)) {
79+
RequestResponse(Option(true))
80+
} else {
81+
RequestResponse(None, result.statusText, hasError = true)
82+
}
83+
}
84+
7885
private def get(url: String): Either[ResponseError, JsValue] = {
7986
val client: WSClient = new NingWSClient(new AsyncHttpClient().getConfig)
8087

@@ -83,14 +90,6 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
8390

8491
if (Seq(HTTPStatusCodes.OK, HTTPStatusCodes.CREATED).contains(result.status)) {
8592
val body = result.body
86-
87-
/* TODO: remove this when not needed (only keep for debug purposes) */
88-
// println("\n\n")
89-
// println(s"STATUS: ${result.status}")
90-
// println("\n\n")
91-
// println(body)
92-
// println("\n\n")
93-
9493
parseJson(body)
9594
} else {
9695
Left(ResponseError(java.util.UUID.randomUUID().toString, result.statusText, result.statusText))

src/main/scala/com/codacy/client/bitbucket/service/CommitServices.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package com.codacy.client.bitbucket.service
22

33
import com.codacy.client.bitbucket.CommitComment
44
import com.codacy.client.bitbucket.client.{BitbucketClient, Request, RequestResponse}
5+
import play.api.libs.json.Json
56

67
class CommitServices(client: BitbucketClient) {
78

89
def createComment(author: String, repo: String, commit: String, body: String): RequestResponse[CommitComment] = {
910
val url = s"https://bitbucket.org/!api/1.0/repositories/$author/$repo/changesets/${commit.take(12)}/comments"
1011

11-
val values = Map(
12-
"content" -> Seq(body)
13-
)
12+
val values = Json.obj("content" -> body)
1413

1514
client.post(Request(url, classOf[CommitComment]), values)
1615
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.codacy.client.bitbucket.service
2+
3+
import com.codacy.client.bitbucket.Service
4+
import com.codacy.client.bitbucket.client.{BitbucketClient, Request, RequestResponse}
5+
import play.api.libs.json.Json
6+
7+
class HookServices(client: BitbucketClient) {
8+
9+
def list(author: String, repo: String): RequestResponse[Seq[Service]] = {
10+
val servicesUrl = getServicesUrl(author, repo)
11+
client.execute(Request(servicesUrl, classOf[Seq[Service]]))
12+
}
13+
14+
def create(author: String, repo: String, hookType: String, hookUrl: String): RequestResponse[Service] = {
15+
val servicesUrl = getServicesUrl(author, repo)
16+
val payload = Json.obj(
17+
"type" -> hookType,
18+
"URL" -> hookUrl
19+
)
20+
client.post(Request(servicesUrl, classOf[Service]), payload)
21+
}
22+
23+
def delete(author: String, repo: String, id: Long): RequestResponse[Boolean] = {
24+
val servicesUrl = getServicesUrl(author, repo)
25+
client.delete(s"$servicesUrl/$id")
26+
}
27+
28+
private lazy val BASE_URL: String = "https://bitbucket.org/!api/1.0/repositories"
29+
30+
private def getServicesUrl(author: String, repo: String) = s"$BASE_URL/$author/$repo/services"
31+
32+
}

src/main/scala/com/codacy/client/bitbucket/service/IssueServices.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ package com.codacy.client.bitbucket.service
22

33
import com.codacy.client.bitbucket.Issue
44
import com.codacy.client.bitbucket.client.{BitbucketClient, Request, RequestResponse}
5+
import play.api.libs.json.Json
56

67
class IssueServices(client: BitbucketClient) {
78

89
def createIssue(author: String, repo: String, title: String, body: String): RequestResponse[Issue] = {
910
val url = s"https://bitbucket.org/!api/1.0/repositories/$author/$repo/issues"
1011

11-
val values = Map(
12-
"title" -> Seq(title),
13-
"content" -> Seq(body)
12+
val values = Json.obj(
13+
"title" -> title,
14+
"content" -> body
1415
)
1516

1617
client.post(Request(url, classOf[Issue]), values)

0 commit comments

Comments
 (0)