add api 'create an issue'

This commit is contained in:
nazoking
2017-01-05 04:37:04 +09:00
parent f2a213f32a
commit ecde6aefbf
7 changed files with 119 additions and 66 deletions

View File

@@ -0,0 +1,11 @@
package gitbucket.core.api
/**
* https://developer.github.com/v3/issues/#create-an-issue
*/
case class CreateAnIssue(
title: String,
body: Option[String],
assignees: List[String],
milestone: Option[Int],
labels: List[String])

View File

@@ -21,10 +21,12 @@ class ApiController extends ApiControllerBase
with ProtectedBranchService with ProtectedBranchService
with IssuesService with IssuesService
with LabelsService with LabelsService
with MilestonesService
with PullRequestService with PullRequestService
with CommitsService with CommitsService
with CommitStatusService with CommitStatusService
with RepositoryCreationService with RepositoryCreationService
with IssueCreationService
with HandleCommentService with HandleCommentService
with WebHookService with WebHookService
with WebHookPullRequestService with WebHookPullRequestService
@@ -44,9 +46,11 @@ trait ApiControllerBase extends ControllerBase {
with ProtectedBranchService with ProtectedBranchService
with IssuesService with IssuesService
with LabelsService with LabelsService
with MilestonesService
with PullRequestService with PullRequestService
with CommitStatusService with CommitStatusService
with RepositoryCreationService with RepositoryCreationService
with IssueCreationService
with HandleCommentService with HandleCommentService
with OwnerAuthenticator with OwnerAuthenticator
with UsersAuthenticator with UsersAuthenticator
@@ -296,6 +300,23 @@ trait ApiControllerBase extends ControllerBase {
}) getOrElse NotFound() }) getOrElse NotFound()
}) })
/**
* https://developer.github.com/v3/issues/#create-an-issue
*/
post("/api/v3/repos/:owner/:repository/issues")(readableUsersOnly { repository =>
if(isIssueEditable(repository)){ // TODO Should this check is provided by authenticator?
(for{
data <- extractFromJsonBody[CreateAnIssue]
loginAccount <- context.loginAccount
} yield {
val milestone = data.milestone.flatMap(getMilestone(repository.owner, repository.name, _))
val issue = createIssue(repository, data.title, data.body, data.assignees.headOption,
milestone.map(_.milestoneId), data.labels)
JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(loginAccount)))
}) getOrElse NotFound()
} else Unauthorized()
})
/** /**
* https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue * https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
*/ */

View File

@@ -21,6 +21,7 @@ class IssuesController extends IssuesControllerBase
with MilestonesService with MilestonesService
with ActivityService with ActivityService
with HandleCommentService with HandleCommentService
with IssueCreationService
with ReadableUsersAuthenticator with ReadableUsersAuthenticator
with ReferrerAuthenticator with ReferrerAuthenticator
with WritableUsersAuthenticator with WritableUsersAuthenticator
@@ -36,6 +37,7 @@ trait IssuesControllerBase extends ControllerBase {
with MilestonesService with MilestonesService
with ActivityService with ActivityService
with HandleCommentService with HandleCommentService
with IssueCreationService
with ReadableUsersAuthenticator with ReadableUsersAuthenticator
with ReferrerAuthenticator with ReferrerAuthenticator
with WritableUsersAuthenticator with WritableUsersAuthenticator
@@ -91,7 +93,7 @@ trait IssuesControllerBase extends ControllerBase {
getAssignableUserNames(owner, name), getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name), getMilestonesWithIssueCount(owner, name),
getLabels(owner, name), getLabels(owner, name),
isEditable(repository), isIssueEditable(repository),
isManageable(repository), isManageable(repository),
repository) repository)
} getOrElse NotFound() } getOrElse NotFound()
@@ -99,7 +101,7 @@ trait IssuesControllerBase extends ControllerBase {
}) })
get("/:owner/:repository/issues/new")(readableUsersOnly { repository => get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
if(isEditable(repository)){ // TODO Should this check is provided by authenticator? if(isIssueEditable(repository)){ // TODO Should this check is provided by authenticator?
defining(repository.owner, repository.name){ case (owner, name) => defining(repository.owner, repository.name){ case (owner, name) =>
html.create( html.create(
getAssignableUserNames(owner, name), getAssignableUserNames(owner, name),
@@ -112,46 +114,10 @@ trait IssuesControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
if(isEditable(repository)){ // TODO Should this check is provided by authenticator? if(isIssueEditable(repository)){ // TODO Should this check is provided by authenticator?
defining(repository.owner, repository.name){ case (owner, name) => val issue = createIssue(repository, form.title, form.content, form.assignedUserName,
val manageable = isManageable(repository) form.milestoneId, form.labelNames.toArray.flatMap(_.split(",")))
val userName = context.loginAccount.get.userName redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}")
// insert issue
val issueId = createIssue(owner, name, userName, form.title, form.content,
if (manageable) form.assignedUserName else None,
if (manageable) form.milestoneId else None)
// insert labels
if (manageable) {
form.labelNames.map { value =>
val labels = getLabels(owner, name)
value.split(",").foreach { labelName =>
labels.find(_.labelName == labelName).map { label =>
registerIssueLabel(owner, name, issueId, label.labelId)
}
}
}
}
// record activity
recordCreateIssueActivity(owner, name, userName, issueId, form.title)
getIssue(owner, name, issueId.toString).foreach { issue =>
// extract references and create refer comment
createReferComment(owner, name, issue, form.title + " " + form.content.getOrElse(""), context.loginAccount.get)
// call web hooks
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
// notifications
Notifier().toNotify(repository, issue, form.content.getOrElse("")) {
Notifier.msgIssue(s"${context.baseUrl}/${owner}/${name}/issues/${issueId}")
}
}
redirect(s"/${owner}/${name}/issues/${issueId}")
}
} else Unauthorized() } else Unauthorized()
}) })
@@ -398,35 +364,15 @@ trait IssuesControllerBase extends ControllerBase {
countIssue(condition.copy(state = "closed"), false, owner -> repoName), countIssue(condition.copy(state = "closed"), false, owner -> repoName),
condition, condition,
repository, repository,
isEditable(repository), isIssueEditable(repository),
isManageable(repository)) isManageable(repository))
} }
} }
/**
* Tests whether an logged-in user can manage issues.
*/
private def isManageable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
}
/**
* Tests whether an logged-in user can post issues.
*/
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean = {
repository.repository.options.issuesOption match {
case "ALL" => !repository.repository.isPrivate && context.loginAccount.isDefined
case "PUBLIC" => hasGuestRole(repository.owner, repository.name, context.loginAccount)
case "PRIVATE" => hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
case "DISABLE" => false
}
}
/** /**
* Tests whether an issue or a comment is editable by a logged-in user. * Tests whether an issue or a comment is editable by a logged-in user.
*/ */
private def isEditableContent(owner: String, repository: String, author: String)(implicit context: Context): Boolean = { private def isEditableContent(owner: String, repository: String, author: String)(implicit context: Context): Boolean = {
hasDeveloperRole(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName hasDeveloperRole(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
} }
} }

View File

@@ -424,7 +424,7 @@ trait PullRequestsControllerBase extends ControllerBase {
if(editable) { if(editable) {
val loginUserName = context.loginAccount.get.userName val loginUserName = context.loginAccount.get.userName
val issueId = createIssue( val issueId = insertIssue(
owner = repository.owner, owner = repository.owner,
repository = repository.name, repository = repository.name,
loginUser = loginUserName, loginUser = loginUserName,

View File

@@ -0,0 +1,75 @@
package gitbucket.core.service
import gitbucket.core.controller.Context
import gitbucket.core.model.Issue
import gitbucket.core.model.Profile.profile.simple.Session
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.util.Notifier
import gitbucket.core.util.Implicits._
/**
* Created by sk on 1/5 005.
*/
trait IssueCreationService {
self: RepositoryService with WebHookIssueCommentService with LabelsService with IssuesService with ActivityService =>
def createIssue(repository: RepositoryInfo, title:String, body:Option[String],
assiginee: Option[String], milestoneId: Option[Int], labelNames:Seq[String])(implicit context: Context, s:Session) : Issue = {
val name = repository.name
val owner = repository.owner
val loginAccount = context.loginAccount.get
val userName = loginAccount.userName
val manageable = isManageable(repository)
// insert issue
val issueId = insertIssue(owner, name, userName, title, body,
if (manageable) assiginee else None,
if (manageable) milestoneId else None)
val issue: Issue = getIssue(owner, name, issueId.toString).get
// insert labels
if (manageable) {
val labels = getLabels(owner, name)
labelNames.map { labelName =>
labels.find(_.labelName == labelName).map { label =>
registerIssueLabel(owner, name, issueId, label.labelId)
}
}
}
// record activity
recordCreateIssueActivity(owner, name, userName, issueId, title)
// extract references and create refer comment
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), context.loginAccount.get)
// call web hooks
callIssuesWebHook("opened", repository, issue, context.baseUrl, context.loginAccount.get)
// notifications
Notifier().toNotify(repository, issue, body.getOrElse("")) {
Notifier.msgIssue(s"${context.baseUrl}/${owner}/${name}/issues/${issueId}")
}
issue
}
/**
* Tests whether an logged-in user can manage issues.
*/
protected def isManageable(repository: RepositoryInfo)(implicit context: Context, s:Session): Boolean = {
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
}
/**
* Tests whether an logged-in user can post issues.
*/
protected def isIssueEditable(repository: RepositoryInfo)(implicit context: Context, s:Session): Boolean = {
repository.repository.options.issuesOption match {
case "ALL" => !repository.repository.isPrivate && context.loginAccount.isDefined
case "PUBLIC" => hasGuestRole(repository.owner, repository.name, context.loginAccount)
case "PRIVATE" => hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
case "DISABLE" => false
}
}
}

View File

@@ -269,7 +269,7 @@ trait IssuesService {
} exists), condition.mentioned.isDefined) } exists), condition.mentioned.isDefined)
} }
def createIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String], def insertIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String],
assignedUserName: Option[String], milestoneId: Option[Int], assignedUserName: Option[String], milestoneId: Option[Int],
isPullRequest: Boolean = false)(implicit s: Session) = isPullRequest: Boolean = false)(implicit s: Session) =
// next id number // next id number

View File

@@ -53,7 +53,7 @@ trait ServiceSpecBase {
} }
def generateNewIssue(userName:String, repositoryName:String, loginUser:String="root")(implicit s:Session): Int = { def generateNewIssue(userName:String, repositoryName:String, loginUser:String="root")(implicit s:Session): Int = {
dummyService.createIssue( dummyService.insertIssue(
owner = userName, owner = userName,
repository = repositoryName, repository = repositoryName,
loginUser = loginUser, loginUser = loginUser,