mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-10 15:35:59 +01:00
show commit status on pull-request-list view
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/main/twirl/issues/commitstatus.scala.html
Normal file
19
src/main/twirl/issues/commitstatus.scala.html
Normal 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){
|
||||||
|
✔
|
||||||
|
}else{
|
||||||
|
×
|
||||||
|
}</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/test/scala/service/IssuesServiceSpec.scala
Normal file
47
src/test/scala/service/IssuesServiceSpec.scala
Normal 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))
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user