mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-08 06:25:51 +01:00
Merge pull request #1140 from gitbucket/separate_api_controller
Separate API controller to improve routing performance
This commit is contained in:
@@ -27,9 +27,10 @@ class ScalatraBootstrap extends LifeCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.mount(new IndexController, "/")
|
context.mount(new IndexController, "/")
|
||||||
|
context.mount(new ApiController, "/api/v3")
|
||||||
context.mount(new FileUploadController, "/upload")
|
context.mount(new FileUploadController, "/upload")
|
||||||
|
context.mount(new SystemSettingsController, "/admin")
|
||||||
context.mount(new DashboardController, "/*")
|
context.mount(new DashboardController, "/*")
|
||||||
context.mount(new SystemSettingsController, "/*")
|
|
||||||
context.mount(new AccountController, "/*")
|
context.mount(new AccountController, "/*")
|
||||||
context.mount(new RepositoryViewerController, "/*")
|
context.mount(new RepositoryViewerController, "/*")
|
||||||
context.mount(new WikiController, "/*")
|
context.mount(new WikiController, "/*")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.account.html
|
import gitbucket.core.account.html
|
||||||
import gitbucket.core.api._
|
|
||||||
import gitbucket.core.helper
|
import gitbucket.core.helper
|
||||||
import gitbucket.core.model.GroupMember
|
import gitbucket.core.model.GroupMember
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
@@ -14,22 +13,19 @@ import gitbucket.core.util._
|
|||||||
|
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.eclipse.jgit.api.Git
|
|
||||||
import org.eclipse.jgit.dircache.DirCache
|
|
||||||
import org.eclipse.jgit.lib.{FileMode, Constants}
|
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
|
|
||||||
|
|
||||||
class AccountController extends AccountControllerBase
|
class AccountController extends AccountControllerBase
|
||||||
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
||||||
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
||||||
with AccessTokenService with WebHookService
|
with AccessTokenService with WebHookService with RepositoryCreationService
|
||||||
|
|
||||||
|
|
||||||
trait AccountControllerBase extends AccountManagementControllerBase {
|
trait AccountControllerBase extends AccountManagementControllerBase {
|
||||||
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
||||||
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
||||||
with AccessTokenService with WebHookService =>
|
with AccessTokenService with WebHookService with RepositoryCreationService =>
|
||||||
|
|
||||||
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
||||||
url: Option[String], fileId: Option[String])
|
url: Option[String], fileId: Option[String])
|
||||||
@@ -156,25 +152,6 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/users/#get-a-single-user
|
|
||||||
*/
|
|
||||||
get("/api/v3/users/:userName") {
|
|
||||||
getAccountByUserName(params("userName")).map { account =>
|
|
||||||
JsonFormat(ApiUser(account))
|
|
||||||
} getOrElse NotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/users/#get-the-authenticated-user
|
|
||||||
*/
|
|
||||||
get("/api/v3/user") {
|
|
||||||
context.loginAccount.map { account =>
|
|
||||||
JsonFormat(ApiUser(account))
|
|
||||||
} getOrElse Unauthorized
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get("/:userName/_edit")(oneselfOnly {
|
get("/:userName/_edit")(oneselfOnly {
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
getAccountByUserName(userName).map { x =>
|
getAccountByUserName(userName).map { x =>
|
||||||
@@ -367,7 +344,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
post("/new", newRepositoryForm)(usersOnly { form =>
|
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||||
LockUtil.lock(s"${form.owner}/${form.name}"){
|
LockUtil.lock(s"${form.owner}/${form.name}"){
|
||||||
if(getRepository(form.owner, form.name).isEmpty){
|
if(getRepository(form.owner, form.name).isEmpty){
|
||||||
createRepository(form.owner, form.name, form.description, form.isPrivate, form.createReadme)
|
createRepository(context.loginAccount.get, form.owner, form.name, form.description, form.isPrivate, form.createReadme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to the repository
|
// redirect to the repository
|
||||||
@@ -375,54 +352,6 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Create user repository
|
|
||||||
* https://developer.github.com/v3/repos/#create
|
|
||||||
*/
|
|
||||||
post("/api/v3/user/repos")(usersOnly {
|
|
||||||
val owner = context.loginAccount.get.userName
|
|
||||||
(for {
|
|
||||||
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
|
||||||
} yield {
|
|
||||||
LockUtil.lock(s"${owner}/${data.name}") {
|
|
||||||
if(getRepository(owner, data.name).isEmpty){
|
|
||||||
createRepository(owner, data.name, data.description, data.`private`, data.auto_init)
|
|
||||||
val repository = getRepository(owner, data.name).get
|
|
||||||
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get)))
|
|
||||||
} else {
|
|
||||||
ApiError(
|
|
||||||
"A repository with this name already exists on this account",
|
|
||||||
Some("https://developer.github.com/v3/repos/#create")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create group repository
|
|
||||||
* https://developer.github.com/v3/repos/#create
|
|
||||||
*/
|
|
||||||
post("/api/v3/orgs/:org/repos")(managersOnly {
|
|
||||||
val groupName = params("org")
|
|
||||||
(for {
|
|
||||||
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
|
||||||
} yield {
|
|
||||||
LockUtil.lock(s"${groupName}/${data.name}") {
|
|
||||||
if(getRepository(groupName, data.name).isEmpty){
|
|
||||||
createRepository(groupName, data.name, data.description, data.`private`, data.auto_init)
|
|
||||||
val repository = getRepository(groupName, data.name).get
|
|
||||||
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get)))
|
|
||||||
} else {
|
|
||||||
ApiError(
|
|
||||||
"A repository with this name already exists for this group",
|
|
||||||
Some("https://developer.github.com/v3/repos/#create")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
||||||
val loginAccount = context.loginAccount.get
|
val loginAccount = context.loginAccount.get
|
||||||
val loginUserName = loginAccount.userName
|
val loginUserName = loginAccount.userName
|
||||||
@@ -456,7 +385,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
||||||
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
||||||
|
|
||||||
createRepository(
|
insertRepository(
|
||||||
repositoryName = repository.name,
|
repositoryName = repository.name,
|
||||||
userName = accountName,
|
userName = accountName,
|
||||||
description = repository.repository.description,
|
description = repository.repository.description,
|
||||||
@@ -496,68 +425,6 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
private def createRepository(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean) {
|
|
||||||
val ownerAccount = getAccountByUserName(owner).get
|
|
||||||
val loginAccount = context.loginAccount.get
|
|
||||||
val loginUserName = loginAccount.userName
|
|
||||||
|
|
||||||
// Insert to the database at first
|
|
||||||
createRepository(name, owner, description, isPrivate)
|
|
||||||
|
|
||||||
// Add collaborators for group repository
|
|
||||||
if(ownerAccount.isGroupAccount){
|
|
||||||
getGroupMembers(owner).foreach { member =>
|
|
||||||
addCollaborator(owner, name, member.userName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert default labels
|
|
||||||
insertDefaultLabels(owner, name)
|
|
||||||
|
|
||||||
// Create the actual repository
|
|
||||||
val gitdir = getRepositoryDir(owner, name)
|
|
||||||
JGitUtil.initRepository(gitdir)
|
|
||||||
|
|
||||||
if(createReadme){
|
|
||||||
using(Git.open(gitdir)){ git =>
|
|
||||||
val builder = DirCache.newInCore.builder()
|
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
|
||||||
val content = if(description.nonEmpty){
|
|
||||||
name + "\n" +
|
|
||||||
"===============\n" +
|
|
||||||
"\n" +
|
|
||||||
description.get
|
|
||||||
} else {
|
|
||||||
name + "\n" +
|
|
||||||
"===============\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
|
||||||
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
|
||||||
builder.finish()
|
|
||||||
|
|
||||||
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
|
||||||
Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Wiki repository
|
|
||||||
createWikiRepository(loginAccount, owner, name)
|
|
||||||
|
|
||||||
// Record activity
|
|
||||||
recordCreateRepositoryActivity(owner, name, loginUserName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
|
||||||
createLabel(userName, repositoryName, "bug", "fc2929")
|
|
||||||
createLabel(userName, repositoryName, "duplicate", "cccccc")
|
|
||||||
createLabel(userName, repositoryName, "enhancement", "84b6eb")
|
|
||||||
createLabel(userName, repositoryName, "invalid", "e6e6e6")
|
|
||||||
createLabel(userName, repositoryName, "question", "cc317c")
|
|
||||||
createLabel(userName, repositoryName, "wontfix", "ffffff")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def existsAccount: Constraint = new Constraint(){
|
private def existsAccount: Constraint = new Constraint(){
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
||||||
|
|||||||
389
src/main/scala/gitbucket/core/controller/ApiController.scala
Normal file
389
src/main/scala/gitbucket/core/controller/ApiController.scala
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
package gitbucket.core.controller
|
||||||
|
|
||||||
|
import gitbucket.core.api._
|
||||||
|
import gitbucket.core.model._
|
||||||
|
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||||
|
import gitbucket.core.service.PullRequestService._
|
||||||
|
import gitbucket.core.service._
|
||||||
|
import gitbucket.core.util.ControlUtil._
|
||||||
|
import gitbucket.core.util.Directory._
|
||||||
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
|
import gitbucket.core.util._
|
||||||
|
import gitbucket.core.util.Implicits._
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.scalatra.{NoContent, UnprocessableEntity, Created}
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
|
class ApiController extends ApiControllerBase
|
||||||
|
with RepositoryService
|
||||||
|
with AccountService
|
||||||
|
with ProtectedBranchService
|
||||||
|
with IssuesService
|
||||||
|
with LabelsService
|
||||||
|
with PullRequestService
|
||||||
|
with CommitStatusService
|
||||||
|
with RepositoryCreationService
|
||||||
|
with HandleCommentService
|
||||||
|
with WebHookService
|
||||||
|
with WebHookPullRequestService
|
||||||
|
with WebHookIssueCommentService
|
||||||
|
with WikiService
|
||||||
|
with ActivityService
|
||||||
|
with OwnerAuthenticator
|
||||||
|
with UsersAuthenticator
|
||||||
|
with GroupManagerAuthenticator
|
||||||
|
with ReferrerAuthenticator
|
||||||
|
with ReadableUsersAuthenticator
|
||||||
|
with CollaboratorsAuthenticator
|
||||||
|
|
||||||
|
trait ApiControllerBase extends ControllerBase {
|
||||||
|
self: RepositoryService
|
||||||
|
with AccountService
|
||||||
|
with ProtectedBranchService
|
||||||
|
with IssuesService
|
||||||
|
with LabelsService
|
||||||
|
with PullRequestService
|
||||||
|
with CommitStatusService
|
||||||
|
with RepositoryCreationService
|
||||||
|
with HandleCommentService
|
||||||
|
with OwnerAuthenticator
|
||||||
|
with UsersAuthenticator
|
||||||
|
with GroupManagerAuthenticator
|
||||||
|
with ReferrerAuthenticator
|
||||||
|
with ReadableUsersAuthenticator
|
||||||
|
with CollaboratorsAuthenticator =>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/users/#get-a-single-user
|
||||||
|
*/
|
||||||
|
get("/api/v3/users/:userName") {
|
||||||
|
getAccountByUserName(params("userName")).map { account =>
|
||||||
|
JsonFormat(ApiUser(account))
|
||||||
|
} getOrElse NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/users/#get-the-authenticated-user
|
||||||
|
*/
|
||||||
|
get("/api/v3/user") {
|
||||||
|
context.loginAccount.map { account =>
|
||||||
|
JsonFormat(ApiUser(account))
|
||||||
|
} getOrElse Unauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create user repository
|
||||||
|
* https://developer.github.com/v3/repos/#create
|
||||||
|
*/
|
||||||
|
post("/api/v3/user/repos")(usersOnly {
|
||||||
|
val owner = context.loginAccount.get.userName
|
||||||
|
(for {
|
||||||
|
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
||||||
|
} yield {
|
||||||
|
LockUtil.lock(s"${owner}/${data.name}") {
|
||||||
|
if(getRepository(owner, data.name).isEmpty){
|
||||||
|
createRepository(context.loginAccount.get, owner, data.name, data.description, data.`private`, data.auto_init)
|
||||||
|
val repository = getRepository(owner, data.name).get
|
||||||
|
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get)))
|
||||||
|
} else {
|
||||||
|
ApiError(
|
||||||
|
"A repository with this name already exists on this account",
|
||||||
|
Some("https://developer.github.com/v3/repos/#create")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create group repository
|
||||||
|
* https://developer.github.com/v3/repos/#create
|
||||||
|
*/
|
||||||
|
post("/api/v3/orgs/:org/repos")(managersOnly {
|
||||||
|
val groupName = params("org")
|
||||||
|
(for {
|
||||||
|
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
||||||
|
} yield {
|
||||||
|
LockUtil.lock(s"${groupName}/${data.name}") {
|
||||||
|
if(getRepository(groupName, data.name).isEmpty){
|
||||||
|
createRepository(context.loginAccount.get, groupName, data.name, data.description, data.`private`, data.auto_init)
|
||||||
|
val repository = getRepository(groupName, data.name).get
|
||||||
|
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get)))
|
||||||
|
} else {
|
||||||
|
ApiError(
|
||||||
|
"A repository with this name already exists for this group",
|
||||||
|
Some("https://developer.github.com/v3/repos/#create")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
||||||
|
patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository =>
|
||||||
|
import gitbucket.core.api._
|
||||||
|
(for{
|
||||||
|
branch <- params.get("branch") if repository.branchList.find(_ == branch).isDefined
|
||||||
|
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
||||||
|
} yield {
|
||||||
|
if(protection.enabled){
|
||||||
|
enableBranchProtection(repository.owner, repository.name, branch, protection.status.enforcement_level == ApiBranchProtection.Everyone, protection.status.contexts)
|
||||||
|
} else {
|
||||||
|
disableBranchProtection(repository.owner, repository.name, branch)
|
||||||
|
}
|
||||||
|
JsonFormat(ApiBranch(branch, protection)(RepositoryName(repository)))
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://developer.github.com/v3/rate_limit/#get-your-current-rate-limit-status
|
||||||
|
* but not enabled.
|
||||||
|
*/
|
||||||
|
get("/api/v3/rate_limit"){
|
||||||
|
contentType = formats("json")
|
||||||
|
// this message is same as github enterprise...
|
||||||
|
org.scalatra.NotFound(ApiError("Rate limiting is not enabled."))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/issues/:id/comments")(referrersOnly { repository =>
|
||||||
|
(for{
|
||||||
|
issueId <- params("id").toIntOpt
|
||||||
|
comments = getCommentsForApi(repository.owner, repository.name, issueId.toInt)
|
||||||
|
} yield {
|
||||||
|
JsonFormat(comments.map{ case (issueComment, user, issue) => ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest) })
|
||||||
|
}).getOrElse(NotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/issues/comments/#create-a-comment
|
||||||
|
*/
|
||||||
|
post("/api/v3/repos/:owner/:repository/issues/:id/comments")(readableUsersOnly { repository =>
|
||||||
|
(for{
|
||||||
|
issueId <- params("id").toIntOpt
|
||||||
|
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||||
|
body <- extractFromJsonBody[CreateAComment].map(_.body) if ! body.isEmpty
|
||||||
|
action = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||||
|
(issue, id) <- handleComment(issue, Some(body), repository, action)
|
||||||
|
issueComment <- getComment(repository.owner, repository.name, id.toString())
|
||||||
|
} yield {
|
||||||
|
JsonFormat(ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(context.loginAccount.get), issue.isPullRequest))
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all labels for this repository
|
||||||
|
* https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/labels")(referrersOnly { repository =>
|
||||||
|
JsonFormat(getLabels(repository.owner, repository.name).map { label =>
|
||||||
|
ApiLabel(label, RepositoryName(repository))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single label
|
||||||
|
* https://developer.github.com/v3/issues/labels/#get-a-single-label
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/labels/:labelName")(referrersOnly { repository =>
|
||||||
|
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
||||||
|
JsonFormat(ApiLabel(label, RepositoryName(repository)))
|
||||||
|
} getOrElse NotFound()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a label
|
||||||
|
* https://developer.github.com/v3/issues/labels/#create-a-label
|
||||||
|
*/
|
||||||
|
post("/api/v3/repos/:owner/:repository/labels")(collaboratorsOnly { repository =>
|
||||||
|
(for{
|
||||||
|
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||||
|
} yield {
|
||||||
|
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||||
|
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
||||||
|
val labelId = createLabel(repository.owner, repository.name, data.name, data.color)
|
||||||
|
getLabel(repository.owner, repository.name, labelId).map { label =>
|
||||||
|
Created(JsonFormat(ApiLabel(label, RepositoryName(repository))))
|
||||||
|
} getOrElse NotFound()
|
||||||
|
} else {
|
||||||
|
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
||||||
|
UnprocessableEntity(ApiError(
|
||||||
|
"Validation Failed",
|
||||||
|
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) getOrElse NotFound()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a label
|
||||||
|
* https://developer.github.com/v3/issues/labels/#update-a-label
|
||||||
|
*/
|
||||||
|
patch("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
||||||
|
(for{
|
||||||
|
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||||
|
} yield {
|
||||||
|
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||||
|
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
||||||
|
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
||||||
|
updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color)
|
||||||
|
JsonFormat(ApiLabel(
|
||||||
|
getLabel(repository.owner, repository.name, label.labelId).get,
|
||||||
|
RepositoryName(repository)))
|
||||||
|
} else {
|
||||||
|
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
||||||
|
UnprocessableEntity(ApiError(
|
||||||
|
"Validation Failed",
|
||||||
|
Some("https://developer.github.com/v3/issues/labels/#create-a-label")))
|
||||||
|
}
|
||||||
|
} getOrElse NotFound()
|
||||||
|
}
|
||||||
|
}) getOrElse NotFound()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a label
|
||||||
|
* https://developer.github.com/v3/issues/labels/#delete-a-label
|
||||||
|
*/
|
||||||
|
delete("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
||||||
|
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||||
|
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
||||||
|
deleteLabel(repository.owner, repository.name, label.labelId)
|
||||||
|
NoContent()
|
||||||
|
} getOrElse NotFound()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/pulls/#list-pull-requests
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/pulls")(referrersOnly { repository =>
|
||||||
|
val page = IssueSearchCondition.page(request)
|
||||||
|
// TODO: more api spec condition
|
||||||
|
val condition = IssueSearchCondition(request)
|
||||||
|
val baseOwner = getAccountByUserName(repository.owner).get
|
||||||
|
val issues:List[(Issue, Account, Int, PullRequest, Repository, Account)] = searchPullRequestByApi(condition, (page - 1) * PullRequestLimit, PullRequestLimit, repository.owner -> repository.name)
|
||||||
|
JsonFormat(issues.map{case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner) =>
|
||||||
|
ApiPullRequest(
|
||||||
|
issue,
|
||||||
|
pullRequest,
|
||||||
|
ApiRepository(headRepo, ApiUser(headOwner)),
|
||||||
|
ApiRepository(repository, ApiUser(baseOwner)),
|
||||||
|
ApiUser(issueUser)) })
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/pulls/#get-a-single-pull-request
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/pulls/:id")(referrersOnly { repository =>
|
||||||
|
(for{
|
||||||
|
issueId <- params("id").toIntOpt
|
||||||
|
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||||
|
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set())
|
||||||
|
baseOwner <- users.get(repository.owner)
|
||||||
|
headOwner <- users.get(pullRequest.requestUserName)
|
||||||
|
issueUser <- users.get(issue.openedUserName)
|
||||||
|
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||||
|
} yield {
|
||||||
|
JsonFormat(ApiPullRequest(
|
||||||
|
issue,
|
||||||
|
pullRequest,
|
||||||
|
ApiRepository(headRepo, ApiUser(headOwner)),
|
||||||
|
ApiRepository(repository, ApiUser(baseOwner)),
|
||||||
|
ApiUser(issueUser)))
|
||||||
|
}).getOrElse(NotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository/pulls/:id/commits")(referrersOnly { repository =>
|
||||||
|
val owner = repository.owner
|
||||||
|
val name = repository.name
|
||||||
|
params("id").toIntOpt.flatMap{ issueId =>
|
||||||
|
getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
|
||||||
|
using(Git.open(getRepositoryDir(owner, name))){ git =>
|
||||||
|
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||||
|
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||||
|
val repoFullName = RepositoryName(repository)
|
||||||
|
val commits = git.log.addRange(oldId, newId).call.iterator.asScala.map(c => ApiCommitListItem(new CommitInfo(c), repoFullName)).toList
|
||||||
|
JsonFormat(commits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/#get
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repository")(referrersOnly { repository =>
|
||||||
|
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(repository.owner).get)))
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
||||||
|
*/
|
||||||
|
post("/api/v3/repos/:owner/:repo/statuses/:sha")(collaboratorsOnly { repository =>
|
||||||
|
(for{
|
||||||
|
ref <- params.get("sha")
|
||||||
|
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||||
|
data <- extractFromJsonBody[CreateAStatus] if data.isValid
|
||||||
|
creator <- context.loginAccount
|
||||||
|
state <- CommitState.valueOf(data.state)
|
||||||
|
statusId = createCommitStatus(repository.owner, repository.name, sha, data.context.getOrElse("default"),
|
||||||
|
state, data.target_url, data.description, new java.util.Date(), creator)
|
||||||
|
status <- getCommitStatus(repository.owner, repository.name, statusId)
|
||||||
|
} yield {
|
||||||
|
JsonFormat(ApiCommitStatus(status, ApiUser(creator)))
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
|
||||||
|
*
|
||||||
|
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
||||||
|
*/
|
||||||
|
val listStatusesRoute = get("/api/v3/repos/:owner/:repo/commits/:ref/statuses")(referrersOnly { repository =>
|
||||||
|
(for{
|
||||||
|
ref <- params.get("ref")
|
||||||
|
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||||
|
} yield {
|
||||||
|
JsonFormat(getCommitStatuesWithCreator(repository.owner, repository.name, sha).map{ case(status, creator) =>
|
||||||
|
ApiCommitStatus(status, ApiUser(creator))
|
||||||
|
})
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
|
||||||
|
*
|
||||||
|
* legacy route
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repo/statuses/:ref"){
|
||||||
|
listStatusesRoute.action()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
|
||||||
|
*
|
||||||
|
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
||||||
|
*/
|
||||||
|
get("/api/v3/repos/:owner/:repo/commits/:ref/status")(referrersOnly { repository =>
|
||||||
|
(for{
|
||||||
|
ref <- params.get("ref")
|
||||||
|
owner <- getAccountByUserName(repository.owner)
|
||||||
|
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||||
|
} yield {
|
||||||
|
val statuses = getCommitStatuesWithCreator(repository.owner, repository.name, sha)
|
||||||
|
JsonFormat(ApiCombinedCommitStatus(sha, statuses, ApiRepository(repository, owner)))
|
||||||
|
}) getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
private def isEditable(owner: String, repository: String, author: String)(implicit context: Context): Boolean =
|
||||||
|
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -121,16 +121,6 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
getAccountByUserName(params("userName")).isDefined
|
getAccountByUserName(params("userName")).isDefined
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://developer.github.com/v3/rate_limit/#get-your-current-rate-limit-status
|
|
||||||
* but not enabled.
|
|
||||||
*/
|
|
||||||
get("/api/v3/rate_limit"){
|
|
||||||
contentType = formats("json")
|
|
||||||
// this message is same as github enterprise...
|
|
||||||
org.scalatra.NotFound(ApiError("Rate limiting is not enabled."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Move to RepositoryViwerController?
|
// TODO Move to RepositoryViwerController?
|
||||||
post("/search", searchForm){ form =>
|
post("/search", searchForm){ form =>
|
||||||
redirect(s"/${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}")
|
redirect(s"/${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}")
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.api._
|
|
||||||
import gitbucket.core.issues.html
|
import gitbucket.core.issues.html
|
||||||
import gitbucket.core.model.Issue
|
|
||||||
import gitbucket.core.service.IssuesService._
|
import gitbucket.core.service.IssuesService._
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.util.ControlUtil._
|
import gitbucket.core.util.ControlUtil._
|
||||||
@@ -16,11 +14,11 @@ import org.scalatra.Ok
|
|||||||
|
|
||||||
|
|
||||||
class IssuesController extends IssuesControllerBase
|
class IssuesController extends IssuesControllerBase
|
||||||
with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService
|
with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService
|
||||||
|
|
||||||
trait IssuesControllerBase extends ControllerBase {
|
trait IssuesControllerBase extends ControllerBase {
|
||||||
self: IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService
|
self: IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService
|
||||||
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService =>
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with WebHookIssueCommentService =>
|
||||||
|
|
||||||
case class IssueCreateForm(title: String, content: Option[String],
|
case class IssueCreateForm(title: String, content: Option[String],
|
||||||
@@ -78,18 +76,6 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/issues/:id/comments")(referrersOnly { repository =>
|
|
||||||
(for{
|
|
||||||
issueId <- params("id").toIntOpt
|
|
||||||
comments = getCommentsForApi(repository.owner, repository.name, issueId.toInt)
|
|
||||||
} yield {
|
|
||||||
JsonFormat(comments.map{ case (issueComment, user, issue) => ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest) })
|
|
||||||
}).getOrElse(NotFound)
|
|
||||||
})
|
|
||||||
|
|
||||||
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
html.create(
|
html.create(
|
||||||
@@ -128,7 +114,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
getIssue(owner, name, issueId.toString).foreach { issue =>
|
getIssue(owner, name, issueId.toString).foreach { issue =>
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""))
|
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
|
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
|
||||||
@@ -150,7 +136,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
// update issue
|
// update issue
|
||||||
updateIssue(owner, name, issue.issueId, title, issue.content)
|
updateIssue(owner, name, issue.issueId, title, issue.content)
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue.copy(title = title), title)
|
createReferComment(owner, name, issue.copy(title = title), title, context.loginAccount.get)
|
||||||
|
|
||||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||||
} else Unauthorized
|
} else Unauthorized
|
||||||
@@ -165,7 +151,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
// update issue
|
// update issue
|
||||||
updateIssue(owner, name, issue.issueId, issue.title, content)
|
updateIssue(owner, name, issue.issueId, issue.title, content)
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue, content.getOrElse(""))
|
createReferComment(owner, name, issue, content.getOrElse(""), context.loginAccount.get)
|
||||||
|
|
||||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||||
} else Unauthorized
|
} else Unauthorized
|
||||||
@@ -174,30 +160,22 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
handleComment(form.issueId, Some(form.content), repository)() map { case (issue, id) =>
|
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||||
|
val actionOpt = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||||
|
handleComment(issue, Some(form.content), repository, actionOpt) map { case (issue, id) =>
|
||||||
redirect(s"/${repository.owner}/${repository.name}/${
|
redirect(s"/${repository.owner}/${repository.name}/${
|
||||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||||
|
}
|
||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/issues/comments/#create-a-comment
|
|
||||||
*/
|
|
||||||
post("/api/v3/repos/:owner/:repository/issues/:id/comments")(readableUsersOnly { repository =>
|
|
||||||
(for{
|
|
||||||
issueId <- params("id").toIntOpt
|
|
||||||
body <- extractFromJsonBody[CreateAComment].map(_.body) if ! body.isEmpty
|
|
||||||
(issue, id) <- handleComment(issueId, Some(body), repository)()
|
|
||||||
issueComment <- getComment(repository.owner, repository.name, id.toString())
|
|
||||||
} yield {
|
|
||||||
JsonFormat(ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(context.loginAccount.get), issue.isPullRequest))
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
||||||
handleComment(form.issueId, form.content, repository)() map { case (issue, id) =>
|
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||||
|
val actionOpt = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||||
|
handleComment(issue, form.content, repository, actionOpt) map { case (issue, id) =>
|
||||||
redirect(s"/${repository.owner}/${repository.name}/${
|
redirect(s"/${repository.owner}/${repository.name}/${
|
||||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||||
|
}
|
||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -315,8 +293,16 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
post("/:owner/:repository/issues/batchedit/state")(collaboratorsOnly { repository =>
|
post("/:owner/:repository/issues/batchedit/state")(collaboratorsOnly { repository =>
|
||||||
defining(params.get("value")){ action =>
|
defining(params.get("value")){ action =>
|
||||||
action match {
|
action match {
|
||||||
case Some("open") => executeBatch(repository) { handleComment(_, None, repository)( _ => Some("reopen")) }
|
case Some("open") => executeBatch(repository) { issueId =>
|
||||||
case Some("close") => executeBatch(repository) { handleComment(_, None, repository)( _ => Some("close")) }
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
|
handleComment(issue, None, repository, Some("reopen"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Some("close") => executeBatch(repository) { issueId =>
|
||||||
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
|
handleComment(issue, None, repository, Some("close"))
|
||||||
|
}
|
||||||
|
}
|
||||||
case _ => // TODO BadRequest
|
case _ => // TODO BadRequest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,99 +359,6 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Same method exists in PullRequestController. Should it moved to IssueService?
|
|
||||||
private def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String) = {
|
|
||||||
StringUtil.extractIssueId(message).foreach { issueId =>
|
|
||||||
val content = fromIssue.issueId + ":" + fromIssue.title
|
|
||||||
if(getIssue(owner, repository, issueId).isDefined){
|
|
||||||
// Not add if refer comment already exist.
|
|
||||||
if(!getComments(owner, repository, issueId.toInt).exists { x => x.action == "refer" && x.content == content }) {
|
|
||||||
createComment(owner, repository, context.loginAccount.get.userName, issueId.toInt, content, "refer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see [[https://github.com/takezoe/gitbucket/wiki/CommentAction]]
|
|
||||||
*/
|
|
||||||
private def handleComment(issueId: Int, content: Option[String], repository: RepositoryService.RepositoryInfo)
|
|
||||||
(getAction: Issue => Option[String] =
|
|
||||||
p1 => params.get("action").filter(_ => isEditable(p1.userName, p1.repositoryName, p1.openedUserName))) = {
|
|
||||||
|
|
||||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
|
||||||
val userName = context.loginAccount.get.userName
|
|
||||||
|
|
||||||
getIssue(owner, name, issueId.toString) flatMap { issue =>
|
|
||||||
val (action, recordActivity) =
|
|
||||||
getAction(issue)
|
|
||||||
.collect {
|
|
||||||
case "close" if(!issue.closed) => true ->
|
|
||||||
(Some("close") -> Some(if(issue.isPullRequest) recordClosePullRequestActivity _ else recordCloseIssueActivity _))
|
|
||||||
case "reopen" if(issue.closed) => false ->
|
|
||||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
|
||||||
}
|
|
||||||
.map { case (closed, t) =>
|
|
||||||
updateClosed(owner, name, issueId, closed)
|
|
||||||
t
|
|
||||||
}
|
|
||||||
.getOrElse(None -> None)
|
|
||||||
|
|
||||||
val commentId = (content, action) match {
|
|
||||||
case (None, None) => None
|
|
||||||
case (None, Some(action)) => Some(createComment(owner, name, userName, issueId, action.capitalize, action))
|
|
||||||
case (Some(content), _) => Some(createComment(owner, name, userName, issueId, content, action.map(_+ "_comment").getOrElse("comment")))
|
|
||||||
}
|
|
||||||
|
|
||||||
// record comment activity if comment is entered
|
|
||||||
content foreach {
|
|
||||||
(if(issue.isPullRequest) recordCommentPullRequestActivity _ else recordCommentIssueActivity _)
|
|
||||||
(owner, name, userName, issueId, _)
|
|
||||||
}
|
|
||||||
recordActivity foreach ( _ (owner, name, userName, issueId, issue.title) )
|
|
||||||
|
|
||||||
// extract references and create refer comment
|
|
||||||
content.map { content =>
|
|
||||||
createReferComment(owner, name, issue, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
// call web hooks
|
|
||||||
action match {
|
|
||||||
case None => commentId.map{ commentIdSome => callIssueCommentWebHook(repository, issue, commentIdSome, context.loginAccount.get) }
|
|
||||||
case Some(act) => val webHookAction = act match {
|
|
||||||
case "open" => "opened"
|
|
||||||
case "reopen" => "reopened"
|
|
||||||
case "close" => "closed"
|
|
||||||
case _ => act
|
|
||||||
}
|
|
||||||
if(issue.isPullRequest){
|
|
||||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, context.loginAccount.get)
|
|
||||||
} else {
|
|
||||||
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, context.loginAccount.get)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notifications
|
|
||||||
Notifier() match {
|
|
||||||
case f =>
|
|
||||||
content foreach {
|
|
||||||
f.toNotify(repository, issue, _){
|
|
||||||
Notifier.msgComment(s"${context.baseUrl}/${owner}/${name}/${
|
|
||||||
if(issue.isPullRequest) "pull" else "issues"}/${issueId}#comment-${commentId.get}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action foreach {
|
|
||||||
f.toNotify(repository, issue, _){
|
|
||||||
Notifier.msgStatus(s"${context.baseUrl}/${owner}/${name}/issues/${issueId}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commentId.map( issue -> _ )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def searchIssues(repository: RepositoryService.RepositoryInfo) = {
|
private def searchIssues(repository: RepositoryService.RepositoryInfo) = {
|
||||||
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
||||||
val page = IssueSearchCondition.page(request)
|
val page = IssueSearchCondition.page(request)
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.api.{ApiError, CreateALabel, ApiLabel, JsonFormat}
|
|
||||||
import gitbucket.core.issues.labels.html
|
import gitbucket.core.issues.labels.html
|
||||||
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService}
|
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService}
|
||||||
import gitbucket.core.util.{LockUtil, RepositoryName, ReferrerAuthenticator, CollaboratorsAuthenticator}
|
import gitbucket.core.util.{ReferrerAuthenticator, CollaboratorsAuthenticator}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import io.github.gitbucket.scalatra.forms._
|
import io.github.gitbucket.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import org.scalatra.{NoContent, UnprocessableEntity, Created, Ok}
|
import org.scalatra.Ok
|
||||||
|
|
||||||
class LabelsController extends LabelsControllerBase
|
class LabelsController extends LabelsControllerBase
|
||||||
with LabelsService with IssuesService with RepositoryService with AccountService
|
with LabelsService with IssuesService with RepositoryService with AccountService
|
||||||
@@ -32,26 +31,6 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* List all labels for this repository
|
|
||||||
* https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/labels")(referrersOnly { repository =>
|
|
||||||
JsonFormat(getLabels(repository.owner, repository.name).map { label =>
|
|
||||||
ApiLabel(label, RepositoryName(repository))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a single label
|
|
||||||
* https://developer.github.com/v3/issues/labels/#get-a-single-label
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/labels/:labelName")(referrersOnly { repository =>
|
|
||||||
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
|
||||||
JsonFormat(ApiLabel(label, RepositoryName(repository)))
|
|
||||||
} getOrElse NotFound()
|
|
||||||
})
|
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/issues/labels/new")(collaboratorsOnly { repository =>
|
ajaxGet("/:owner/:repository/issues/labels/new")(collaboratorsOnly { repository =>
|
||||||
html.edit(None, repository)
|
html.edit(None, repository)
|
||||||
})
|
})
|
||||||
@@ -66,31 +45,6 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a label
|
|
||||||
* https://developer.github.com/v3/issues/labels/#create-a-label
|
|
||||||
*/
|
|
||||||
post("/api/v3/repos/:owner/:repository/labels")(collaboratorsOnly { repository =>
|
|
||||||
(for{
|
|
||||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
|
||||||
} yield {
|
|
||||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
|
||||||
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
|
||||||
val labelId = createLabel(repository.owner, repository.name, data.name, data.color)
|
|
||||||
getLabel(repository.owner, repository.name, labelId).map { label =>
|
|
||||||
Created(JsonFormat(ApiLabel(label, RepositoryName(repository))))
|
|
||||||
} getOrElse NotFound()
|
|
||||||
} else {
|
|
||||||
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
|
||||||
UnprocessableEntity(ApiError(
|
|
||||||
"Validation Failed",
|
|
||||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) getOrElse NotFound()
|
|
||||||
})
|
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(collaboratorsOnly { repository =>
|
ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(collaboratorsOnly { repository =>
|
||||||
getLabel(repository.owner, repository.name, params("labelId").toInt).map { label =>
|
getLabel(repository.owner, repository.name, params("labelId").toInt).map { label =>
|
||||||
html.edit(Some(label), repository)
|
html.edit(Some(label), repository)
|
||||||
@@ -107,50 +61,11 @@ trait LabelsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a label
|
|
||||||
* https://developer.github.com/v3/issues/labels/#update-a-label
|
|
||||||
*/
|
|
||||||
patch("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
|
||||||
(for{
|
|
||||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
|
||||||
} yield {
|
|
||||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
|
||||||
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
|
||||||
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
|
||||||
updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color)
|
|
||||||
JsonFormat(ApiLabel(
|
|
||||||
getLabel(repository.owner, repository.name, label.labelId).get,
|
|
||||||
RepositoryName(repository)))
|
|
||||||
} else {
|
|
||||||
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
|
||||||
UnprocessableEntity(ApiError(
|
|
||||||
"Validation Failed",
|
|
||||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")))
|
|
||||||
}
|
|
||||||
} getOrElse NotFound()
|
|
||||||
}
|
|
||||||
}) getOrElse NotFound()
|
|
||||||
})
|
|
||||||
|
|
||||||
ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(collaboratorsOnly { repository =>
|
ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(collaboratorsOnly { repository =>
|
||||||
deleteLabel(repository.owner, repository.name, params("labelId").toInt)
|
deleteLabel(repository.owner, repository.name, params("labelId").toInt)
|
||||||
Ok()
|
Ok()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a label
|
|
||||||
* https://developer.github.com/v3/issues/labels/#delete-a-label
|
|
||||||
*/
|
|
||||||
delete("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository =>
|
|
||||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
|
||||||
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
|
||||||
deleteLabel(repository.owner, repository.name, label.labelId)
|
|
||||||
NoContent()
|
|
||||||
} getOrElse NotFound()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint for the identifier such as user name, repository name or page name.
|
* Constraint for the identifier such as user name, repository name or page name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.api._
|
import gitbucket.core.model.WebHook
|
||||||
import gitbucket.core.model.{Account, CommitStatus, CommitState, Repository, PullRequest, Issue, WebHook}
|
|
||||||
import gitbucket.core.pulls.html
|
import gitbucket.core.pulls.html
|
||||||
import gitbucket.core.service.CommitStatusService
|
import gitbucket.core.service.CommitStatusService
|
||||||
import gitbucket.core.service.MergeService
|
import gitbucket.core.service.MergeService
|
||||||
@@ -82,24 +81,6 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/pulls/#list-pull-requests
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/pulls")(referrersOnly { repository =>
|
|
||||||
val page = IssueSearchCondition.page(request)
|
|
||||||
// TODO: more api spec condition
|
|
||||||
val condition = IssueSearchCondition(request)
|
|
||||||
val baseOwner = getAccountByUserName(repository.owner).get
|
|
||||||
val issues:List[(Issue, Account, Int, PullRequest, Repository, Account)] = searchPullRequestByApi(condition, (page - 1) * PullRequestLimit, PullRequestLimit, repository.owner -> repository.name)
|
|
||||||
JsonFormat(issues.map{case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner) =>
|
|
||||||
ApiPullRequest(
|
|
||||||
issue,
|
|
||||||
pullRequest,
|
|
||||||
ApiRepository(headRepo, ApiUser(headOwner)),
|
|
||||||
ApiRepository(repository, ApiUser(baseOwner)),
|
|
||||||
ApiUser(issueUser)) })
|
|
||||||
})
|
|
||||||
|
|
||||||
get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
|
get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
|
||||||
params("id").toIntOpt.flatMap{ issueId =>
|
params("id").toIntOpt.flatMap{ issueId =>
|
||||||
val owner = repository.owner
|
val owner = repository.owner
|
||||||
@@ -126,47 +107,6 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/pulls/#get-a-single-pull-request
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/pulls/:id")(referrersOnly { repository =>
|
|
||||||
(for{
|
|
||||||
issueId <- params("id").toIntOpt
|
|
||||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
|
||||||
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set())
|
|
||||||
baseOwner <- users.get(repository.owner)
|
|
||||||
headOwner <- users.get(pullRequest.requestUserName)
|
|
||||||
issueUser <- users.get(issue.openedUserName)
|
|
||||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
|
||||||
} yield {
|
|
||||||
JsonFormat(ApiPullRequest(
|
|
||||||
issue,
|
|
||||||
pullRequest,
|
|
||||||
ApiRepository(headRepo, ApiUser(headOwner)),
|
|
||||||
ApiRepository(repository, ApiUser(baseOwner)),
|
|
||||||
ApiUser(issueUser)))
|
|
||||||
}).getOrElse(NotFound)
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository/pulls/:id/commits")(referrersOnly { repository =>
|
|
||||||
val owner = repository.owner
|
|
||||||
val name = repository.name
|
|
||||||
params("id").toIntOpt.flatMap{ issueId =>
|
|
||||||
getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
|
|
||||||
using(Git.open(getRepositoryDir(owner, name))){ git =>
|
|
||||||
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
|
||||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
|
||||||
val repoFullName = RepositoryName(repository)
|
|
||||||
val commits = git.log.addRange(oldId, newId).call.iterator.asScala.map(c => ApiCommitListItem(new CommitInfo(c), repoFullName)).toList
|
|
||||||
JsonFormat(commits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
ajaxGet("/:owner/:repository/pull/:id/mergeguide")(referrersOnly { repository =>
|
ajaxGet("/:owner/:repository/pull/:id/mergeguide")(referrersOnly { repository =>
|
||||||
params("id").toIntOpt.flatMap{ issueId =>
|
params("id").toIntOpt.flatMap{ issueId =>
|
||||||
val owner = repository.owner
|
val owner = repository.owner
|
||||||
@@ -523,7 +463,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
getIssue(owner, name, issueId.toString) foreach { issue =>
|
getIssue(owner, name, issueId.toString) foreach { issue =>
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""))
|
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
||||||
@@ -535,19 +475,6 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO Same method exists in IssueController. Should it moved to IssueService?
|
|
||||||
private def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String) = {
|
|
||||||
StringUtil.extractIssueId(message).foreach { issueId =>
|
|
||||||
val content = fromIssue.issueId + ":" + fromIssue.title
|
|
||||||
if(getIssue(owner, repository, issueId).isDefined){
|
|
||||||
// Not add if refer comment already exist.
|
|
||||||
if(!getComments(owner, repository, issueId.toInt).exists { x => x.action == "refer" && x.content == content }) {
|
|
||||||
createComment(owner, repository, context.loginAccount.get.userName, issueId.toInt, content, "refer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses branch identifier and extracts owner and branch name as tuple.
|
* Parses branch identifier and extracts owner and branch name as tuple.
|
||||||
*
|
*
|
||||||
@@ -611,14 +538,4 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
hasWritePermission(owner, repoName, context.loginAccount))
|
hasWritePermission(owner, repoName, context.loginAccount))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: same as gitbucket.core.servlet.CommitLogHook ...
|
|
||||||
private def createIssueComment(owner: String, repository: String, commit: CommitInfo) = {
|
|
||||||
StringUtil.extractIssueId(commit.fullMessage).foreach { issueId =>
|
|
||||||
if(getIssue(owner, repository, issueId).isDefined){
|
|
||||||
getAccountByMailAddress(commit.committerEmailAddress).foreach { account =>
|
|
||||||
createComment(owner, repository, account.userName, issueId.toInt, commit.fullMessage + " " + commit.id, "commit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,22 +142,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
|
||||||
patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository =>
|
|
||||||
import gitbucket.core.api._
|
|
||||||
(for{
|
|
||||||
branch <- params.get("branch") if repository.branchList.find(_ == branch).isDefined
|
|
||||||
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
|
||||||
} yield {
|
|
||||||
if(protection.enabled){
|
|
||||||
enableBranchProtection(repository.owner, repository.name, branch, protection.status.enforcement_level == ApiBranchProtection.Everyone, protection.status.contexts)
|
|
||||||
} else {
|
|
||||||
disableBranchProtection(repository.owner, repository.name, branch)
|
|
||||||
}
|
|
||||||
JsonFormat(ApiBranch(branch, protection)(RepositoryName(repository)))
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the Collaborators page.
|
* Display the Collaborators page.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||||
|
|
||||||
import gitbucket.core.api._
|
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.repo.html
|
import gitbucket.core.repo.html
|
||||||
import gitbucket.core.helper
|
import gitbucket.core.helper
|
||||||
@@ -13,7 +12,7 @@ import gitbucket.core.util.StringUtil._
|
|||||||
import gitbucket.core.util.ControlUtil._
|
import gitbucket.core.util.ControlUtil._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.model.{Account, CommitState, WebHook}
|
import gitbucket.core.model.{Account, WebHook}
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService._
|
||||||
import gitbucket.core.view
|
import gitbucket.core.view
|
||||||
import gitbucket.core.view.helpers
|
import gitbucket.core.view.helpers
|
||||||
@@ -122,13 +121,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
fileList(_)
|
fileList(_)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/repos/#get
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repository")(referrersOnly { repository =>
|
|
||||||
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(repository.owner).get)))
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the file list of the specified path and branch.
|
* Displays the file list of the specified path and branch.
|
||||||
*/
|
*/
|
||||||
@@ -160,65 +152,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
|
||||||
*/
|
|
||||||
post("/api/v3/repos/:owner/:repo/statuses/:sha")(collaboratorsOnly { repository =>
|
|
||||||
(for{
|
|
||||||
ref <- params.get("sha")
|
|
||||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
|
||||||
data <- extractFromJsonBody[CreateAStatus] if data.isValid
|
|
||||||
creator <- context.loginAccount
|
|
||||||
state <- CommitState.valueOf(data.state)
|
|
||||||
statusId = createCommitStatus(repository.owner, repository.name, sha, data.context.getOrElse("default"),
|
|
||||||
state, data.target_url, data.description, new java.util.Date(), creator)
|
|
||||||
status <- getCommitStatus(repository.owner, repository.name, statusId)
|
|
||||||
} yield {
|
|
||||||
JsonFormat(ApiCommitStatus(status, ApiUser(creator)))
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
|
|
||||||
*
|
|
||||||
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
|
||||||
*/
|
|
||||||
val listStatusesRoute = get("/api/v3/repos/:owner/:repo/commits/:ref/statuses")(referrersOnly { repository =>
|
|
||||||
(for{
|
|
||||||
ref <- params.get("ref")
|
|
||||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
|
||||||
} yield {
|
|
||||||
JsonFormat(getCommitStatuesWithCreator(repository.owner, repository.name, sha).map{ case(status, creator) =>
|
|
||||||
ApiCommitStatus(status, ApiUser(creator))
|
|
||||||
})
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
|
|
||||||
*
|
|
||||||
* legacy route
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repo/statuses/:ref"){
|
|
||||||
listStatusesRoute.action()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
|
|
||||||
*
|
|
||||||
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
|
||||||
*/
|
|
||||||
get("/api/v3/repos/:owner/:repo/commits/:ref/status")(referrersOnly { repository =>
|
|
||||||
(for{
|
|
||||||
ref <- params.get("ref")
|
|
||||||
owner <- getAccountByUserName(repository.owner)
|
|
||||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
|
||||||
} yield {
|
|
||||||
val statuses = getCommitStatuesWithCreator(repository.owner, repository.name, sha)
|
|
||||||
JsonFormat(ApiCombinedCommitStatus(sha, statuses, ApiRepository(repository, owner)))
|
|
||||||
}) getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
get("/:owner/:repository/new/*")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/new/*")(collaboratorsOnly { repository =>
|
||||||
val (branch, path) = splitPath(repository, multiParams("splat").head)
|
val (branch, path) = splitPath(repository, multiParams("splat").head)
|
||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package gitbucket.core.service
|
||||||
|
|
||||||
|
import gitbucket.core.controller.Context
|
||||||
|
import gitbucket.core.model.Issue
|
||||||
|
import gitbucket.core.model.Profile._
|
||||||
|
import gitbucket.core.util.ControlUtil._
|
||||||
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.Notifier
|
||||||
|
import profile.simple._
|
||||||
|
|
||||||
|
trait HandleCommentService {
|
||||||
|
self: RepositoryService with IssuesService with ActivityService
|
||||||
|
with WebHookService with WebHookIssueCommentService with WebHookPullRequestService =>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [[https://github.com/takezoe/gitbucket/wiki/CommentAction]]
|
||||||
|
*/
|
||||||
|
def handleComment(issue: Issue, content: Option[String], repository: RepositoryService.RepositoryInfo, actionOpt: Option[String])
|
||||||
|
(implicit context: Context, s: Session) = {
|
||||||
|
|
||||||
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
|
val userName = context.loginAccount.get.userName
|
||||||
|
|
||||||
|
val (action, recordActivity) = actionOpt
|
||||||
|
.collect {
|
||||||
|
case "close" if(!issue.closed) => true ->
|
||||||
|
(Some("close") -> Some(if(issue.isPullRequest) recordClosePullRequestActivity _ else recordCloseIssueActivity _))
|
||||||
|
case "reopen" if(issue.closed) => false ->
|
||||||
|
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
||||||
|
}
|
||||||
|
.map { case (closed, t) =>
|
||||||
|
updateClosed(owner, name, issue.issueId, closed)
|
||||||
|
t
|
||||||
|
}
|
||||||
|
.getOrElse(None -> None)
|
||||||
|
|
||||||
|
val commentId = (content, action) match {
|
||||||
|
case (None, None) => None
|
||||||
|
case (None, Some(action)) => Some(createComment(owner, name, userName, issue.issueId, action.capitalize, action))
|
||||||
|
case (Some(content), _) => Some(createComment(owner, name, userName, issue.issueId, content, action.map(_+ "_comment").getOrElse("comment")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// record comment activity if comment is entered
|
||||||
|
content foreach {
|
||||||
|
(if(issue.isPullRequest) recordCommentPullRequestActivity _ else recordCommentIssueActivity _)
|
||||||
|
(owner, name, userName, issue.issueId, _)
|
||||||
|
}
|
||||||
|
recordActivity foreach ( _ (owner, name, userName, issue.issueId, issue.title) )
|
||||||
|
|
||||||
|
// extract references and create refer comment
|
||||||
|
content.map { content =>
|
||||||
|
createReferComment(owner, name, issue, content, context.loginAccount.get)
|
||||||
|
}
|
||||||
|
|
||||||
|
// call web hooks
|
||||||
|
action match {
|
||||||
|
case None => commentId.map{ commentIdSome => callIssueCommentWebHook(repository, issue, commentIdSome, context.loginAccount.get) }
|
||||||
|
case Some(act) => val webHookAction = act match {
|
||||||
|
case "open" => "opened"
|
||||||
|
case "reopen" => "reopened"
|
||||||
|
case "close" => "closed"
|
||||||
|
case _ => act
|
||||||
|
}
|
||||||
|
if(issue.isPullRequest){
|
||||||
|
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, context.loginAccount.get)
|
||||||
|
} else {
|
||||||
|
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, context.loginAccount.get)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifications
|
||||||
|
Notifier() match {
|
||||||
|
case f =>
|
||||||
|
content foreach {
|
||||||
|
f.toNotify(repository, issue, _){
|
||||||
|
Notifier.msgComment(s"${context.baseUrl}/${owner}/${name}/${
|
||||||
|
if(issue.isPullRequest) "pull" else "issues"}/${issue.issueId}#comment-${commentId.get}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action foreach {
|
||||||
|
f.toNotify(repository, issue, _){
|
||||||
|
Notifier.msgStatus(s"${context.baseUrl}/${owner}/${name}/issues/${issue.issueId}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commentId.map( issue -> _ )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
|
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
|
import gitbucket.core.util.StringUtil
|
||||||
import profile.simple._
|
import profile.simple._
|
||||||
|
|
||||||
import gitbucket.core.util.StringUtil._
|
import gitbucket.core.util.StringUtil._
|
||||||
@@ -12,6 +14,7 @@ import Q.interpolation
|
|||||||
|
|
||||||
|
|
||||||
trait IssuesService {
|
trait IssuesService {
|
||||||
|
self: AccountService =>
|
||||||
import IssuesService._
|
import IssuesService._
|
||||||
|
|
||||||
def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
|
def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
|
||||||
@@ -394,6 +397,29 @@ trait IssuesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String, loginAccount: Account)(implicit s: Session) = {
|
||||||
|
StringUtil.extractIssueId(message).foreach { issueId =>
|
||||||
|
val content = fromIssue.issueId + ":" + fromIssue.title
|
||||||
|
if(getIssue(owner, repository, issueId).isDefined){
|
||||||
|
// Not add if refer comment already exist.
|
||||||
|
if(!getComments(owner, repository, issueId.toInt).exists { x => x.action == "refer" && x.content == content }) {
|
||||||
|
createComment(owner, repository, loginAccount.userName, issueId.toInt, content, "refer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def createIssueComment(owner: String, repository: String, commit: CommitInfo)(implicit s: Session) = {
|
||||||
|
StringUtil.extractIssueId(commit.fullMessage).foreach { issueId =>
|
||||||
|
if(getIssue(owner, repository, issueId).isDefined){
|
||||||
|
getAccountByMailAddress(commit.committerEmailAddress).foreach { account =>
|
||||||
|
createComment(owner, repository, account.userName, issueId.toInt, commit.fullMessage + " " + commit.id, "commit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object IssuesService {
|
object IssuesService {
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package gitbucket.core.service
|
||||||
|
|
||||||
|
import gitbucket.core.model.Profile._
|
||||||
|
import gitbucket.core.util.ControlUtil._
|
||||||
|
import gitbucket.core.util.Directory._
|
||||||
|
import gitbucket.core.util.JGitUtil
|
||||||
|
import gitbucket.core.model.Account
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.dircache.DirCache
|
||||||
|
import org.eclipse.jgit.lib.{FileMode, Constants}
|
||||||
|
import profile.simple._
|
||||||
|
|
||||||
|
trait RepositoryCreationService {
|
||||||
|
self: AccountService with RepositoryService with LabelsService with WikiService with ActivityService =>
|
||||||
|
|
||||||
|
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
|
||||||
|
(implicit s: Session) {
|
||||||
|
val ownerAccount = getAccountByUserName(owner).get
|
||||||
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
|
// Insert to the database at first
|
||||||
|
insertRepository(name, owner, description, isPrivate)
|
||||||
|
|
||||||
|
// Add collaborators for group repository
|
||||||
|
if(ownerAccount.isGroupAccount){
|
||||||
|
getGroupMembers(owner).foreach { member =>
|
||||||
|
addCollaborator(owner, name, member.userName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert default labels
|
||||||
|
insertDefaultLabels(owner, name)
|
||||||
|
|
||||||
|
// Create the actual repository
|
||||||
|
val gitdir = getRepositoryDir(owner, name)
|
||||||
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
|
if(createReadme){
|
||||||
|
using(Git.open(gitdir)){ git =>
|
||||||
|
val builder = DirCache.newInCore.builder()
|
||||||
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
val content = if(description.nonEmpty){
|
||||||
|
name + "\n" +
|
||||||
|
"===============\n" +
|
||||||
|
"\n" +
|
||||||
|
description.get
|
||||||
|
} else {
|
||||||
|
name + "\n" +
|
||||||
|
"===============\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
||||||
|
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
||||||
|
builder.finish()
|
||||||
|
|
||||||
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
||||||
|
Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Wiki repository
|
||||||
|
createWikiRepository(loginAccount, owner, name)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
recordCreateRepositoryActivity(owner, name, loginUserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
def insertDefaultLabels(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||||
|
createLabel(userName, repositoryName, "bug", "fc2929")
|
||||||
|
createLabel(userName, repositoryName, "duplicate", "cccccc")
|
||||||
|
createLabel(userName, repositoryName, "enhancement", "84b6eb")
|
||||||
|
createLabel(userName, repositoryName, "invalid", "e6e6e6")
|
||||||
|
createLabel(userName, repositoryName, "question", "cc317c")
|
||||||
|
createLabel(userName, repositoryName, "wontfix", "ffffff")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
* @param originRepositoryName specify for the forked repository. (default is None)
|
* @param originRepositoryName specify for the forked repository. (default is None)
|
||||||
* @param originUserName specify for the forked repository. (default is None)
|
* @param originUserName specify for the forked repository. (default is None)
|
||||||
*/
|
*/
|
||||||
def createRepository(repositoryName: String, userName: String, description: Option[String], isPrivate: Boolean,
|
def insertRepository(repositoryName: String, userName: String, description: Option[String], isPrivate: Boolean,
|
||||||
originRepositoryName: Option[String] = None, originUserName: Option[String] = None,
|
originRepositoryName: Option[String] = None, originUserName: Option[String] = None,
|
||||||
parentRepositoryName: Option[String] = None, parentUserName: Option[String] = None)
|
parentRepositoryName: Option[String] = None, parentUserName: Option[String] = None)
|
||||||
(implicit s: Session): Unit = {
|
(implicit s: Session): Unit = {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import gitbucket.core.service.WebHookService._
|
|||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.util.ControlUtil._
|
import gitbucket.core.util.ControlUtil._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
@@ -168,7 +167,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
if (!existIds.contains(commit.id) && !pushedIds.contains(commit.id)) {
|
if (!existIds.contains(commit.id) && !pushedIds.contains(commit.id)) {
|
||||||
if (issueCount > 0) {
|
if (issueCount > 0) {
|
||||||
pushedIds.add(commit.id)
|
pushedIds.add(commit.id)
|
||||||
createIssueComment(commit)
|
createIssueComment(owner, repository, commit)
|
||||||
// close issues
|
// close issues
|
||||||
if(refName(1) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE){
|
if(refName(1) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE){
|
||||||
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository)
|
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository)
|
||||||
@@ -230,13 +229,4 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def createIssueComment(commit: CommitInfo) = {
|
|
||||||
StringUtil.extractIssueId(commit.fullMessage).foreach { issueId =>
|
|
||||||
if(getIssue(owner, repository, issueId).isDefined){
|
|
||||||
getAccountByMailAddress(commit.committerEmailAddress).foreach { account =>
|
|
||||||
createComment(owner, repository, account.userName, issueId.toInt, commit.fullMessage + " " + commit.id, "commit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class CommitStatusServiceSpec extends FunSuite with ServiceSpecBase with CommitS
|
|||||||
now = fixture1.registeredDate)
|
now = fixture1.registeredDate)
|
||||||
test("createCommitState can insert and update") { withTestDB { implicit session =>
|
test("createCommitState can insert and update") { withTestDB { implicit session =>
|
||||||
val tester = generateNewAccount(fixture1.creator)
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
insertRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
val id = generateFixture1(tester:Account)
|
val id = generateFixture1(tester:Account)
|
||||||
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
|
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
|
||||||
// other one can update
|
// other one can update
|
||||||
@@ -60,14 +60,14 @@ class CommitStatusServiceSpec extends FunSuite with ServiceSpecBase with CommitS
|
|||||||
|
|
||||||
test("getCommitStatus can find by commitId and context") { withTestDB { implicit session =>
|
test("getCommitStatus can find by commitId and context") { withTestDB { implicit session =>
|
||||||
val tester = generateNewAccount(fixture1.creator)
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
insertRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
val id = generateFixture1(tester:Account)
|
val id = generateFixture1(tester:Account)
|
||||||
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, fixture1.commitId, fixture1.context) == Some(fixture1.copy(commitStatusId=id)))
|
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, fixture1.commitId, fixture1.context) == Some(fixture1.copy(commitStatusId=id)))
|
||||||
}}
|
}}
|
||||||
|
|
||||||
test("getCommitStatus can find by commitStatusId") { withTestDB { implicit session =>
|
test("getCommitStatus can find by commitStatusId") { withTestDB { implicit session =>
|
||||||
val tester = generateNewAccount(fixture1.creator)
|
val tester = generateNewAccount(fixture1.creator)
|
||||||
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
insertRepository(fixture1.repositoryName,fixture1.userName,None,false)
|
||||||
val id = generateFixture1(tester:Account)
|
val id = generateFixture1(tester:Account)
|
||||||
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
|
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package gitbucket.core.service
|
|||||||
import gitbucket.core.model._
|
import gitbucket.core.model._
|
||||||
import org.scalatest.FunSpec
|
import org.scalatest.FunSpec
|
||||||
|
|
||||||
class PullRequestServiceSpec extends FunSpec with ServiceSpecBase with PullRequestService with IssuesService {
|
class PullRequestServiceSpec extends FunSpec with ServiceSpecBase with PullRequestService with IssuesService with AccountService {
|
||||||
|
|
||||||
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
|
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.scalatest.FunSuite
|
|||||||
class RepositoryServiceSpec extends FunSuite with ServiceSpecBase with RepositoryService with AccountService{
|
class RepositoryServiceSpec extends FunSuite with ServiceSpecBase with RepositoryService with AccountService{
|
||||||
test("renameRepository can rename CommitState, ProtectedBranches") { withTestDB { implicit session =>
|
test("renameRepository can rename CommitState, ProtectedBranches") { withTestDB { implicit session =>
|
||||||
val tester = generateNewAccount("tester")
|
val tester = generateNewAccount("tester")
|
||||||
createRepository("repo", "root", None, false)
|
insertRepository("repo", "root", None, false)
|
||||||
val service = new CommitStatusService with ProtectedBranchService {}
|
val service = new CommitStatusService with ProtectedBranchService {}
|
||||||
val id = service.createCommitStatus(
|
val id = service.createCommitStatus(
|
||||||
userName = "root",
|
userName = "root",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ trait ServiceSpecBase {
|
|||||||
|
|
||||||
def generateNewUserWithDBRepository(userName:String, repositoryName:String)(implicit s:Session):Account = {
|
def generateNewUserWithDBRepository(userName:String, repositoryName:String)(implicit s:Session):Account = {
|
||||||
val ac = AccountService.getAccountByUserName(userName).getOrElse(generateNewAccount(userName))
|
val ac = AccountService.getAccountByUserName(userName).getOrElse(generateNewAccount(userName))
|
||||||
dummyService.createRepository(repositoryName, userName, None, false)
|
dummyService.insertRepository(repositoryName, userName, None, false)
|
||||||
ac
|
ac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user