package service import model.Profile._ import profile.simple._ import model.{WebHook, Account, Issue, PullRequest} import org.slf4j.LoggerFactory import service.RepositoryService.RepositoryInfo import util.JGitUtil import org.eclipse.jgit.diff.DiffEntry import util.JGitUtil.CommitInfo import org.eclipse.jgit.api.Git import org.apache.http.message.BasicNameValuePair import org.apache.http.client.entity.UrlEncodedFormEntity import org.apache.http.NameValuePair trait WebHookService { import WebHookService._ private val logger = LoggerFactory.getLogger(classOf[WebHookService]) def getWebHookURLs(owner: String, repository: String)(implicit s: Session): List[WebHook] = WebHooks.filter(_.byRepository(owner, repository)).sortBy(_.url).list def addWebHookURL(owner: String, repository: String, url :String)(implicit s: Session): Unit = WebHooks insert WebHook(owner, repository, url) def deleteWebHookURL(owner: String, repository: String, url :String)(implicit s: Session): Unit = WebHooks.filter(_.byPrimaryKey(owner, repository, url)).delete def callWebHook(eventName: String, webHookURLs: List[WebHook], payload: WebHookPayload): Unit = { import org.json4s._ import org.json4s.jackson.Serialization import org.json4s.jackson.Serialization.{read, write} import org.apache.http.client.methods.HttpPost import org.apache.http.impl.client.HttpClientBuilder import scala.concurrent._ import ExecutionContext.Implicits.global logger.debug("start callWebHook") implicit val formats = Serialization.formats(NoTypeHints) if(webHookURLs.nonEmpty){ val json = write(payload) val httpClient = HttpClientBuilder.create.build webHookURLs.foreach { webHookUrl => val f = Future { logger.debug(s"start web hook invocation for ${webHookUrl}") val httpPost = new HttpPost(webHookUrl.url) httpPost.addHeader("X-Github-Event", eventName) val params: java.util.List[NameValuePair] = new java.util.ArrayList() params.add(new BasicNameValuePair("payload", json)) httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")) httpClient.execute(httpPost) httpPost.releaseConnection() logger.debug(s"end web hook invocation for ${webHookUrl}") } f.onSuccess { case s => logger.debug(s"Success: web hook request to ${webHookUrl.url}") } f.onFailure { case t => logger.error(s"Failed: web hook request to ${webHookUrl.url}", t) } } } logger.debug("end callWebHook") } } object WebHookService { trait WebHookPayload case class WebHookPushPayload( pusher: WebHookApiUser, ref: String, commits: List[WebHookCommit], repository: WebHookRepository ) extends WebHookPayload object WebHookPayload { def apply(git: Git, pusher: Account, refName: String, repositoryInfo: RepositoryInfo, commits: List[CommitInfo], repositoryOwner: Account): WebHookPayload = WebHookPushPayload( WebHookApiUser(pusher), refName, commits.map{ commit => WebHookCommit(git, repositoryInfo, commit) }, WebHookRepository( repositoryInfo, owner= WebHookApiUser(repositoryOwner) ) ) } case class WebHookCommit( id: String, message: String, timestamp: String, url: String, added: List[String], removed: List[String], modified: List[String], author: WebHookCommitUser, committer: WebHookCommitUser) object WebHookCommit{ def apply(git: Git, repositoryInfo: RepositoryInfo, commit: CommitInfo): WebHookCommit = { val diffs = JGitUtil.getDiffs(git, commit.id, false) val commitUrl = repositoryInfo.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/commit/" + commit.id WebHookCommit( id = commit.id, message = commit.fullMessage, timestamp = commit.commitTime.toString, url = commitUrl, added = diffs._1.collect { case x if(x.changeType == DiffEntry.ChangeType.ADD) => x.newPath }, removed = diffs._1.collect { case x if(x.changeType == DiffEntry.ChangeType.DELETE) => x.oldPath }, modified = diffs._1.collect { case x if(x.changeType != DiffEntry.ChangeType.ADD && x.changeType != DiffEntry.ChangeType.DELETE) => x.newPath }, author = WebHookCommitUser( name = commit.authorName, email = commit.authorEmailAddress ), committer = WebHookCommitUser( name = commit.committerName, email = commit.committerEmailAddress ) ) } } case class WebHookApiUser( login: String, `type`: String, site_admin: Boolean) object WebHookApiUser{ def apply(user: Account): WebHookApiUser = WebHookApiUser( login = user.fullName, `type` = if(user.isGroupAccount){ "Organization" }else{ "User" }, site_admin = user.isAdmin ) } case class WebHookRepository( name: String, url: String, description: String, watchers: Int, forks: Int, `private`: Boolean, owner: WebHookApiUser) object WebHookRepository{ def apply(repositoryInfo: RepositoryInfo, owner: WebHookApiUser): WebHookRepository = WebHookRepository( name = repositoryInfo.name, url = repositoryInfo.httpUrl, description = repositoryInfo.repository.description.getOrElse(""), watchers = 0, forks = repositoryInfo.forkedCount, `private` = repositoryInfo.repository.isPrivate, owner = owner ) } case class WebHookCommitUser( name: String, email: String) case class WebHookPullRequestPayload( val action: String, val number: Int, val repository: WebHookRepository, val pull_request: WebHookPullRequest, val sender: WebHookApiUser ) extends WebHookPayload object WebHookPullRequestPayload{ def apply(action: String, issue: Issue, pullRequest: PullRequest, headRepository: RepositoryInfo, headOwner: Account, baseRepository: RepositoryInfo, baseOwner: Account, sender: model.Account): WebHookPullRequestPayload = { val headRepoPayload = WebHookRepository(headRepository, owner=WebHookApiUser(headOwner)) val baseRepoPayload = WebHookRepository(baseRepository, owner=WebHookApiUser(baseOwner)) val senderPayload = WebHookApiUser(sender) val pr = WebHookPullRequest(issue, pullRequest, headRepoPayload, baseRepoPayload, senderPayload) WebHookPullRequestPayload( action = action, number = issue.issueId, repository = pr.base.repo, pull_request = pr, sender = senderPayload ) } } case class WebHookPullRequest( number: Int, updated_at: String, head: WebHookPullRequestCommit, base: WebHookPullRequestCommit, mergeable: Option[Boolean], title: String, body: String, user: WebHookApiUser, url: String) object WebHookPullRequest{ def apply(issue: Issue, pullRequest: PullRequest, headRepo: WebHookRepository, baseRepo: WebHookRepository, user: WebHookApiUser): WebHookPullRequest = WebHookPullRequest( number = issue.issueId, updated_at = issue.updatedDate.toString(), head = WebHookPullRequestCommit( sha = pullRequest.commitIdTo, ref = pullRequest.requestBranch, repo = headRepo), base = WebHookPullRequestCommit( sha = pullRequest.commitIdFrom, ref = pullRequest.branch, repo = baseRepo), mergeable = None, title = issue.title, body = issue.content.getOrElse(""), user = user, url = s"${baseRepo.url}/pulls/${issue.issueId}" ) } case class WebHookPullRequestCommit( label: String, sha: String, ref: String, repo: WebHookRepository, user: WebHookApiUser) object WebHookPullRequestCommit{ def apply(sha: String, ref: String, repo: WebHookRepository): WebHookPullRequestCommit = WebHookPullRequestCommit( label = s"${repo.owner.login}:${ref}", sha = sha, ref = ref, repo = repo, user = repo.owner) } }