mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-11 07:55:55 +01:00
add CommitStatus table and model and service.
This commit is contained in:
@@ -11,3 +11,32 @@ ALTER TABLE ACCESS_TOKEN ADD CONSTRAINT IDX_ACCESS_TOKEN_PK PRIMARY KEY (ACCESS_
|
|||||||
ALTER TABLE ACCESS_TOKEN ADD CONSTRAINT IDX_ACCESS_TOKEN_FK0 FOREIGN KEY (USER_NAME) REFERENCES ACCOUNT (USER_NAME)
|
ALTER TABLE ACCESS_TOKEN ADD CONSTRAINT IDX_ACCESS_TOKEN_FK0 FOREIGN KEY (USER_NAME) REFERENCES ACCOUNT (USER_NAME)
|
||||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
ALTER TABLE ACCESS_TOKEN ADD CONSTRAINT IDX_ACCESS_TOKEN_TOKEN_HASH UNIQUE(TOKEN_HASH);
|
ALTER TABLE ACCESS_TOKEN ADD CONSTRAINT IDX_ACCESS_TOKEN_TOKEN_HASH UNIQUE(TOKEN_HASH);
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS COMMIT_STATUS;
|
||||||
|
CREATE TABLE COMMIT_STATUS(
|
||||||
|
COMMIT_STATUS_ID INT AUTO_INCREMENT,
|
||||||
|
USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
||||||
|
COMMIT_ID VARCHAR(40) NOT NULL,
|
||||||
|
CONTEXT VARCHAR(255) NOT NULL, -- context is too long (maximum is 255 characters)
|
||||||
|
STATE VARCHAR(10) NOT NULL, -- pending, success, error, or failure
|
||||||
|
TARGET_URL VARCHAR(200),
|
||||||
|
DESCRIPTION TEXT,
|
||||||
|
CREATOR VARCHAR(100) NOT NULL,
|
||||||
|
REGISTERED_DATE TIMESTAMP NOT NULL, -- CREATED_AT
|
||||||
|
UPDATED_DATE TIMESTAMP NOT NULL -- UPDATED_AT
|
||||||
|
);
|
||||||
|
ALTER TABLE COMMIT_STATUS ADD CONSTRAINT IDX_COMMIT_STATUS_PK PRIMARY KEY (COMMIT_STATUS_ID);
|
||||||
|
ALTER TABLE COMMIT_STATUS ADD CONSTRAINT IDX_COMMIT_STATUS_1
|
||||||
|
UNIQUE (USER_NAME, REPOSITORY_NAME, COMMIT_ID, CONTEXT);
|
||||||
|
ALTER TABLE COMMIT_STATUS ADD CONSTRAINT IDX_COMMIT_STATUS_FK1
|
||||||
|
FOREIGN KEY (USER_NAME, REPOSITORY_NAME)
|
||||||
|
REFERENCES REPOSITORY (USER_NAME, REPOSITORY_NAME)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE COMMIT_STATUS ADD CONSTRAINT IDX_COMMIT_STATUS_FK2
|
||||||
|
FOREIGN KEY (USER_NAME) REFERENCES ACCOUNT (USER_NAME)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE COMMIT_STATUS ADD CONSTRAINT IDX_COMMIT_STATUS_FK3
|
||||||
|
FOREIGN KEY (CREATOR) REFERENCES ACCOUNT (USER_NAME)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ protected[model] trait TemplateComponent { self: Profile =>
|
|||||||
|
|
||||||
def byCommit(owner: String, repository: String, commitId: String) =
|
def byCommit(owner: String, repository: String, commitId: String) =
|
||||||
byRepository(owner, repository) && (this.commitId === commitId)
|
byRepository(owner, repository) && (this.commitId === commitId)
|
||||||
|
|
||||||
|
def byCommit(owner: Column[String], repository: Column[String], commitId: Column[String]) =
|
||||||
|
byRepository(userName, repositoryName) && (this.commitId === commitId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/main/scala/model/CommitStatus.scala
Normal file
71
src/main/scala/model/CommitStatus.scala
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import scala.slick.lifted.MappedTo
|
||||||
|
import scala.slick.jdbc._
|
||||||
|
|
||||||
|
trait CommitStatusComponent extends TemplateComponent { self: Profile =>
|
||||||
|
import profile.simple._
|
||||||
|
import self._
|
||||||
|
|
||||||
|
implicit val commitStateColumnType = MappedColumnType.base[CommitState, String](b => b.name , i => CommitState(i))
|
||||||
|
|
||||||
|
lazy val CommitStatuses = TableQuery[CommitStatuses]
|
||||||
|
class CommitStatuses(tag: Tag) extends Table[CommitStatus](tag, "COMMIT_STATUS") with CommitTemplate {
|
||||||
|
val commitStatusId = column[Int]("COMMIT_STATUS_ID", O AutoInc)
|
||||||
|
val context = column[String]("CONTEXT")
|
||||||
|
val state = column[CommitState]("STATE")
|
||||||
|
val targetUrl = column[Option[String]]("TARGET_URL")
|
||||||
|
val description = column[Option[String]]("DESCRIPTION")
|
||||||
|
val creator = column[String]("CREATOR")
|
||||||
|
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||||
|
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||||
|
def * = (commitStatusId, userName, repositoryName, commitId, context, state, targetUrl, description, creator, registeredDate, updatedDate) <> (CommitStatus.tupled, CommitStatus.unapply)
|
||||||
|
def byPrimaryKey(id: Int) = commitStatusId === id.bind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class CommitStatus(
|
||||||
|
commitStatusId: Int = 0,
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
commitId: String,
|
||||||
|
context: String,
|
||||||
|
state: CommitState,
|
||||||
|
targetUrl: Option[String],
|
||||||
|
description: Option[String],
|
||||||
|
creator: String,
|
||||||
|
registeredDate: java.util.Date,
|
||||||
|
updatedDate: java.util.Date
|
||||||
|
)
|
||||||
|
sealed abstract class CommitState(val name: String)
|
||||||
|
object CommitState {
|
||||||
|
object ERROR extends CommitState("error")
|
||||||
|
object FAILURE extends CommitState("failure")
|
||||||
|
object PENDING extends CommitState("pending")
|
||||||
|
object SUCCESS extends CommitState("success")
|
||||||
|
|
||||||
|
val values: Vector[CommitState] = Vector(PENDING, SUCCESS, ERROR, FAILURE)
|
||||||
|
private val map: Map[String, CommitState] = values.map(enum => enum.name -> enum).toMap
|
||||||
|
def apply(name: String): CommitState = map(name)
|
||||||
|
def valueOf(name: String): Option[CommitState] = map.get(name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* failure if any of the contexts report as error or failure
|
||||||
|
* pending if there are no statuses or a context is pending
|
||||||
|
* success if the latest status for all contexts is success
|
||||||
|
*/
|
||||||
|
def combine(statuses: Set[CommitState]): CommitState = {
|
||||||
|
if(statuses.isEmpty){
|
||||||
|
PENDING
|
||||||
|
}else if(statuses.contains(CommitState.ERROR) || statuses.contains(CommitState.FAILURE)){
|
||||||
|
FAILURE
|
||||||
|
}else if(statuses.contains(CommitState.PENDING)){
|
||||||
|
PENDING
|
||||||
|
}else{
|
||||||
|
SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val getResult: GetResult[CommitState] = GetResult(r => CommitState(r.<<))
|
||||||
|
implicit val getResultOpt: GetResult[Option[CommitState]] = GetResult(r => r.<<?[String].map(CommitState(_)))
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ object Profile extends {
|
|||||||
with ActivityComponent
|
with ActivityComponent
|
||||||
with CollaboratorComponent
|
with CollaboratorComponent
|
||||||
with CommitCommentComponent
|
with CommitCommentComponent
|
||||||
|
with CommitStatusComponent
|
||||||
with GroupMemberComponent
|
with GroupMemberComponent
|
||||||
with IssueComponent
|
with IssueComponent
|
||||||
with IssueCommentComponent
|
with IssueCommentComponent
|
||||||
|
|||||||
49
src/main/scala/service/CommitStatusService.scala
Normal file
49
src/main/scala/service/CommitStatusService.scala
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import model.Profile._
|
||||||
|
import profile.simple._
|
||||||
|
import model.{CommitState, CommitStatus, Account}
|
||||||
|
import util.Implicits._
|
||||||
|
import util.StringUtil._
|
||||||
|
import service.RepositoryService.RepositoryInfo
|
||||||
|
|
||||||
|
trait CommitStatusService {
|
||||||
|
/** insert or update */
|
||||||
|
def createCommitStatus(userName: String, repositoryName: String, sha:String, context:String, state:CommitState, targetUrl:Option[String], description:Option[String], now:java.util.Date, creator:Account)(implicit s: Session): Int =
|
||||||
|
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) && t.context===context.bind )
|
||||||
|
.map(_.commitStatusId).firstOption match {
|
||||||
|
case Some(id:Int) => {
|
||||||
|
CommitStatuses.filter(_.byPrimaryKey(id)).map{
|
||||||
|
t => (t.state , t.targetUrl , t.updatedDate , t.creator, t.description)
|
||||||
|
}.update( (state, targetUrl, now, creator.userName, description) )
|
||||||
|
id
|
||||||
|
}
|
||||||
|
case None => (CommitStatuses returning CommitStatuses.map(_.commitStatusId)) += CommitStatus(
|
||||||
|
userName = userName,
|
||||||
|
repositoryName = repositoryName,
|
||||||
|
commitId = sha,
|
||||||
|
context = context,
|
||||||
|
state = state,
|
||||||
|
targetUrl = targetUrl,
|
||||||
|
description = description,
|
||||||
|
creator = creator.userName,
|
||||||
|
registeredDate = now,
|
||||||
|
updatedDate = now)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getCommitStatus(userName: String, repositoryName: String, id: Int)(implicit s: Session) :Option[CommitStatus] =
|
||||||
|
CommitStatuses.filter(t => t.byPrimaryKey(id) && t.byRepository(userName, repositoryName)).firstOption
|
||||||
|
|
||||||
|
def getCommitStatus(userName: String, repositoryName: String, sha: String, context: String)(implicit s: Session) :Option[CommitStatus] =
|
||||||
|
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) && t.context===context.bind ).firstOption
|
||||||
|
|
||||||
|
def getCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) :List[CommitStatus] =
|
||||||
|
byCommitStatues(userName, repositoryName, sha).list
|
||||||
|
|
||||||
|
def getCommitStatuesWithCreator(userName: String, repositoryName: String, sha: String)(implicit s: Session) :List[(CommitStatus, Account)] =
|
||||||
|
byCommitStatues(userName, repositoryName, sha).innerJoin(Accounts)
|
||||||
|
.filter{ case (t,a) => t.creator === a.userName }.list
|
||||||
|
|
||||||
|
protected def byCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) =
|
||||||
|
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) ).sortBy(_.updatedDate desc)
|
||||||
|
}
|
||||||
@@ -55,6 +55,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
val issueComments = IssueComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issueComments = IssueComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
|
val commitStatuses = CommitStatuses.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val collaborators = Collaborators .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val collaborators = Collaborators .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
|
|
||||||
Repositories.filter { t =>
|
Repositories.filter { t =>
|
||||||
@@ -95,6 +96,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
CommitComments.insertAll(commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
CommitComments.insertAll(commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
|
CommitStatuses.insertAll(commitStatuses.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
|
|
||||||
// Convert labelId
|
// Convert labelId
|
||||||
val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap
|
val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap
|
||||||
|
|||||||
25
src/test/scala/model/CommitStateSpec.scala
Normal file
25
src/test/scala/model/CommitStateSpec.scala
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
import CommitState._
|
||||||
|
|
||||||
|
class CommitStateSpec extends Specification {
|
||||||
|
"CommitState" should {
|
||||||
|
"combine empty must eq PENDING" in {
|
||||||
|
combine(Set()) must_== PENDING
|
||||||
|
}
|
||||||
|
"combine includes ERROR must eq FAILURE" in {
|
||||||
|
combine(Set(ERROR, SUCCESS, PENDING)) must_== FAILURE
|
||||||
|
}
|
||||||
|
"combine includes FAILURE must eq peinding" in {
|
||||||
|
combine(Set(FAILURE, SUCCESS, PENDING)) must_== FAILURE
|
||||||
|
}
|
||||||
|
"combine includes PENDING must eq peinding" in {
|
||||||
|
combine(Set(PENDING, SUCCESS)) must_== PENDING
|
||||||
|
}
|
||||||
|
"combine only SUCCESS must eq SUCCESS" in {
|
||||||
|
combine(Set(SUCCESS)) must_== SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/test/scala/service/CommitStateServiceSpec.scala
Normal file
77
src/test/scala/service/CommitStateServiceSpec.scala
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package service
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
import java.util.Date
|
||||||
|
import model._
|
||||||
|
import model.Profile._
|
||||||
|
import profile.simple._
|
||||||
|
class CommitStatusServiceSpec extends Specification with ServiceSpecBase with CommitStatusService
|
||||||
|
with RepositoryService with AccountService{
|
||||||
|
def generateNewAccount(name:String)(implicit s:Session):Account = {
|
||||||
|
createAccount(name, name, name, s"${name}@example.com", false, None)
|
||||||
|
getAccountByUserName(name).get
|
||||||
|
}
|
||||||
|
val now = new java.util.Date()
|
||||||
|
val fixture1 = CommitStatus(
|
||||||
|
userName = "root",
|
||||||
|
repositoryName = "repo",
|
||||||
|
commitId = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
|
||||||
|
context = "jenkins/test",
|
||||||
|
creator = "tester",
|
||||||
|
state = CommitState.PENDING,
|
||||||
|
targetUrl = Some("http://example.com/target"),
|
||||||
|
description = Some("description"),
|
||||||
|
updatedDate = now,
|
||||||
|
registeredDate = now)
|
||||||
|
def findById(id: Int)(implicit s:Session) = CommitStatuses.filter(_.byPrimaryKey(id)).firstOption
|
||||||
|
def generateFixture1(tester:Account)(implicit s:Session) = createCommitStatus(
|
||||||
|
userName = fixture1.userName,
|
||||||
|
repositoryName = fixture1.repositoryName,
|
||||||
|
sha = fixture1.commitId,
|
||||||
|
context = fixture1.context,
|
||||||
|
state = fixture1.state,
|
||||||
|
targetUrl = fixture1.targetUrl,
|
||||||
|
description = fixture1.description,
|
||||||
|
creator = tester,
|
||||||
|
now = fixture1.registeredDate)
|
||||||
|
"CommitStatusService" should {
|
||||||
|
"createCommitState can insert and update" in { withTestDB { implicit session =>
|
||||||
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
|
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
|
val id = generateFixture1(tester:Account)
|
||||||
|
getCommitStatus(fixture1.userName, fixture1.repositoryName, id) must_==
|
||||||
|
Some(fixture1.copy(commitStatusId=id))
|
||||||
|
// other one can update
|
||||||
|
val tester2 = generateNewAccount("tester2")
|
||||||
|
val time2 = new java.util.Date();
|
||||||
|
val id2 = createCommitStatus(
|
||||||
|
userName = fixture1.userName,
|
||||||
|
repositoryName = fixture1.repositoryName,
|
||||||
|
sha = fixture1.commitId,
|
||||||
|
context = fixture1.context,
|
||||||
|
state = CommitState.SUCCESS,
|
||||||
|
targetUrl = Some("http://example.com/target2"),
|
||||||
|
description = Some("description2"),
|
||||||
|
creator = tester2,
|
||||||
|
now = time2)
|
||||||
|
getCommitStatus(fixture1.userName, fixture1.repositoryName, id2) must_== Some(fixture1.copy(
|
||||||
|
commitStatusId = id,
|
||||||
|
creator = "tester2",
|
||||||
|
state = CommitState.SUCCESS,
|
||||||
|
targetUrl = Some("http://example.com/target2"),
|
||||||
|
description = Some("description2"),
|
||||||
|
updatedDate = time2))
|
||||||
|
}}
|
||||||
|
"getCommitStatus can find by commitId and context" in { withTestDB { implicit session =>
|
||||||
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
|
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
|
val id = generateFixture1(tester:Account)
|
||||||
|
getCommitStatus(fixture1.userName, fixture1.repositoryName, fixture1.commitId, fixture1.context) must_== Some(fixture1.copy(commitStatusId=id))
|
||||||
|
}}
|
||||||
|
"getCommitStatus can find by commitStatusId" in { withTestDB { implicit session =>
|
||||||
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
|
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
|
val id = generateFixture1(tester:Account)
|
||||||
|
getCommitStatus(fixture1.userName, fixture1.repositoryName, id) must_== Some(fixture1.copy(commitStatusId=id))
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/test/scala/service/RepositoryServiceSpec.scala
Normal file
37
src/test/scala/service/RepositoryServiceSpec.scala
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package service
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
import java.util.Date
|
||||||
|
import model._
|
||||||
|
import model.Profile._
|
||||||
|
import profile.simple._
|
||||||
|
class RepositoryServiceSpec extends Specification with ServiceSpecBase with RepositoryService with AccountService{
|
||||||
|
def generateNewAccount(name:String)(implicit s:Session):Account = {
|
||||||
|
createAccount(name, name, name, s"${name}@example.com", false, None)
|
||||||
|
getAccountByUserName(name).get
|
||||||
|
}
|
||||||
|
"RepositoryService" should {
|
||||||
|
"renameRepository can rename CommitState" in { withTestDB { implicit session =>
|
||||||
|
val tester = generateNewAccount("tester")
|
||||||
|
createRepository("repo","root",None,false)
|
||||||
|
val commitStatusService = new CommitStatusService{}
|
||||||
|
val id = commitStatusService.createCommitStatus(
|
||||||
|
userName = "root",
|
||||||
|
repositoryName = "repo",
|
||||||
|
sha = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
|
||||||
|
context = "jenkins/test",
|
||||||
|
state = CommitState.PENDING,
|
||||||
|
targetUrl = Some("http://example.com/target"),
|
||||||
|
description = Some("description"),
|
||||||
|
creator = tester,
|
||||||
|
now = new java.util.Date)
|
||||||
|
val org = commitStatusService.getCommitStatus("root","repo", id).get
|
||||||
|
renameRepository("root","repo","tester","repo2")
|
||||||
|
val neo = commitStatusService.getCommitStatus("tester","repo2", org.commitId, org.context).get
|
||||||
|
neo must_==
|
||||||
|
org.copy(
|
||||||
|
commitStatusId=neo.commitStatusId,
|
||||||
|
repositoryName="repo2",
|
||||||
|
userName="tester")
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user