diff --git a/README.md b/README.md index 3ccc294..a275383 100644 --- a/README.md +++ b/README.md @@ -3,61 +3,24 @@ repatch-github this is a dispatch plugin for github API v3. -## setup - -### using source deps - -use source dependency to point to a commit hash of this project. - -```scala -import sbt._, Keys._ - -object build extends Build{ - lazy val root = Project( - "project_name", - file(".") - ) settings ( - scalaVersion := "2.9.2" - ) dependsOn ( - uri("git://github.com/eed3si9n/repatch-github#hash") - ) -} -``` - ## authentication you can choose one from the four authenticating clients: -- automatic (`Client`) +- Local Github Config (`LocalConfigClient`) - OAuth (`OAuthClient`) - BasicAuth (`BasicAuthClient`) - none (`NoAuthClient`) -the automatic client uses OAuth token stored in the environment varable `GITHUB_TOKEN` or git config `github.token`. +Local Github Config client uses OAuth token stored in the environment varable `GITHUB_TOKEN` or git config `github.token`. ### setting OAuth token to git config create token: -``` -$ curl -u 'username' -d '{"scopes":[]}' https://api.github.com/authorizations -Enter host password for user 'username': [type password] -{ - "app": { - "url": "http://developer.github.com/v3/oauth/#oauth-authorizations-api", - "name": "GitHub API" - }, - "token": "your_token", - "updated_at": "2012-12-08T22:13:41Z", - "url": "https://api.github.com/authorizations/123456", - "note_url": null, - "scopes": [ - ], - "note": null, - "created_at": "2012-12-08T22:13:41Z", - "id": 123456, -} -``` +1. [Account settings > Applications](https://github.com/settings/applications) +2. Personal access tokens > Generate new token + then save that token: @@ -65,65 +28,195 @@ then save that token: $ git config --global --add github.token your_token ``` -## request builders and response handlers - -repatch-github provides two categories of classes: request builders and response handlers. +now we can hit the GitHub API using the token: -request builders, when used in conjunction with authenticating client, build a `dispatch.Req` object. to distinguish from response case classes, these classes ends with `s` like `Repos`. - -response handlers helps you parse the response from github. - -### repos API using case class handler ```scala -scala> import dispatch._, repatch._, repatch.github._ +scala> import dispatch._, Defaults._, repatch.github.request._ import dispatch._ -import repatch._ -import repatch.github._ +import Defaults._ +import repatch.github.request._ + +scala> val http = new Http +http: dispatch.Http = Http(com.ning.http.client.AsyncHttpClient@70e54515) -scala> Http(Client(Repos("dispatch", "dispatch")) > as_github.Repo) -res0: dispatch.Promise[dispatch.github.Repo] = Promise(-incomplete-) +scala> val client = LocalConfigClient +client: repatch.github.request.LocalConfigClient.type = + +scala> http(client(repo("dispatch", "reboot")) > as.json4s.Json) +res0: dispatch.Future[org.json4s.JValue] = scala.concurrent.impl.Promise$DefaultPromise@136e6221 scala> res0() -res1: repatch.github.Repo = Repo(408372,Owner(1115066,Organization,dispatch,... +res1: org.json4s.JValue = JObject(List((id,JInt(2960515)), (name,JString(reboot)), (full_name,JString(dispatch/reboot))... ``` -### repos API using json handler +## general design + +following the ethos of Dispatch Reboot, repatch-github splits request hander and response handler apart. the request handler is generally responsible for constructing the request URL sometimes with additional parameters or HTTP headers. + +you can choose what format you would like Dispatch to return the response in. if you want raw string, you specify `as.String`. if you want json, you say `as.json4s.Json`. to assist the json parsing, repatch-github provides known field names as extractors under the companion object of the response classes. + +if you just want a case classes of commonly used fields, you say `as.repatch.github.response.Repo` etc, and it would give you `Repo` case class. + +## [repositories](https://developer.github.com/v3/repos/) + +here's to querying for a repository as Json. ```scala -scala> Http(Client(Repos("dispatch", "dispatch")) > as_github.Json) -res2: dispatch.Promise[net.liftweb.json.package.JValue] = Promise(-incomplete-) +scala> val x = http(client(repo("dispatch", "reboot")) > as.json4s.Json) +x: dispatch.Future[org.json4s.JValue] = scala.concurrent.impl.Promise$DefaultPromise@2be9d442 -scala> res2 map { js => - import Repo._ - full_name(js) +scala> val json = x() +json: org.json4s.JValue = JObject(List((id,JInt(2960515)), (name,JString(reboot)), (full_name,JString(dispatch/reboot)), (owner,JObject(List((login,JString(dispatch)), (id,JInt(1115066)), .... + +scala> { + import repatch.github.response.Repo._ + import Owner._ + login(owner(json)) } -res3: dispatch.Promise[Option[String]] = Promise(Some(dispatch/dispatch)) +res0: String = dispatch +``` + +here's the same query using case class response handler. + + +```scala +scala> val x = http(client(repo("dispatch", "reboot")) > as.repatch.github.response.Repo) +x: dispatch.Future[repatch.github.response.Repo] = scala.concurrent.impl.Promise$DefaultPromise@15447f19 + +scala> x() +res5: repatch.github.response.Repo = Repo(2960515,Owner(1115066,Organization,dispatch,https://avatars.githubusercontent.com/u/1115066?,c4050b114966f021d1d91d0b5baabd5c,... +``` + +## [references](https://developer.github.com/v3/git/refs/) -scala> res3().head -res4: String = dispatch/dispatch +> This will return an array of all the references on the system, including things like notes and stashes if they exist on the server. + +```scala +scala> val refs = http(client(repo("dispatch", "reboot").git_refs) > as.repatch.github.response.GitRefs) +refs: dispatch.Future[Seq[repatch.github.response.GitRef]] = scala.concurrent.impl.Promise$DefaultPromise@24a3332a + +scala> refs() +res1: Seq[repatch.github.response.GitRef] = List(GitRef(refs/heads/0.9.x,https://api.github.com/repos/dispatch/reboot/git/refs/heads/0.9.x,GitObject(e547dd41a38e5d3c40576d00c929b25fc6d333a6,.... ``` -### git blob API using string hander +> The ref in the URL must be formatted as `heads/branch`, not just `branch`. + +```scala +scala> val ref = http(client(repo("dispatch", "reboot").git_refs.heads("master")) > as.repatch.github.response.GitRef) +ref: dispatch.Future[repatch.github.response.GitRef] = scala.concurrent.impl.Promise$DefaultPromise@7e955067 + +scala> ref() +res2: repatch.github.response.GitRef = GitRef(refs/heads/master,https://api.github.com/repos/dispatch/reboot/git/refs/heads/master,... +``` + +## [commits](https://developer.github.com/v3/git/commits/) + +> `GET /repos/:owner/:repo/git/commits/:sha` ```scala -scala> val blob_sha = "fb4c8b459f05bcc5296d9c13a3f6757597786f1d" -blob_sha: java.lang.String = fb4c8b459f05bcc5296d9c13a3f6757597786f1d +scala> val commit = http(client(repo("dispatch", "reboot").git_commit("bcf6d255317088ca1e32c6e6ecd4dce1979ac718")) > as.repatch.github.response.GitCommit) +commit: dispatch.Future[repatch.github.response.GitCommit] = scala.concurrent.impl.Promise$DefaultPromise@34110e6b -scala> Http(Client(Repos("dispatch", "dispatch").git_blob(blob_sha).raw) > as.String) -res5: dispatch.Promise[String] = Promise(-incomplete-) +scala> commit() +res0: repatch.github.response.GitCommit = GitCommit(bcf6d255317088ca1e32c6e6ecd4dce1979ac718,https://api.github.com/repos/dispatch/reboot/git/commits/bcf6d255317088ca1e32c6e6ecd4dce1979ac718 +``` + +git reference can also be passed in. + +```scala +scala> val commit = for { + master <- http(client(repo("dispatch", "reboot").git_refs.heads("master")) > as.repatch.github.response.GitRef) + x <- http(client(repo("dispatch", "reboot").git_commit(master)) > as.repatch.github.response.GitCommit) + } yield x +commit: scala.concurrent.Future[repatch.github.response.GitCommit] = scala.concurrent.impl.Promise$DefaultPromise@39439e65 + +scala> commit() +res1: repatch.github.response.GitCommit = +GitCommit(28dbd9265dd9780124c1412f7f530684dab020ae,https://api.github.com/repos/dispatch/reboot/git/commits/28dbd9265dd9780124c1412f7f530684dab020ae, +``` + +## [trees](https://developer.github.com/v3/git/trees/) + +> `GET /repos/:owner/:repo/git/trees/:sha` + +```scala +scala> val trees = http(client(repo("dispatch", "reboot").git_trees("b1193d20d761654b7fc35a48cd64b53aedc7a697")) > as.repatch.github.response.GitTrees) +trees: dispatch.Future[Seq[repatch.github.response.GitTree]] = scala.concurrent.impl.Promise$DefaultPromise@73259adf + +scala> trees() +res8: Seq[repatch.github.response.GitTree] = List(GitTree(3baebe52555bc73ad1c9a94261c4552fb8d771cd,https://api.github.com/repos/dispatch/reboot/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd,.gitignore,100644,blob,Some(93)), ... +``` + +> Get a tree recursively + +```scala +scala> val trees = http(client(repo("dispatch", "reboot").git_trees("b1193d20d761654b7fc35a48cd64b53aedc7a697").recursive(10)) > as.repatch.github.response.GitTrees) +trees: dispatch.Future[Seq[repatch.github.response.GitTree]] = scala.concurrent.impl.Promise$DefaultPromise@b6c8874 + +scala> trees() +res9: Seq[repatch.github.response.GitTree] = List(GitTree(3baebe52555bc73ad1c9a94261c4552fb8d771cd,https://api.github.com/repos/dispatch/reboot/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd,.gitignore,100644,blob,Some(93)), ... +``` + +here's how to get a tree from a git commit. + +```scala +scala> val trees = for { + master <- http(client(repo("dispatch", "reboot").git_refs.heads("master")) > as.repatch.github.response.GitRef) + commit <- http(client(repo("dispatch", "reboot").git_commit(master)) > as.repatch.github.response.GitCommit) + x <- http(client(repo("dispatch", "reboot").git_trees(commit)) > as.repatch.github.response.GitTrees) + } yield x +trees: scala.concurrent.Future[Seq[repatch.github.response.GitTree]] = scala.concurrent.impl.Promise$DefaultPromise@3d333b3e + +scala> trees() +res10: Seq[repatch.github.response.GitTree] = List(GitTree(3baebe52555bc73ad1c9a94261c4552fb8d771cd,https://api.github.com/repos/dispatch/reboot/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd,.gitignore,100644,blob,Some(93)), ... +``` + +## [blobs](https://developer.github.com/v3/git/blobs/) + +> `GET /repos/:owner/:repo/git/blobs/:sha` + +```scala +scala> val blob = http(client(repo("dispatch", "reboot").git_blob(blob_sha)) > as.repatch.github.response.GitBlob) +blob: dispatch.Future[repatch.github.response.GitBlob] = scala.concurrent.impl.Promise$DefaultPromise@3943a8f9 + +scala> blob() +res11: repatch.github.response.GitBlob = +GitBlob(3baebe52555bc73ad1c9a94261c4552fb8d771cd,https://api.github.com/repos/dispatch/reboot/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd,base64,LmNsYXNzcGF0aAoucHJvamVjdAouc2V0dGluZ3MKdGFyZ2V0CnNjcmF0Y2gu +c2NhbGEKc3JjX21hbmFnZWQKKi50ZXN0LnByb3BlcnRpZXMKLmlkZWEKKi5p +bWwK +,93) + +scala> res11.as_utf8 +res12: String = +".classpath +.project +.settings +target +scratch.scala +src_managed +*.test.properties +.idea +*.iml +" +``` + +git the blob as raw. + +```scala +scala> http(client(repo("dispatch", "reboot").git_blob(blob_sha).raw) > as.String) +res13: dispatch.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@522c7ab9 -scala> res5() -res6: String = +scala> res13() +res14: String = ".classpath .project .settings target scratch.scala -lib_managed -project/boot -project/plugins/project src_managed *.test.properties +.idea +*.iml " ``` diff --git a/core/src/main/scala/as/package.scala b/core/src/main/scala/as/package.scala index d021beb..296bc70 100644 --- a/core/src/main/scala/as/package.scala +++ b/core/src/main/scala/as/package.scala @@ -1,33 +1,14 @@ -package repatch.as_github - -import dispatch._ -import repatch.github -import net.liftweb.json.{JsonParser, JValue} - -object Json extends (Res => JValue) { - def apply(r: Res) = (dispatch.as.String andThen JsonParser.parse)(r) -} - -object Repo extends (Res => github.Repo) { - def apply(r: Res) = (Json andThen github.Repo.fromJson)(r) -} - -object GitRef extends (Res => github.GitRef) { - def apply(r: Res) = (Json andThen github.GitRef.fromJson)(r) -} - -object GitRefs extends (Res => Seq[github.GitRef]) { - def apply(r: Res) = (Json andThen github.GitRefs.fromJson)(r) -} - -object GitCommit extends (Res => github.GitCommit) { - def apply(r: Res) = (Json andThen github.GitCommit.fromJson)(r) -} - -object GitTrees extends (Res => Seq[github.GitTree]) { - def apply(r: Res) = (Json andThen github.GitTrees.fromJson)(r) -} - -object GitBlob extends (Res => github.GitBlob) { - def apply(r: Res) = (Json andThen github.GitBlob.fromJson)(r) +package dispatch.as.repatch.github + +package object response { + import com.ning.http.client.Response + import repatch.github.{response => res} + import dispatch.as.json4s.Json + + val Repo: Response => res.Repo = Json andThen res.Repo.apply + val GitRefs: Response => Seq[res.GitRef] = Json andThen res.GitRefs.apply + val GitRef: Response => res.GitRef = Json andThen res.GitRef.apply + val GitCommit: Response => res.GitCommit = Json andThen res.GitCommit.apply + val GitTrees: Response => Seq[res.GitTree] = Json andThen res.GitTrees.apply + val GitBlob: Response => res.GitBlob = Json andThen res.GitBlob.apply } diff --git a/core/src/main/scala/auths.scala b/core/src/main/scala/auths.scala index 4b21136..c7559fa 100644 --- a/core/src/main/scala/auths.scala +++ b/core/src/main/scala/auths.scala @@ -1,33 +1,33 @@ -package repatch.github +package repatch.github.request import dispatch._ -import net.liftweb.json._ +import org.json4s._ import scala.util.control.Exception.allCatch -/** Client is a function to wrap API operations */ -abstract class AbstractClient extends ((Req => Req) => Req) { - def hostname = "api.github.com" - def host: Req - def apply(block: Req => Req): Req = block(host) +/** AbstractClient is a function to wrap API operations */ +abstract class AbstractClient extends (Method => Req) { + def hostName = "api.github.com" + def host = :/(hostName).secure + def apply(method: Method): Req = method(host) } case object NoAuthClient extends AbstractClient { - def host = :/(hostname).secure } case class BasicAuthClient(user: String, pass: String) extends AbstractClient { - def host = :/(hostname).secure as_! (user, pass) + override def host = :/(hostName).secure as_! (user, pass) } -case object Client extends AbstractClient { - def host = underlying.host - override def apply(block: Req => Req): Req = underlying.apply(block) - - lazy val underlying = token map { case (tkn) => - OAuthClient(tkn) } getOrElse NoAuthClient - - lazy val token: Option[(String)] = gitConfig("github.token") +/** OAuthClient using local github config (https://github.com/blog/180-local-github-config). + */ +case object LocalConfigClient extends AbstractClient { + override def host = underlying.host + override def apply(method: Method): Req = underlying.apply(method) + lazy val underlying = underlyingOpt getOrElse sys.error("Token was not found in local config!") + lazy val token: Option[String] = gitConfig("github.token") + lazy val underlyingOpt: Option[OAuthClient] = token map { OAuthClient } + // https://github.com/defunkt/gist/blob/master/lib/gist.rb#L237 def gitConfig(key: String): Option[String] = allCatch opt { @@ -36,37 +36,24 @@ case object Client extends AbstractClient { val reader = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream)) Option(reader.readLine) } - } getOrElse {None} + } getOrElse None +} + +case class OAuthClient(token: String) extends AbstractClient { + override def apply(method: Method): Req = method(host) <:< Map("Authorization" -> "bearer %s".format(token)) } object OAuth { def authorizations = :/("api.github.com").secure / "authorizations" - /** Fetches a new access token given a gh username and password + /* Fetches a new access token given a gh username and password * and optional list of scopes */ - def accessToken(user: String, pass: String, scopes: Seq[String] = Nil): Option[String] = { - val tok = Http(authorizations.POST.as_!(user, pass) << """{"scopes":[%s]}""".format( + def accessToken(user: String, pass: String, scopes: Seq[String] = Nil): Future[String] = { + import scala.concurrent.ExecutionContext.Implicits.global + Http(authorizations.POST.as_!(user, pass) << """{"scopes":[%s]}""".format( scopes.mkString("\"","\",","\"") - ) > as.lift.Json) map { _ \ "token" match { - case JString(tok) => Some(tok) - case _ => None + ) > as.json4s.Json) map { _ \ "token" match { + case JString(tok) => tok + case _ => sys.error("Token was not found!") }} - tok() - } -} - -case class OAuthClient(token: String) extends AbstractClient { - def host = :/(hostname).secure - override def apply(block: Req => Req): Req = block(host) <:< Map("Authorization" -> "bearer %s".format(token)) -} - -trait Method extends (Req => Req) { - def mime: Option[String] = Some("application/json") - def complete: Req => Req - def apply(req: Req): Req = { - val r = complete(req) - mime match { - case Some(x) => r <:< Map("Accept" -> x) - case _ => r - } } } diff --git a/core/src/main/scala/package.scala b/core/src/main/scala/package.scala new file mode 100644 index 0000000..684f636 --- /dev/null +++ b/core/src/main/scala/package.scala @@ -0,0 +1,5 @@ +package repatch.github + +package object request { + def repo(owner: String, name: String): Repos = Repos(owner, name) +} diff --git a/core/src/main/scala/requests.scala b/core/src/main/scala/requests.scala index 946af56..3b72e21 100644 --- a/core/src/main/scala/requests.scala +++ b/core/src/main/scala/requests.scala @@ -1,128 +1,23 @@ -package repatch.github +package repatch.github.request import dispatch._ -import net.liftweb.json._ +import org.json4s._ +import repatch.github.{response => res} -/** represents a github repository from the request-side */ -case class Repos(user: String, name: String) extends Method { +/** represents a github repository from the request-side + * @see https://developer.github.com/v3/repos/ + */ +case class Repos(owner: String, name: String) extends Method { def git_refs = GitRefs(this, None) - def git_commit(ref: GitRef): GitCommits = GitCommits(this, ref.git_object.sha) + def git_commit(ref: res.GitRef): GitCommits = GitCommits(this, ref.git_object.sha) def git_commit(sha: String): GitCommits = GitCommits(this, sha) - def git_trees(commit: GitCommit): GitTrees = GitTrees(this, commit.tree.sha) + def git_trees(commit: res.GitCommit): GitTrees = GitTrees(this, commit.tree.sha) def git_trees(sha: String): GitTrees = GitTrees(this, sha) def git_blob(sha: String): GitBlobs = GitBlobs(this, sha, None) - def complete = _ / "repos" / user / name + def complete = _ / "repos" / owner / name } -/** provides parsing support for a github repository response. - * @see http://developer.github.com/v3/repos/ - */ -object Repo extends Parsers { - def fromJson(json: JValue): Repo = Repo(json) - - def apply(json: JValue): Repo = - Repo(id = id(json).head, - owner = owner(json).head, - name = name(json).head, - full_name = full_name(json).head, - description = description(json).head, - `private` = `private`(json).head, - fork = fork(json).head, - url = url(json).head, - html_url = html_url(json).head, - clone_url = clone_url(json).head, - git_url = git_url(json).head, - ssh_url = ssh_url(json).head, - // svn_url = svn_url(json).head, - // mirror_url: Option[String], - homepage = homepage(json).head, - language = language(json), - // forks: BigInt, - forks_count = forks_count(json).head, - // watchers: BigInt, - watchers_count = watchers_count(json).head, - size = size(json).head, - master_branch = master_branch(json).head, - open_issues_count = open_issues_count(json).head, - pushed_at = pushed_at(json).head, - created_at = created_at(json).head, - updated_at = updated_at(json).head) - - val full_name = 'full_name ? str - val description = 'description ? str - val `private` = 'private ? bool - val fork = 'fork ? bool - val html_url = 'html_url ? str - val clone_url = 'clone_url ? str - val git_url = 'git_url ? str - val ssh_url = 'ssh_url ? str - val svn_url = 'svn_url ? str - val mirror_url = 'mirror_url ? str - val homepage = 'homepage ? str - val language = 'language ? str - val forks = 'forks ? int - val forks_count = 'forks_count ? int - val watchers = 'watchers ? int - val watchers_count = 'watchers_count ? int - val master_branch = 'master_branch ? str - val open_issues_count = 'open_issues_count ? int - val pushed_at = 'pushed_at ? iso8601datetime - - def owner(json: JValue): Option[Owner] = (for { JField("owner", v) <- json } yield Owner(v)).headOption - - object Owner extends Parsers { - val login = 'login ? str - val avatar_url = 'avatar_url ? str - val gravatar_id = 'gravatar_id ? str - - def apply(json: JValue): Owner = - Owner(id = id(json).head, - `type` = `type`(json).head, - login = login(json).head, - avatar_url = avatar_url(json).head, - gravatar_id = gravatar_id(json).head, - url = url(json).head) - } - - case class Owner(id: BigInt, - `type`: String, - login: String, - avatar_url: String, - gravatar_id: String, - url: String) -} - -/** represents repository response. - * @see http://developer.github.com/v3/repos/ - */ -case class Repo(id: BigInt, - owner: Repo.Owner, - name: String, - full_name: String, - description: String, - `private`: Boolean, - fork: Boolean, - url: String, - html_url: String, - clone_url: String, - git_url: String, - ssh_url: String, - // svn_url: String, - // mirror_url: Option[String], - homepage: String, - language: Option[String], - // forks: BigInt, - forks_count: BigInt, - // watchers: BigInt, - watchers_count: BigInt, - size: BigInt, - master_branch: String, - open_issues_count: BigInt, - pushed_at: java.util.Calendar, - created_at: java.util.Calendar, - updated_at: java.util.Calendar) - /** represents git blob request. * @see http://developer.github.com/v3/git/blobs/ */ @@ -134,94 +29,21 @@ case class GitBlobs(repo: Repos, sha: String, override val mime: Option[String]) } } -/** provides parsing support for a git blob response. */ -object GitBlob extends Parsers { - def fromJson(json: JValue): GitBlob = GitBlob(json) - - def apply(json: JValue): GitBlob = - GitBlob(sha = sha(json).head, - url = url(json).head, - encoding = encoding(json).head, - content = content(json).head, - size = size(json).head) -} - -/** represents git blob response. - * @see http://developer.github.com/v3/git/blobs/ - */ -case class GitBlob(sha: String, - url: String, - encoding: String, - content: String, - size: BigInt) { - def as_str(charset: String): String = - encoding match { - case "base64" => new String(bytes, charset) - case _ => content - } - - def as_utf8: String = as_str("UTF-8") - - def bytes: Array[Byte] = - encoding match { - case "utf-8" => content.getBytes - case "base64" => (new sun.misc.BASE64Decoder()).decodeBuffer(content) - } -} - -object GitRefs { - def fromJson(json: JValue): Seq[GitRef] = - for { - JArray(array) <- json - v <- array - } yield GitRef(v) -} - /** represents git reference request. * @see http://developer.github.com/v3/git/refs/ */ -case class GitRefs(repo: Repos, branch: Option[String]) extends Method { - def head(branch: String): GitRefs = copy(branch = Some(branch)) +case class GitRefs(repo: Repos, ref: Option[String]) extends Method { + def heads(branch: String): GitRefs = copy(ref = Some("heads/" + branch)) val complete = { req: Req => val request = repo.complete(req) / "git" / "refs" - branch match { - case Some(b) => request / "heads" / b + ref match { + case Some(r) => request / r case _ => request } } } -/** provides parsing support for a git reference response. */ -object GitRef extends Parsers { - def fromJson(json: JValue): GitRef = GitRef(json) - - def apply(json: JValue): GitRef = - GitRef(ref = ref(json).head, - url = url(json).head, - git_object = git_object(json).head) - - def git_object(json: JValue): Seq[GitObject] = for { JField("object", v) <- json } yield GitObject(v) - - object GitObject extends Parsers { - def apply(json: JValue): GitObject = - GitObject(sha = sha(json).head, - url = url(json).head, - `type` = `type`(json).head) - } - - case class GitObject(sha: String, - url: String, - `type`: String) -} - -/** represents git reference response. - * @see http://developer.github.com/v3/git/refs/ - */ -case class GitRef(ref: String, - url: String, - git_object: GitRef.GitObject) - /** represents git commit request. * @see http://developer.github.com/v3/git/commits/ */ @@ -229,68 +51,6 @@ case class GitCommits(repo: Repos, sha: String) extends Method { def complete = repo.complete(_) / "git" / "commits" / sha } -object GitCommit extends Parsers { - def fromJson(json: JValue): GitCommit = GitCommit(json) - - def apply(json: JValue): GitCommit = - GitCommit(sha = sha(json).head, - url = url(json).head, - author = author(json).head, - committer = committer(json).head, - message = message(json).head, - tree = tree(json).head, - parents = parents(json)) - - def author(json: JValue): Seq[GitUser] = for { JField("author", v) <- json } yield GitUser(v) - def committer(json: JValue): Seq[GitUser] = for { JField("committer", v) <- json } yield GitUser(v) - def tree(json: JValue): Seq[GitShaUrl] = for { JField("tree", v) <- json } yield GitShaUrl(v) - def parents(json: JValue): Seq[GitShaUrl] = - for { - JField("parents", JArray(a)) <- json - v <- a - } yield GitShaUrl(v) - - object GitUser { - def apply(json: JValue): GitUser = - GitUser(date = date(json).head, - name = name(json).head, - email = email(json).head) - } - - case class GitUser(date: java.util.Calendar, - name: String, - email: String) -} - -/** represents git commit response. - * @see http://developer.github.com/v3/git/commits/ - */ -case class GitCommit(sha: String, - url: String, - author: GitCommit.GitUser, - committer: GitCommit.GitUser, - message: String, - tree: GitShaUrl, - parents: Seq[GitShaUrl]) - -object GitShaUrl extends Parsers { - def fromJson(json: JValue): GitShaUrl = GitShaUrl(json) - - def apply(json: JValue): GitShaUrl = - GitShaUrl(sha = sha(json).head, - url = url(json).head) -} - -case class GitShaUrl(sha: String, - url: String) - -object GitTrees { - import Js._ - val tree = 'tree ? ary - - def fromJson(json: JValue): Seq[GitTree] = tree(json).getOrElse(Nil) map {GitTree(_)} -} - /** represents git tree request. * @see http://developer.github.com/v3/git/trees/ */ @@ -302,42 +62,14 @@ case class GitTrees(repo: Repos, sha: String, params: Map[String, String] = Map( def complete = repo.complete(_) / "git" / "trees" / sha < Req) { + def mime: Option[String] = Some("application/json") + def complete: Req => Req + def apply(req: Req): Req = { + val r = complete(req) + mime match { + case Some(x) => r <:< Map("Accept" -> x) + case _ => r + } + } } diff --git a/core/src/main/scala/responses.scala b/core/src/main/scala/responses.scala index d941516..6eec2da 100644 --- a/core/src/main/scala/responses.scala +++ b/core/src/main/scala/responses.scala @@ -1,74 +1,318 @@ -package repatch.github +package repatch.github.response import dispatch._ -import net.liftweb.json._ - -/** Json Extractor, extracts a value of type T from the given JsValue. */ -trait Extract[T] { - def unapply(js: JValue): Option[T] - override def toString = getClass.getName -} - -/** Namespace and context for Json extraction. Client extraction - objects, e.g. dispatch.twitter.Search, may extend this trait to - receive all implicit functions and values. */ -trait Js { - object str extends Extract[String] { - def unapply(js: JValue) = js match { - case JString(v) => Some(v) - case _ => None - } - } - object int extends Extract[BigInt] { - def unapply(js: JValue) = js match { - case JInt(v) => Some(v) - case _ => None - } - } - object bool extends Extract[Boolean] { - def unapply(js: JValue) = js match { - case JBool(v) => Some(v) - case _ => None - } - } - // keep in wrapped type to allow nested extractors - object obj extends Extract[JObject] { - def unapply(js: JValue) = js match { - case JObject(v) => Some(JObject(v)) - case _ => None - } - } - object ary extends Extract[List[JValue]] { - def unapply(js: JValue) = js match { - case JArray(v) => Some(v) - case _ => None - } - } - object iso8601datetime extends Extract[java.util.Calendar] { - def unapply(js: JValue) = js match { - case JString(v) => - Some(javax.xml.bind.DatatypeConverter.parseDateTime(v)) - case _ => None - } +import org.json4s._ +import java.util.{GregorianCalendar, Calendar, Locale} + +/** provides parsing support for a github repository response. + * @see http://developer.github.com/v3/repos/ + */ +object Repo extends Parse with CommonField { + def apply(json: JValue): Repo = + Repo(id = id(json), + owner = Owner(owner(json)), + name = name(json), + full_name = full_name(json), + description = description(json), + `private` = `private`(json), + fork = fork(json), + url = url(json), + html_url = html_url(json), + clone_url = clone_url(json), + git_url = git_url(json), + ssh_url = ssh_url(json), + // svn_url = svn_url(json).head, + // mirror_url: Option[String], + homepage = homepage(json), + language_opt = language_opt(json), + // forks: BigInt, + forks_count = forks_count(json), + // watchers: BigInt, + watchers_count = watchers_count(json), + size = size(json), + master_branch = master_branch(json), + open_issues_count = open_issues_count(json), + pushed_at = pushed_at(json), + created_at = created_at(json), + updated_at = updated_at(json)) + + val full_name = 'full_name.![String] + val description = 'description.![String] + val `private` = 'private.![Boolean] + val fork = 'fork.![Boolean] + val html_url = 'html_url.![String] + val clone_url = 'clone_url.![String] + val git_url = 'git_url.![String] + val ssh_url = 'ssh_url.![String] + val svn_url = 'svn_url.![String] + val mirror_url_opt = 'mirror_url.?[String] + val homepage = 'homepage.![String] + val language = 'language.![String] + val language_opt = 'language.?[String] + val forks = 'forks.![BigInt] + val forks_count = 'forks_count.![BigInt] + val watchers = 'watchers.![BigInt] + val watchers_count = 'watchers_count.![BigInt] + val master_branch = 'master_branch.![String] + val open_issues_count = 'open_issues_count.![BigInt] + val owner = 'owner.![JObject] + val pushed_at = 'pushed_at.![Calendar] +} + +/** represents repository response. + * @see http://developer.github.com/v3/repos/ + */ +case class Repo(id: BigInt, + owner: Owner, + name: String, + full_name: String, + description: String, + `private`: Boolean, + fork: Boolean, + url: String, + html_url: String, + clone_url: String, + git_url: String, + ssh_url: String, + // svn_url: String, + // mirror_url: Option[String], + homepage: String, + language_opt: Option[String], + // forks: BigInt, + forks_count: BigInt, + // watchers: BigInt, + watchers_count: BigInt, + size: BigInt, + master_branch: String, + open_issues_count: BigInt, + pushed_at: java.util.Calendar, + created_at: java.util.Calendar, + updated_at: java.util.Calendar) + +object Owner extends Parse with CommonField { + val login = 'login.![String] + val avatar_url = 'avatar_url.![String] + val gravatar_id = 'gravatar_id.![String] + + def apply(json: JValue): Owner = + Owner(id = id(json), + `type` = `type`(json), + login = login(json), + avatar_url = avatar_url(json), + gravatar_id = gravatar_id(json), + url = url(json)) +} + +case class Owner(id: BigInt, + `type`: String, + login: String, + avatar_url: String, + gravatar_id: String, + url: String) + +object GitRefs { + def apply(json: JValue): Seq[GitRef] = + for { + JArray(array) <- json + v <- array + } yield GitRef(v) +} + +/** represents git reference response. + * @see http://developer.github.com/v3/git/refs/ + */ +case class GitRef(ref: String, + url: String, + git_object: GitObject) + +/** provides parsing support for a git reference response. */ +object GitRef extends Parse with CommonField { + def apply(json: JValue): GitRef = { + val x = GitObject(git_object(json)) + GitRef(ref = ref(json), + url = url(json), + git_object = x) } + + val git_object = 'object.![JObject] +} - /** Add operators to Symbol. */ - implicit def sym_add_operators[T](sym: Symbol) = new SymOp(sym) +object GitObject extends Parse with CommonField { + def apply(json: JValue): GitObject = + GitObject(sha = sha(json), + url = url(json), + `type` = `type`(json)) +} + +case class GitObject(sha: String, + url: String, + `type`: String) - /** For ! and ? on Symbol. */ - case class SymOp(sym: Symbol) { - /** @return an extractor */ - def ? [T](cst: Extract[T]): JValue => Option[T] = { js => - cst.unapply(js \ sym.name) - } +object GitCommit extends Parse with CommonField { + def apply(json: JValue): GitCommit = + GitCommit(sha = sha(json), + url = url(json), + author = GitUser(author(json)), + committer = GitUser(committer(json)), + message = message(json), + tree = GitShaUrl(tree(json)), + parents = parents(json) map {GitShaUrl.apply}) + + val author = 'author.![JObject] + val committer = 'committer.![JObject] + val tree = 'tree.![JObject] + val parents = 'parents.![List[JValue]] +} + +/** represents git commit response. + * @see http://developer.github.com/v3/git/commits/ + */ +case class GitCommit(sha: String, + url: String, + author: GitUser, + committer: GitUser, + message: String, + tree: GitShaUrl, + parents: Seq[GitShaUrl]) + +object GitUser extends Parse with CommonField { + def apply(json: JValue): GitUser = + GitUser(date = date(json), + name = name(json), + email = email(json)) +} + +case class GitUser(date: java.util.Calendar, + name: String, + email: String) + +object GitShaUrl extends Parse with CommonField { + def apply(json: JValue): GitShaUrl = + GitShaUrl(sha = sha(json), + url = url(json)) +} + +case class GitShaUrl(sha: String, + url: String) + +object GitTrees extends Parse { + val tree = 'tree.![List[JValue]] + + def apply(json: JValue): Seq[GitTree] = tree(json) map GitTree.apply +} + +object GitTree extends Parse with CommonField { + def apply(json: JValue): GitTree = + GitTree(sha = sha(json), + url = url(json), + path = path(json), + mode = mode(json), + `type` = `type`(json), + size_opt = size_opt(json)) +} + +/** represents git tree response + * @see http://developer.github.com/v3/git/trees/ + */ +case class GitTree(sha: String, + url: String, + path: String, + mode: String, + `type`: String, + size_opt: Option[BigInt]) + +/** provides parsing support for a git blob response. */ +object GitBlob extends Parse with CommonField { + def apply(json: JValue): GitBlob = + GitBlob(sha = sha(json), + url = url(json), + encoding = encoding(json), + content = content(json), + size = size(json)) +} + +/** represents git blob response. + * @see http://developer.github.com/v3/git/blobs/ + */ +case class GitBlob(sha: String, + url: String, + encoding: String, + content: String, + size: BigInt) { + def as_str(charset: String): String = + encoding match { + case "base64" => new String(bytes, charset) + case _ => content + } - /** @return an assertion extracting function */ - def ! [T](cst: Extract[T]): JValue => T = { js => - cst.unapply(js \ sym.name).get + def as_utf8: String = as_str("UTF-8") + + def bytes: Array[Byte] = + encoding match { + case "utf-8" => content.getBytes + case "base64" => (new sun.misc.BASE64Decoder()).decodeBuffer(content) } - } } -object Js extends Js { +trait CommonField { self: Parse => + val id = 'id.![BigInt] + val sha = 'sha.![String] + val url = 'url.![String] + val ref = 'ref.![String] + val path = 'path.![String] + val mode = 'mode.![String] + val `type` = 'type.![String] + val size = 'size.![BigInt] + val size_opt = 'size.?[BigInt] + val message = 'message.![String] + val name = 'name.![String] + val email = 'email.![String] + val date = 'date.![Calendar] + val created_at = 'created_at.![Calendar] + val updated_at = 'updated_at.![Calendar] + val encoding = 'encoding.![String] + val content = 'content.![String] +} + +trait Parse { + def parse[A: ReadJs](js: JValue): Option[A] = + implicitly[ReadJs[A]].readJs.lift(js) + def parse_![A: ReadJs](js: JValue): A = parse(js).get + def parseField[A: ReadJs](key: String)(js: JValue): Option[A] = parse[A](js \ key) + def parseField_![A: ReadJs](key: String)(js: JValue): A = parseField(key)(js).get + implicit class SymOp(sym: Symbol) { + def ?[A: ReadJs](js: JValue): Option[A] = parseField[A](sym.name)(js) + def ?[A: ReadJs]: JValue => Option[A] = ?[A](_) + def ![A: ReadJs]: JValue => A = parseField_![A](sym.name)_ + } +} + +trait ReadJs[A] { + import ReadJs.=>? + val readJs: JValue =>? A +} +object ReadJs { + type =>?[-A, +B] = PartialFunction[A, B] + def readJs[A](pf: JValue =>? A): ReadJs[A] = new ReadJs[A] { + val readJs = pf + } + implicit val listRead: ReadJs[List[JValue]] = readJs { case JArray(v) => v } + implicit val objectRead: ReadJs[JObject] = readJs { case JObject(v) => JObject(v) } + implicit val bigIntRead: ReadJs[BigInt] = readJs { case JInt(v) => v } + implicit val intRead: ReadJs[Int] = readJs { case JInt(v) => v.toInt } + implicit val stringRead: ReadJs[String] = readJs { case JString(v) => v } + implicit val boolRead: ReadJs[Boolean] = readJs { case JBool(v) => v } + implicit val calendarRead: ReadJs[Calendar] = + readJs { case JString(v) => + // iso8601s + javax.xml.bind.DatatypeConverter.parseDateTime(v) + } + implicit def readJsListRead[A: ReadJs]: ReadJs[List[A]] = { + val f = implicitly[ReadJs[A]].readJs + readJs { + case JArray(xs) if xs forall {f.isDefinedAt} => + xs map {f.apply} + } + } } diff --git a/core/src/test/scala/githubspec.scala b/core/src/test/scala/githubspec.scala index 4d33438..a204210 100644 --- a/core/src/test/scala/githubspec.scala +++ b/core/src/test/scala/githubspec.scala @@ -1,160 +1,184 @@ +package repatch.github + import org.specs2._ import org.specs2.matcher._ -import dispatch._ -import repatch._ -import github._ - -class GithubSpec extends Specification { def is = sequential ^ - "This is a specification to check the github handler" ^ - p^ - "`Respos(user, repo)` should" ^ - "return a json object that can be parsed using `as_github.Repo`" ! repos1^ - "return a json object that can be parsed manually" ! repos2^ - p^ - "`git_refs` should" ^ - "return a json array that can be parsed using `as_github.GitRefs`" ! references1^ - p^ - "`git_refs.head(\"master\")` should" ^ - "return a json object that can be parsed using `as_github.GitRef`" ! reference1^ - p^ - "`git_commit(:sha)` should" ^ - "return a json object that can be parsed using `as_github.GitCommit`" ! commit1^ - "return a json object that can be parsed manually" ! commit2^ - p^ - "`git_commit(git_ref)` should" ^ - "return a commit json object for the given `GitRef`" ! commit3^ - p^ - "`git_trees(:sha)` should" ^ - "return a json object that can be parsed using `as_github.GitTrees`" ! trees1^ - p^ - "`git_trees(:sha).recursive(10)` should" ^ - "return a json object that contains subdir blobs" ! recursive1^ - p^ - "`git_trees(commit)` should" ^ - "return a tree json object for the given `GitCommit`" ! trees2^ - p^ - "`git_blob(:sha)` should" ^ - "return a json object that can be parsed using `as_github.GitBlob`" ! blob1^ - p^ - "`git_blob(:sha).raw` should" ^ - "return raw blob bytes" ! raw1^ - end - def repos1: MatchResult[Any] = { - // `Client(Repos(user, repo)` constructs a request to - // https://api.github.com/repos/dispatch/dispatch - // Returned json object can then be parsed using `as_github.Repo`, - // which returns a Repo case class - val repos = Http(Client(Repos(user, repo)) > as_github.Repo) +import dispatch._, Defaults._ +import repatch.github.request._ +import org.json4s._ - repos().full_name must_== "dispatch/dispatch" - } +class GithubSpec extends Specification { def is = args(sequential = true) ^ s2""" + This is a specification to check the github handler - def repos2: MatchResult[Any] = { + `repo(owner, repo)` should + return a json object that can be parsed manually ${repos1} + return a json object that can be parsed with extractors ${repos2} + return a json object that can be parsed using `Repo`" ${repos3} + + `git_refs` should + return a json array that can be parsed using `GitRefs` ${references1} + + `git_refs.heads(\"master\")` should + return a json object that can be parsed using `GitRef` ${references2} + + `git_commit(:sha)` should + return a json object that can be parsed using `GitCommit` ${commit1} + return a json object that can be parsed manually ${commit2} + + `git_commit(git_ref)` should + return a commit json object for the given `GitRef` ${commit3} + + `git_trees(:sha)` should + return a json object that can be parsed using `GitTrees` ${trees1} + + `git_trees(:sha).recursive(10)` should + return a json object that contains subdir blobs ${recursive1} + + `git_trees(commit)` should + return a tree json object for the given `GitCommit` ${trees2} + + `git_blob(:sha)` should + return a json object that can be parsed using `GitBlob` ${blob1} + + `git_blob(:sha).raw` should + return raw blob bytes" ${raw1} + """ + + + lazy val http = new Http + lazy val client = LocalConfigClient + val user = "dispatch" + val name = "reboot" + val tree_sha = "b1193d20d761654b7fc35a48cd64b53aedc7a697" + val commit_sha = "bcf6d255317088ca1e32c6e6ecd4dce1979ac718" + val blob_sha = "3baebe52555bc73ad1c9a94261c4552fb8d771cd" + + def repos1 = { + // `client(repo(user, name))` constructs a request to + // https://api.github.com/repos/dispatch/reboot + // Returned json object + val x = http(client(repo(user, name)) > as.json4s.Json) + val login = x map { json => + for { + JObject(fs) <- json + JField("owner", JObject(owner)) <- fs + JField("login", JString(login)) <- owner + } yield login + } + login().headOption must_== Some("dispatch") + } + + def repos2 = { // Returned json object can also be parsed field-by-field using an extractor - val json = Http(Client(Repos(user, repo)) > as_github.Json) - val o = json map { js => - import Repo._ - owner(js) + val x = http(client(repo(user, name)) > as.json4s.Json) + val o = x map { json => + import repatch.github.response.Repo._ + owner(json) + } + { + import repatch.github.response.Owner._ + login(o()) must_== "dispatch" } - o().head.login must_== "dispatch" } - def references1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_refs)` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/refs - // Returned json array can then be parsed using `as_github.GitRefs`, + def repos3 = { + // Returned json object can then be parsed using `as.repatch.github.response.Repo`, + // which returns a Repo case class + val repos = http(client(repo(user, name)) > as.repatch.github.response.Repo) + + repos().full_name must_== "dispatch/reboot" + } + + def references1 = { + // `client(repos(user, repo).git_refs)` constructs a request to + // https://api.github.com/repos/dispatch/reboot/git/refs + // Returned json array can then be parsed using `GitRefs`, // which returns a seqence of GitRef case classes - val refs = Http(Client(Repos(user, repo).git_refs) > as_github.GitRefs) + val refs = http(client(repo(user, name).git_refs) > as.repatch.github.response.GitRefs) val master = (refs() find {_.ref == "refs/heads/master"}).head master.git_object.`type` must_== "commit" } - - def reference1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_refs.head("master")` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/refs/heads/master - // Returned json object can then be parsed using `as_github.GitRef`, - // which returns a GitRef case class - val master = Http(Client(Repos(user, repo).git_refs.head("master")) > as_github.GitRef) - master().git_object.`type` must_== "commit" + + def references2 = { + val ref = http(client(repo(user, name).git_refs.heads("master")) > as.repatch.github.response.GitRef) + val master = ref() + master.ref must_== "refs/heads/master" } + - def commit1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_commit(commit_sha))` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/commits/02d638afcd5b155a335db2e8262ffd852290c17c - // Returned json object can then be parsed using `as_github.GitCommit`, + def commit1 = { + // `client(repos(user, name).git_commit(commit_sha))` constructs a request to + // https://api.github.com/repos/dispatch/reboot/git/commits/bcf6d255317088ca1e32c6e6ecd4dce1979ac718 + // Returned json object can then be parsed using `GitCommit`, // which returns a GitCommit case class - val commit = Http(Client(Repos(user, repo).git_commit(commit_sha)) > as_github.GitCommit) + val commit = http(client(repo(user, name).git_commit(commit_sha)) > as.repatch.github.response.GitCommit) commit().committer.name must_== "softprops" } - def commit2: MatchResult[Any] = { + def commit2 = { // Returned json object can also be parsed field-by-field using an extractor - val json = Http(Client(Repos(user, repo).git_commit(commit_sha)) > as_github.Json) + val json = http(client(repo(user, name).git_commit(commit_sha)) > as.json4s.Json) val msg = json map { js => - import GitCommit._ + import repatch.github.response.GitCommit._ message(js) } - msg().head.startsWith("add") must_== true + msg().startsWith("send") must_== true } - def commit3: MatchResult[Any] = { + def commit3 = { // this returns a GitRef case class - val master = Http(Client(Repos(user, repo).git_refs.head("master")) > as_github.GitRef) + val master = http(client(repo(user, name).git_refs.heads("master")) > as.repatch.github.response.GitRef) // this returns a GitCommit case class - val commit = Http(Client(Repos(user, repo).git_commit(master())) > as_github.GitCommit) + val commit = http(client(repo(user, name).git_commit(master())) > as.repatch.github.response.GitCommit) commit().sha must_== master().git_object.sha } - def trees1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_trees(tree_sha))` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/trees/563c7dcea4bbb71e49313e92c01337a0a4b7ce72 - // Returned json object can then be parsed using `as_github.GitTrees`, + def trees1 = { + // `client(repos(user, name).git_trees(tree_sha))` constructs a request to + // https://api.github.com/repos/dispatch/reboot/git/trees/563c7dcea4bbb71e49313e92c01337a0a4b7ce72 + // Returned json object can then be parsed using `GitTrees`, // which returns a seqence of GitTree case class - val trees = Http(Client(Repos(user, repo).git_trees(tree_sha)) > as_github.GitTrees) - trees() must have (_.path == ".gitignore") + val trees = http(client(repo(user, name).git_trees(tree_sha)) > as.repatch.github.response.GitTrees) + import repatch.github.response.GitTree + trees() must contain { tree: GitTree => tree.path must be_==(".gitignore") } + } + + def recursive1 = { + // this returns a sequence of GitTree case class + val trees = http(client(repo(user, name).git_trees(tree_sha).recursive(10)) > as.repatch.github.response.GitTrees) + import repatch.github.response.GitTree + trees() must contain { tree: GitTree => tree.path must be_==("core/src/main/scala/retry/retries.scala") } } - def trees2: MatchResult[Any] = { + def trees2 = { // this returns a GitCommit case class - val commit = Http(Client(Repos(user, repo).git_commit(commit_sha)) > as_github.GitCommit) + val commit = http(client(repo(user, name).git_commit(commit_sha)) > as.repatch.github.response.GitCommit) // this returns a seqence of GitTree case class - val trees = Http(Client(Repos(user, repo).git_trees(commit())) > as_github.GitTrees) - trees() must have (_.path == ".gitignore") - } - - def recursive1: MatchResult[Any] = { - // this returns a sequence of GitTree case class - val trees = Http(Client(Repos(user, repo).git_trees(tree_sha).recursive(10)) > as_github.GitTrees) - trees() must have (_.path == "twitter/src/main/scala/dispatch/Twitter.scala") + val trees = http(client(repo(user, name).git_trees(commit())) > as.repatch.github.response.GitTrees) + import repatch.github.response.GitTree + trees() must contain { tree: GitTree => tree.path must be_==(".gitignore") } } - - def blob1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_blob(blob_sha))` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/blobs/fb4c8b459f05bcc5296d9c13a3f6757597786f1d - // Returned json object can then be parsed using `as_github.GitBlob`, + + def blob1 = { + // `client(repos(user, name).git_blob(blob_sha))` constructs a request to + // https://api.github.com/repos/dispatch/reboot/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd + // Returned json object can then be parsed using `GitBlob`, // which returns a GitBlob case class - val blob = Http(Client(Repos(user, repo).git_blob(blob_sha)) > as_github.GitBlob) + val blob = http(client(repo(user, name).git_blob(blob_sha)) > as.repatch.github.response.GitBlob) // `as_utf8` method makes the assumption that the contained content is encoded in UTF-8. (blob().as_utf8 startsWith ".classpath") must_== true } - def raw1: MatchResult[Any] = { - // `Client(Repos(user, repo).git_blob(blob_sha).raw)` constructs a request to - // https://api.github.com/repos/dispatch/dispatch/git/blobs/fb4c8b459f05bcc5296d9c13a3f6757597786f1d + def raw1 = { + // `client(repo(user, name).git_blob(blob_sha).raw)` constructs a request to + // https://api.github.com/repos/dispatch/repatch/git/blobs/3baebe52555bc73ad1c9a94261c4552fb8d771cd // with "application/vnd.github.raw" as http Accept header. // This returns raw bytes. You are responsible for figuring out the charset. - val raw = Http(Client(Repos(user, repo).git_blob(blob_sha).raw) > as.String) + val raw = http(client(repo(user, name).git_blob(blob_sha).raw) > as.String) (raw() startsWith ".classpath") must_== true } - - val user = "dispatch" - val repo = "dispatch" - val tree_sha = "563c7dcea4bbb71e49313e92c01337a0a4b7ce72" - val commit_sha = "02d638afcd5b155a335db2e8262ffd852290c17c" - val blob_sha = "fb4c8b459f05bcc5296d9c13a3f6757597786f1d" }