Skip to content

Commit

Permalink
Merge pull request #29 from zamblauskas/basic-auth-merge
Browse files Browse the repository at this point in the history
Basic auth support
  • Loading branch information
mrfyda authored Oct 31, 2017
2 parents d88adbe + 37baffa commit 01e9175
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 30 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Dependencies._

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

version := "1.7.1-SNAPSHOT"
version := "1.8.0-SNAPSHOT"

scalaVersion := "2.10.5"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.codacy.client.bitbucket.client

import play.api.libs.oauth.{ConsumerKey, OAuthCalculator, RequestToken}
import play.api.libs.ws.{WSAuthScheme, WSRequest}

/**
* Handles request authentication.
* Provides several different authentication options.
*
* @author - Robertas Zamblauskas
*/
object Authentication {

sealed trait Credentials

case class OAuthCredentials(key: String, secretKey: String, token: String, secretToken: String) extends Credentials

/**
* Your username and password | app password.
*/
case class BasicAuthCredentials(username: String, password: String) extends Credentials


sealed trait Authenticator {
def authenticate(req: WSRequest): WSRequest
}

object Authenticator {
def fromCredentials(credentials: Credentials): Authenticator = {
credentials match {
case c: OAuthCredentials => new OAuthAuthenticator(c)
case c: BasicAuthCredentials => new BasicAuthAuthenticator(c)
}
}
}

class OAuthAuthenticator(credentials: OAuthCredentials) extends Authenticator {
private lazy val KEY = ConsumerKey(credentials.key, credentials.secretKey)
private lazy val TOKEN = RequestToken(credentials.token, credentials.secretToken)

private lazy val requestSigner = OAuthCalculator(KEY, TOKEN)

def authenticate(req: WSRequest): WSRequest = req.sign(requestSigner)
}

class BasicAuthAuthenticator(credentials: BasicAuthCredentials) extends Authenticator {
def authenticate(req: WSRequest): WSRequest = req.withAuth(credentials.username, credentials.password, WSAuthScheme.BASIC)
}

/**
* Provide nicer syntax for authentication.
*/
implicit class WsRequestExtensions(val req: WSRequest) extends AnyVal {
def authenticate(authenticator: Authenticator): WSRequest = authenticator.authenticate(req)
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
package com.codacy.client.bitbucket.client

import java.net.URI
import java.util.concurrent.{SynchronousQueue, ThreadPoolExecutor, TimeUnit}

import com.codacy.client.bitbucket.client.Authentication._
import com.codacy.client.bitbucket.util.HTTPStatusCodes
import com.codacy.client.bitbucket.util.Implicits.URIQueryParam
import com.ning.http.client.AsyncHttpClientConfig
import play.api.http.{ContentTypeOf, Writeable}
import play.api.libs.json._
import play.api.libs.oauth._
import play.api.libs.ws.ning.{NingAsyncHttpClientConfigBuilder, NingWSClient}

import scala.concurrent.Await
import scala.concurrent.duration.{Duration, SECONDS}
import scala.util.{Failure, Properties, Success, Try}

class BitbucketClient(key: String, secretKey: String, token: String, secretToken: String) {

private lazy val KEY = ConsumerKey(key, secretKey)
private lazy val TOKEN = RequestToken(token, secretToken)

class BitbucketClient(credentials: Credentials) {

private lazy val requestTimeout = Duration(10, SECONDS)
private lazy val requestSigner = OAuthCalculator(KEY, TOKEN)

private lazy val authenticator = Authenticator.fromCredentials(credentials)

/*
* Does an API request and parses the json output into a class
Expand Down Expand Up @@ -75,7 +74,7 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
*/
private def performRequest[D, T](method: String, request: Request[T], values: D)(implicit reader: Reads[T], writer: Writeable[D], contentType: ContentTypeOf[D]): RequestResponse[T] = withClientRequest { client =>
val jpromise = client.url(request.url)
.sign(requestSigner)
.authenticate(authenticator)
.withFollowRedirects(follow = true)
.withMethod(method).withBody(values).execute()
val result = Await.result(jpromise, requestTimeout)
Expand Down Expand Up @@ -128,7 +127,7 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken
/* copy paste from post ... */
def delete[T](url: String): RequestResponse[Boolean] = withClientRequest { client =>
val jpromise = client.url(url)
.sign(requestSigner)
.authenticate(authenticator)
.withFollowRedirects(follow = true)
.delete()
val result = Await.result(jpromise, requestTimeout)
Expand All @@ -144,7 +143,7 @@ class BitbucketClient(key: String, secretKey: String, token: String, secretToken

private def get(url: String): Either[ResponseError, JsValue] = withClientEither { client =>
val jpromise = client.url(url)
.sign(requestSigner)
.authenticate(authenticator)
.withFollowRedirects(follow = true)
.get()
val result = Await.result(jpromise, requestTimeout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class BuildStatusServices(client: BitbucketClient) {
*
*/
def getBuildStatus(owner: String, repository: String, commit: String, key: String): RequestResponse[BuildStatus] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build/$key"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build/$key"

client.execute(Request(url, classOf[BuildStatus]))
}
Expand All @@ -22,7 +22,7 @@ class BuildStatusServices(client: BitbucketClient) {
*
*/
def createBuildStatus(owner: String, repository: String, commit: String, buildStatus: BuildStatus): RequestResponse[BuildStatus] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build"

val values = Map("state" -> Seq(buildStatus.state.toString), "key" -> Seq(buildStatus.key),
"name" -> Seq(buildStatus.name), "url" -> Seq(buildStatus.url),
Expand All @@ -36,7 +36,7 @@ class BuildStatusServices(client: BitbucketClient) {
*
*/
def updateBuildStatus(owner: String, repository: String, commit: String, buildStatus: BuildStatus): RequestResponse[BuildStatus] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build/${buildStatus.key}"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/commit/$commit/statuses/build/${buildStatus.key}"

val payload = Json.obj(
"state" -> buildStatus.state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import play.api.libs.json.{JsNumber, JsObject, JsString}
class CommitServices(client: BitbucketClient) {

def createComment(author: String, repo: String, commit: String, body: String, file: Option[String] = None, line: Option[Int] = None): RequestResponse[CommitComment] = {
val url = s"https://bitbucket.org/!api/1.0/repositories/$author/$repo/changesets/${CommitHelper.anchor(commit)}/comments"
val url = s"https://bitbucket.org/api/1.0/repositories/$author/$repo/changesets/${CommitHelper.anchor(commit)}/comments"

val params = file.map(filename => "filename" -> JsString(filename)) ++
line.map(lineTo => "line_to" -> JsNumber(lineTo))
Expand All @@ -19,7 +19,7 @@ class CommitServices(client: BitbucketClient) {
}

def deleteComment(author: String, repo: String, commit: String, commentId: Long): Unit = {
val url = s"https://bitbucket.org/!api/1.0/repositories/$author/$repo/changesets/${CommitHelper.anchor(commit)}/comments/$commentId"
val url = s"https://bitbucket.org/api/1.0/repositories/$author/$repo/changesets/${CommitHelper.anchor(commit)}/comments/$commentId"

client.delete(url)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@ class HookServices(client: BitbucketClient) {
"url" -> hookUrl,
"events" -> events
)

client.postJson(Request(servicesUrl, classOf[Webhook]), payload)
}

def update(author: String, repo: String, uuid: String,
active: Boolean, description: String, hookUrl: String, events:Set[String]): RequestResponse[Webhook] = {
val servicesUrl = getServicesUrl(author, repo)
val payload = Json.obj(
"active" -> active,
"description" -> description,
"url" -> hookUrl,
"events" -> events
)
client.putJson(Request(s"$servicesUrl/$uuid", classOf[Webhook]), payload)
}

def delete(author: String, repo: String, uuid: String): RequestResponse[Boolean] = {
val servicesUrl = getServicesUrl(author, repo)
client.delete(s"$servicesUrl/$uuid")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class PullRequestServices(client: BitbucketClient) {
*
*/
def getPullRequests(owner: String, repository: String, states: Seq[String] = Seq("OPEN")): RequestResponse[Seq[PullRequest]] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests?pagelen=50&state=${states.mkString("&state=")}"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests?pagelen=50&state=${states.mkString("&state=")}"

client.executePaginated(Request(url, classOf[Seq[PullRequest]]))
}
Expand All @@ -24,13 +24,13 @@ class PullRequestServices(client: BitbucketClient) {
*
*/
def getPullRequestCommits(owner: String, repository: String, prId: Long): RequestResponse[Seq[SimpleCommit]] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests/$prId/commits?pagelen=100"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests/$prId/commits?pagelen=100"

client.executePaginated(Request(url, classOf[Seq[SimpleCommit]]))
}

def create(owner: String, repository: String, title: String, sourceBranch: String, destinationBranch: String): RequestResponse[JsObject] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests"

val payload = Json.obj(
"title" -> title,
Expand All @@ -50,22 +50,22 @@ class PullRequestServices(client: BitbucketClient) {
}

def postApprove(owner: String, repository: String, prId: Long): RequestResponse[JsObject] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests/$prId/approve"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests/$prId/approve"
client.postJson(Request(url, classOf[JsObject]), JsNull)
}

def deleteApprove(owner: String, repository: String, prId: Long): RequestResponse[Boolean] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests/$prId/approve"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests/$prId/approve"
client.delete(url)
}

def merge(owner: String, repository: String, prId: Long): RequestResponse[JsObject] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests/$prId/merge"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests/$prId/merge"
client.postJson(Request(url, classOf[JsObject]), JsNull)
}

def decline(owner: String, repository: String, prId: Long): RequestResponse[JsObject] = {
val url = s"https://bitbucket.org/!api/2.0/repositories/$owner/$repository/pullrequests/$prId/decline"
val url = s"https://bitbucket.org/api/2.0/repositories/$owner/$repository/pullrequests/$prId/decline"
client.postJson(Request(url, classOf[JsObject]), JsNull)
}

Expand Down Expand Up @@ -95,7 +95,7 @@ class PullRequestServices(client: BitbucketClient) {
}

def listComments(author: String, repo: String, pullRequestId: Int): RequestResponse[Seq[SimplePullRequestComment]] = {
val url = s"https://bitbucket.org/!api/1.0/repositories/$author/$repo/pullrequests/$pullRequestId/comments"
val url = s"https://bitbucket.org/api/1.0/repositories/$author/$repo/pullrequests/$pullRequestId/comments"

client.execute(Request(url, classOf[Seq[SimplePullRequestComment]]))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ class RepositoryServices(client: BitbucketClient) {
* Use this if you're looking for a full list of all of the repositories associated with a user
*/
def getRepositories: RequestResponse[Seq[SimpleRepository]] = {
client.execute(Request(s"https://bitbucket.org/!api/1.0/user/repositories", classOf[Seq[SimpleRepository]]))
client.execute(Request(s"https://bitbucket.org/api/1.0/user/repositories", classOf[Seq[SimpleRepository]]))
}

/*
* Gets the list of the user's repositories. Private repositories only appear on this list
* if the caller is authenticated and is authorized to view the repository.
*/
def getRepositories(username: String): RequestResponse[Seq[Repository]] = {
client.executePaginated(Request(s"https://bitbucket.org/!api/2.0/repositories/$username", classOf[Seq[Repository]]))
client.executePaginated(Request(s"https://bitbucket.org/api/2.0/repositories/$username", classOf[Seq[Repository]]))
}

/*
* Creates a ssh key
*/
def createKey(username: String, repo: String, key: String): RequestResponse[SshKey] = {
val url = s"https://bitbucket.org/!api/1.0/repositories/$username/$repo/deploy-keys"
val url = s"https://bitbucket.org/api/1.0/repositories/$username/$repo/deploy-keys"

val values = Json.obj(
"key" -> key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ class UserServices(client: BitbucketClient) {
* Gets the basic information associated with the token owner account.
*/
def getUser: RequestResponse[User] = {
client.execute(Request("https://bitbucket.org/!api/1.0/user", classOf[User]))
client.execute(Request("https://bitbucket.org/api/1.0/user", classOf[User]))
}

/*
* Gets the basic information associated with an account.
*/
def getUser(username: String): RequestResponse[User] = {
client.execute(Request(s"https://bitbucket.org/!api/1.0/users/$username", classOf[User]))
client.execute(Request(s"https://bitbucket.org/api/1.0/users/$username", classOf[User]))
}

/*
* Creates a ssh key
*/
def createKey(username: String, key: String): RequestResponse[SshKey] = {
val url = s"https://bitbucket.org/!api/1.0/users/$username/ssh-keys"
val url = s"https://bitbucket.org/api/1.0/users/$username/ssh-keys"

val values = Json.obj(
"key" -> key,
Expand Down

0 comments on commit 01e9175

Please sign in to comment.