From 647e9de7375003f484f41c124bcad2ab6d5afef8 Mon Sep 17 00:00:00 2001 From: Chris Rebert Date: Fri, 15 May 2015 20:11:42 -0700 Subject: [PATCH] egit-github => jcabi-github; fixes #98 --- build.sbt | 5 +--- .../lmvtfy/github/Credentials.scala | 9 +++++++ .../lmvtfy/github/GitHubIssueCommenter.scala | 16 +++++------- .../lmvtfy/github/GitHubJsonProtocol.scala | 8 +++--- .../lmvtfy/github/implicits/package.scala | 10 ++++++++ .../chrisrebert/lmvtfy/http/SuperWire.scala | 20 +++++++++++++++ .../chrisrebert/lmvtfy/http/UserAgent.scala | 3 +++ .../lmvtfy/http/UserAgentWire.scala | 25 +++++++++++++++++++ .../server/IssueCommentEventHandler.scala | 2 +- .../lmvtfy/server/LmvtfyActor.scala | 1 - .../chrisrebert/lmvtfy/server/Settings.scala | 13 +++++++--- 11 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 src/main/scala/com/chrisrebert/lmvtfy/github/Credentials.scala create mode 100644 src/main/scala/com/chrisrebert/lmvtfy/github/implicits/package.scala create mode 100644 src/main/scala/com/chrisrebert/lmvtfy/http/SuperWire.scala create mode 100644 src/main/scala/com/chrisrebert/lmvtfy/http/UserAgent.scala create mode 100644 src/main/scala/com/chrisrebert/lmvtfy/http/UserAgentWire.scala diff --git a/build.sbt b/build.sbt index 8fcfeb1..858c36b 100644 --- a/build.sbt +++ b/build.sbt @@ -19,10 +19,7 @@ libraryDependencies += "nu.validator" % "validator" % "15.4.12" excludeAll( ExclusionRule(organization = "commons-httpclient") ) -libraryDependencies += "org.eclipse.mylyn.github" % "org.eclipse.egit.github.core" % "4.0.0.201503231230-m1" - -// egit-github needs Gson, but doesn't explicitly require it -libraryDependencies += "com.google.code.gson" % "gson" % "2.3.1" +libraryDependencies += "com.jcabi" % "jcabi-github" % "0.23" libraryDependencies += "com.twitter" % "twitter-text" % "1.11.1" diff --git a/src/main/scala/com/chrisrebert/lmvtfy/github/Credentials.scala b/src/main/scala/com/chrisrebert/lmvtfy/github/Credentials.scala new file mode 100644 index 0000000..c889111 --- /dev/null +++ b/src/main/scala/com/chrisrebert/lmvtfy/github/Credentials.scala @@ -0,0 +1,9 @@ +package com.chrisrebert.lmvtfy.github + +import com.jcabi.github.{Github, RtGithub} +import com.chrisrebert.lmvtfy.http.{UserAgent, SuperWire} + +case class Credentials(username: String, password: String) { + private def basicGithub: Github = new RtGithub(username, password) + def github(threshold: Int)(implicit userAgent: UserAgent): Github = new RtGithub(basicGithub.entry.through(classOf[SuperWire], userAgent, new Integer(threshold))) +} diff --git a/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubIssueCommenter.scala b/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubIssueCommenter.scala index 2122640..924b19e 100644 --- a/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubIssueCommenter.scala +++ b/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubIssueCommenter.scala @@ -1,24 +1,20 @@ package com.chrisrebert.lmvtfy.github import scala.util.{Try,Failure,Success} -import org.eclipse.egit.github.core.client.GitHubClient -import org.eclipse.egit.github.core.service.IssueService -import org.eclipse.egit.github.core.RepositoryId +import com.jcabi.github.Coordinates.{Simple=>RepoId} import com.chrisrebert.lmvtfy.{MarkdownAboutHtml, MarkdownAboutBootstrap, Markdown, ValidationResult} import com.chrisrebert.lmvtfy.live_examples.{LiveExampleMention, LiveExample} +import com.chrisrebert.lmvtfy.github.implicits._ import com.chrisrebert.lmvtfy.server.{ActorWithLogging, Settings} class GitHubIssueCommenter extends ActorWithLogging { val settings = Settings(context.system) - private val client = new GitHubClient() - client.setUserAgent(settings.UserAgent) - client.setCredentials(settings.BotUsername, settings.BotPassword) + private val client = settings.github() - private def tryToCommentOn(repo: RepositoryId, issue: IssueNumber, commentMarkdown: String) = { - val issueService = new IssueService(client) - Try { issueService.createComment(repo, issue.number, commentMarkdown) } + private def tryToCommentOn(repo: RepoId, issue: IssueNumber, commentMarkdown: String) = { + Try { client.repos.get(repo).issues.get(issue.number).comments.post(commentMarkdown) } } private def introFor(markdown: Markdown): String = { @@ -45,7 +41,7 @@ class GitHubIssueCommenter extends ActorWithLogging { """.stripMargin tryToCommentOn(repo.id, issue, commentMarkdown) match { - case Success(comment) => log.info(s"Successfully posted comment ${comment.getUrl} for ${mention}") + case Success(comment) => log.info(s"Successfully posted comment ${comment.smart.url} for ${mention}") case Failure(exc) => log.error(exc, s"Error posting comment for ${mention}") } } diff --git a/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubJsonProtocol.scala b/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubJsonProtocol.scala index f59674c..1d4ca14 100644 --- a/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubJsonProtocol.scala +++ b/src/main/scala/com/chrisrebert/lmvtfy/github/GitHubJsonProtocol.scala @@ -1,12 +1,12 @@ package com.chrisrebert.lmvtfy.github import spray.json._ -import org.eclipse.egit.github.core.RepositoryId +import com.jcabi.github.Coordinates.{Simple=>RepoId} -case class GitHubRepository(val fullName: String) extends AnyVal { - def id: RepositoryId = RepositoryId.createFromId(fullName) +case class GitHubRepository(fullName: String) extends AnyVal { + def id: RepoId = new RepoId(fullName) } -case class GitHubUser(val username: String) extends AnyVal +case class GitHubUser(username: String) extends AnyVal case class IssueOrComment( number: Option[Int], // issue number body: String, diff --git a/src/main/scala/com/chrisrebert/lmvtfy/github/implicits/package.scala b/src/main/scala/com/chrisrebert/lmvtfy/github/implicits/package.scala new file mode 100644 index 0000000..560b3aa --- /dev/null +++ b/src/main/scala/com/chrisrebert/lmvtfy/github/implicits/package.scala @@ -0,0 +1,10 @@ +package com.chrisrebert.lmvtfy.github + +import com.jcabi.github.Comment +import com.jcabi.github.Comment.{Smart=>SmartComment} + +package object implicits { + implicit class RichComment(comment: Comment) { + def smart: SmartComment = new SmartComment(comment) + } +} diff --git a/src/main/scala/com/chrisrebert/lmvtfy/http/SuperWire.scala b/src/main/scala/com/chrisrebert/lmvtfy/http/SuperWire.scala new file mode 100644 index 0000000..6d5d178 --- /dev/null +++ b/src/main/scala/com/chrisrebert/lmvtfy/http/SuperWire.scala @@ -0,0 +1,20 @@ +package com.chrisrebert.lmvtfy.http + +import java.util.{Collection=>JavaCollection} +import java.util.Map.{Entry=>MapEntry} +import java.io.InputStream +import com.jcabi.github.wire.RetryCarefulWire +import com.jcabi.http.{Wire,Request,Response} + +case class SuperWire(private val wire: Wire, userAgent: UserAgent, threshold: Int) extends Wire { + private val wrappedWire = UserAgentWire(wire = new RetryCarefulWire(wire, threshold), userAgent = userAgent) + + @Override + def send( + request: Request, + home: String, + method: String, + headers: JavaCollection[MapEntry[String, String]], + content: InputStream + ): Response = wrappedWire.send(request, home, method, headers, content) +} diff --git a/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgent.scala b/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgent.scala new file mode 100644 index 0000000..d52b74c --- /dev/null +++ b/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgent.scala @@ -0,0 +1,3 @@ +package com.chrisrebert.lmvtfy.http + +case class UserAgent(userAgent: String) diff --git a/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgentWire.scala b/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgentWire.scala new file mode 100644 index 0000000..f4a5d02 --- /dev/null +++ b/src/main/scala/com/chrisrebert/lmvtfy/http/UserAgentWire.scala @@ -0,0 +1,25 @@ +package com.chrisrebert.lmvtfy.http + +import java.util.{Collection=>JavaCollection} +import java.util.Map.{Entry=>MapEntry} +import java.io.InputStream +import scala.collection.JavaConverters._ +import com.jcabi.http._ + +object UserAgentWire { + private val userAgentHeader = "User-Agent" +} +case class UserAgentWire(private val wire: Wire, userAgent: UserAgent) extends Wire { + @Override + def send( + request: Request, + home: String, + method: String, + headers: JavaCollection[MapEntry[String, String]], + content: InputStream + ): Response = { + val header = new ImmutableHeader(UserAgentWire.userAgentHeader, userAgent.userAgent) + val newHeaders = header +: headers.asScala.filter{ _.getKey != UserAgentWire.userAgentHeader}.toSeq + wire.send(request, home, method, newHeaders.asJava, content) + } +} diff --git a/src/main/scala/com/chrisrebert/lmvtfy/server/IssueCommentEventHandler.scala b/src/main/scala/com/chrisrebert/lmvtfy/server/IssueCommentEventHandler.scala index 64a9a2f..50a7ea7 100644 --- a/src/main/scala/com/chrisrebert/lmvtfy/server/IssueCommentEventHandler.scala +++ b/src/main/scala/com/chrisrebert/lmvtfy/server/IssueCommentEventHandler.scala @@ -15,7 +15,7 @@ class IssueCommentEventHandler(fetcher: ActorRef) extends ActorWithLogging { override def receive = { case event: IssueOrCommentEvent => { - if (settings.RepoFullNames contains event.repository.fullName) { + if (settings.RepoIds contains event.repository.id) { event.gitHubIssue.map { issue => event.message.map { message => if (message.user.username == settings.BotUsername) { diff --git a/src/main/scala/com/chrisrebert/lmvtfy/server/LmvtfyActor.scala b/src/main/scala/com/chrisrebert/lmvtfy/server/LmvtfyActor.scala index 614b1cc..b89f53f 100644 --- a/src/main/scala/com/chrisrebert/lmvtfy/server/LmvtfyActor.scala +++ b/src/main/scala/com/chrisrebert/lmvtfy/server/LmvtfyActor.scala @@ -29,7 +29,6 @@ class LmvtfyActor(protected val issueCommentEventHandler: ActorRef) extends Acto event.message match { case Some(_) => { issueCommentEventHandler ! event - // FIXME: do throttling // FIXME: ignore examples already posted in previous comments complete(StatusCodes.OK) } diff --git a/src/main/scala/com/chrisrebert/lmvtfy/server/Settings.scala b/src/main/scala/com/chrisrebert/lmvtfy/server/Settings.scala index 710ffb6..ef12b4a 100644 --- a/src/main/scala/com/chrisrebert/lmvtfy/server/Settings.scala +++ b/src/main/scala/com/chrisrebert/lmvtfy/server/Settings.scala @@ -8,14 +8,21 @@ import akka.actor.ExtensionId import akka.actor.ExtensionIdProvider import akka.actor.ExtendedActorSystem import akka.util.ByteString +import com.jcabi.github.Github +import com.jcabi.github.Coordinates.{Simple=>RepoId} +import com.chrisrebert.lmvtfy.github.Credentials +import com.chrisrebert.lmvtfy.http.{UserAgent=>UA} import com.chrisrebert.lmvtfy.util.Utf8String class SettingsImpl(config: Config) extends Extension { - val RepoFullNames: Set[String] = config.getStringList("lmvtfy.github-repos-to-watch").toSet + val RepoIds: Set[RepoId] = config.getStringList("lmvtfy.github-repos-to-watch").toSet[String].map{ new RepoId(_) } val BotUsername: String = config.getString("lmvtfy.username") - val BotPassword: String = config.getString("lmvtfy.password") + private val botPassword: String = config.getString("lmvtfy.password") + private val botCredentials: Credentials = Credentials(username = BotUsername, password = botPassword) + private val githubRateLimitThreshold: Int = config.getInt("lmvtfy.github-rate-limit-threshold") + def github(): Github = botCredentials.github(githubRateLimitThreshold)(UserAgent) val WebHookSecretKey: ByteString = ByteString(config.getString("lmvtfy.web-hook-secret-key").utf8Bytes) - val UserAgent: String = config.getString("spray.can.client.user-agent-header") + val UserAgent: UA = UA(config.getString("spray.can.client.user-agent-header")) val DefaultPort: Int = config.getInt("lmvtfy.default-port") val SquelchInvalidHttpLogging: Boolean = config.getBoolean("lmvtfy.squelch-invalid-http-logging") val DebugHtml: Boolean = config.getBoolean("lmvtfy.debug-html")