mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-11 16:05:49 +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)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
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) =
|
||||
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 CollaboratorComponent
|
||||
with CommitCommentComponent
|
||||
with CommitStatusComponent
|
||||
with GroupMemberComponent
|
||||
with IssueComponent
|
||||
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 issueLabels = IssueLabels .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
|
||||
|
||||
Repositories.filter { t =>
|
||||
@@ -95,6 +96,7 @@ trait RepositoryService { self: AccountService =>
|
||||
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
Labels .insertAll(labels .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
|
||||
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