show commit status on pull-request-list view

This commit is contained in:
nazoking
2015-03-02 03:10:36 +09:00
parent 44a8e98c7b
commit 1e6d26221d
7 changed files with 133 additions and 14 deletions

View File

@@ -46,4 +46,5 @@ trait CommitStatusService {
protected def byCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) = protected def byCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) =
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) ).sortBy(_.updatedDate desc) CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) ).sortBy(_.updatedDate desc)
} }

View File

@@ -84,6 +84,47 @@ trait IssuesService {
.toMap .toMap
} }
def getCommitStatues(issueList:Seq[(String, String, Int)])(implicit s: Session) :Map[(String, String, Int), CommitStatusInfo] ={
if(issueList.isEmpty){
Map.empty
}else{
import scala.slick.jdbc._
val issueIdQuery = issueList.map(i => "(PR.USER_NAME=? AND PR.REPOSITORY_NAME=? AND PR.ISSUE_ID=?)").mkString(" OR ")
implicit val qset = SetParameter[Seq[(String, String, Int)]] {
case (seq, pp) =>
for (a <- seq) {
pp.setString(a._1)
pp.setString(a._2)
pp.setInt(a._3)
}
}
import model.Profile.commitStateColumnType
val query = Q.query[Seq[(String, String, Int)], (String, String, Int, Int, Int, Option[String], Option[model.CommitState], Option[String], Option[String])](s"""
SELECT SUMM.USER_NAME, SUMM.REPOSITORY_NAME, SUMM.ISSUE_ID, CS_ALL, CS_SUCCESS
, CSD.CONTEXT, CSD.STATE, CSD.TARGET_URL, CSD.DESCRIPTION
FROM (SELECT
PR.USER_NAME
, PR.REPOSITORY_NAME
, PR.ISSUE_ID
, COUNT(CS.STATE) AS CS_ALL
, SUM(CS.STATE='success') AS CS_SUCCESS
, PR.COMMIT_ID_TO AS COMMIT_ID
FROM PULL_REQUEST PR
JOIN COMMIT_STATUS CS
ON PR.USER_NAME=CS.USER_NAME
AND PR.REPOSITORY_NAME=CS.REPOSITORY_NAME
AND PR.COMMIT_ID_TO=CS.COMMIT_ID
WHERE $issueIdQuery
GROUP BY PR.USER_NAME, PR.REPOSITORY_NAME, PR.ISSUE_ID) as SUMM
LEFT OUTER JOIN COMMIT_STATUS CSD
ON SUMM.CS_ALL = 1 AND SUMM.COMMIT_ID = CSD.COMMIT_ID""");
query(issueList).list.map{
case(userName, repositoryName, issueId, count, successCount, context, state, targetUrl, description) =>
(userName, repositoryName, issueId) -> CommitStatusInfo(count, successCount, context, state, targetUrl, description)
}.toMap
}
}
/** /**
* Returns the search result against issues. * Returns the search result against issues.
* *
@@ -96,9 +137,8 @@ trait IssuesService {
*/ */
def searchIssue(condition: IssueSearchCondition, pullRequest: Boolean, offset: Int, limit: Int, repos: (String, String)*) def searchIssue(condition: IssueSearchCondition, pullRequest: Boolean, offset: Int, limit: Int, repos: (String, String)*)
(implicit s: Session): List[IssueInfo] = { (implicit s: Session): List[IssueInfo] = {
// get issues and comment count and labels // get issues and comment count and labels
searchIssueQueryBase(condition, pullRequest, offset, limit, repos) val result = searchIssueQueryBase(condition, pullRequest, offset, limit, repos)
.leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) } .leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
.leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) } .leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) }
.leftJoin (Milestones) .on { case ((((t1, t2), t3), t4), t5) => t1.byMilestone(t5.userName, t5.repositoryName, t5.milestoneId) } .leftJoin (Milestones) .on { case ((((t1, t2), t3), t4), t5) => t1.byMilestone(t5.userName, t5.repositoryName, t5.milestoneId) }
@@ -111,14 +151,17 @@ trait IssuesService {
c1._1.repositoryName == c2._1.repositoryName && c1._1.repositoryName == c2._1.repositoryName &&
c1._1.issueId == c2._1.issueId c1._1.issueId == c2._1.issueId
} }
.map { issues => issues.head match { val status = getCommitStatues(result.map(_.head._1).map(is => (is.userName, is.repositoryName, is.issueId)))
result.map { issues => issues.head match {
case (issue, commentCount, _, _, _, milestone) => case (issue, commentCount, _, _, _, milestone) =>
IssueInfo(issue, IssueInfo(issue,
issues.flatMap { t => t._3.map ( issues.flatMap { t => t._3.map (
Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get) Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get)
)} toList, )} toList,
milestone, milestone,
commentCount) commentCount,
status.get(issue.userName, issue.repositoryName, issue.issueId))
}} toList }} toList
} }
@@ -490,6 +533,8 @@ object IssuesService {
} }
} }
case class IssueInfo(issue: Issue, labels: List[Label], milestone: Option[String], commentCount: Int) case class CommitStatusInfo(count: Int, successCount: Int, context: Option[String], state: Option[model.CommitState], targetUrl: Option[String], description: Option[String])
case class IssueInfo(issue: Issue, labels: List[Label], milestone: Option[String], commentCount: Int, status:Option[CommitStatusInfo])
} }

View File

@@ -14,7 +14,7 @@
@dashboard.html.header(openCount, closedCount, condition, groups) @dashboard.html.header(openCount, closedCount, condition, groups)
</th> </th>
</tr> </tr>
@issues.map { case IssueInfo(issue, labels, milestone, commentCount) => @issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr> <tr>
<td style="padding-top: 15px; padding-bottom: 15px;"> <td style="padding-top: 15px; padding-bottom: 15px;">
@if(issue.isPullRequest){ @if(issue.isPullRequest){
@@ -28,6 +28,7 @@
} else { } else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a> <a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
} }
@_root_.issues.html.commitstatus(issue, commitStatus)
@labels.map { label => @labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span> <span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
} }

View File

@@ -0,0 +1,19 @@
@(issue: model.Issue, statusInfo: Option[service.IssuesService.CommitStatusInfo])(implicit context: app.Context)
@import view.helpers._
@statusInfo.map{ status =>
@if(status.count==1 && status.state.isDefined){
@if(status.targetUrl.isDefined){
<a href="@status.targetUrl.get" class="text-@status.state.get.name" data-toggle="tooltip" title="@status.state.get.name : @status.description.getOrElse(status.context.get)">@commitStateIcon(status.state.get)</a>
}else{
<span class="text-@status.state.get.name">@commitStateIcon(status.state.get)
}
}else{
@defining(status.count==status.successCount){ isSuccess =>
<a href="@context.path/@issue.userName/@issue.repositoryName/@issue.issueId" class="@if(isSuccess){ text-success }else{ text-error }" data-toggle="tooltip" title="@status.successCount / @status.count checks OK">@if(isSuccess){
&#x2714;
}else{
×
}</a>
}
}
}

View File

@@ -170,7 +170,7 @@
</td> </td>
</tr> </tr>
} }
@issues.map { case IssueInfo(issue, labels, milestone, commentCount) => @issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr> <tr>
<td style="padding-top: 15px; padding-bottom: 15px;"> <td style="padding-top: 15px; padding-bottom: 15px;">
@if(hasWritePermission){ @if(hasWritePermission){
@@ -185,6 +185,7 @@
} else { } else {
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a> <a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
} }
@commitstatus(issue, commitStatus)
@labels.map { label => @labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span> <span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
} }

View File

@@ -0,0 +1,47 @@
package service
import org.specs2.mutable.Specification
import java.util.Date
import model._
import service.IssuesService._
class IssuesServiceSpec extends Specification with ServiceSpecBase {
"IssuesService" should {
"getCommitStatues" in { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
def getCommitStatues = dummyService.getCommitStatues(List(("user1","repo1",1),("user1","repo1",2)))
getCommitStatues must_== Map.empty
val now = new java.util.Date()
val issueId = generateNewIssue("user1","repo1")
issueId must_== 1
getCommitStatues must_== Map.empty
val cs = dummyService.createCommitStatus("user1","repo1","shasha", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map.empty
val (is2, pr2) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature1")
pr2.issueId must_== 2
// if there are no statuses, state is none
getCommitStatues must_== Map.empty
// if there is a status, state is that
val cs2 = dummyService.createCommitStatus("user1","repo1","feature1", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(1,1,Some("default"),Some(CommitState.SUCCESS),Some("http://exmple.com/ci"),Some("exampleService")))
// if there are two statuses, state is none
val cs3 = dummyService.createCommitStatus("user1","repo1","feature1", "pend", CommitState.PENDING, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None))
// get only statuses in query issues
val (is3, pr3) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature3")
val cs4 = dummyService.createCommitStatus("user1","repo1","feature3", "none", CommitState.PENDING, None, None, now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None))
} }
}
}

View File

@@ -30,26 +30,31 @@ trait ServiceSpecBase {
AccountService.getAccountByUserName(name).get AccountService.getAccountByUserName(name).get
} }
lazy val dummyService = new RepositoryService with AccountService with IssuesService with PullRequestService (){} lazy val dummyService = new RepositoryService with AccountService with IssuesService with PullRequestService
with CommitStatusService (){}
def generateNewUserWithDBRepository(userName:String, repositoryName:String)(implicit s:Session):Account = { def generateNewUserWithDBRepository(userName:String, repositoryName:String)(implicit s:Session):Account = {
val ac = generateNewAccount(userName) val ac = generateNewAccount(userName)
dummyService.createRepository(repositoryName, userName, None, false) dummyService.createRepository(repositoryName, userName, None, false)
ac ac
} }
def generateNewPullRequest(base:String, request:String)(implicit s:Session):(Issue, PullRequest) = {
val Array(baseUserName, baseRepositoryName, baesBranch)=base.split("/") def generateNewIssue(userName:String, repositoryName:String, requestUserName:String="root")(implicit s:Session): Int = {
val Array(requestUserName, requestRepositoryName, requestBranch)=request.split("/") dummyService.createIssue(
val issueId = dummyService.createIssue( owner = userName,
owner = baseUserName, repository = repositoryName,
repository = baseRepositoryName,
loginUser = requestUserName, loginUser = requestUserName,
title = "issue title", title = "issue title",
content = None, content = None,
assignedUserName = None, assignedUserName = None,
milestoneId = None, milestoneId = None,
isPullRequest = true) isPullRequest = true)
}
def generateNewPullRequest(base:String, request:String)(implicit s:Session):(Issue, PullRequest) = {
val Array(baseUserName, baseRepositoryName, baesBranch)=base.split("/")
val Array(requestUserName, requestRepositoryName, requestBranch)=request.split("/")
val issueId = generateNewIssue(baseUserName, baseRepositoryName, requestUserName)
dummyService.createPullRequest( dummyService.createPullRequest(
originUserName = baseUserName, originUserName = baseUserName,
originRepositoryName = baseRepositoryName, originRepositoryName = baseRepositoryName,