diff --git a/src/main/scala/gitbucket/core/api/ApiMilestone.scala b/src/main/scala/gitbucket/core/api/ApiMilestone.scala new file mode 100644 index 0000000..4126c77 --- /dev/null +++ b/src/main/scala/gitbucket/core/api/ApiMilestone.scala @@ -0,0 +1,51 @@ +package gitbucket.core.api + +import gitbucket.core.model.{Milestone, Repository} +import gitbucket.core.util.RepositoryName +import java.util.Date + +/** + * https://docs.github.com/en/rest/reference/issues#milestones + */ +case class ApiMilestone( + url: ApiPath, + html_url: ApiPath, +// label_url: ApiPath, + id: Int, + number: Int, + state: String, + title: String, + description: String, + creator: ApiUser, + open_issues: Int, + closed_issues: Int, +// created_at: Option[Date], +// updated_at: Option[Date], + closed_at: Option[Date], + due_on: Option[Date] +) + +object ApiMilestone { + def apply( + repository: Repository, + milestone: Milestone, + user: ApiUser, + open_issue_count: Int = 0, + closed_issue_count: Int = 0 + ): ApiMilestone = + ApiMilestone( + url = ApiPath(s"/api/v3/repos/${RepositoryName(repository).fullName}/milestones/${milestone.milestoneId}"), + html_url = ApiPath(s"/${RepositoryName(repository).fullName}/issues?milestone=${milestone.title}&state=open"), +// label_url = ApiPath(s"/api/v3/repos/${RepositoryName(repository).fullName}/milestones/${milestone_number}/labels"), + id = milestone.milestoneId, + number = milestone.milestoneId, // use milestoneId as number + state = if (milestone.closedDate.isDefined) "closed" else "open", + title = milestone.title, + description = milestone.description.getOrElse(""), + creator = user, + open_issues = open_issue_count, + closed_issues = closed_issue_count, + closed_at = milestone.closedDate, + due_on = milestone.dueDate + ) +} diff --git a/src/main/scala/gitbucket/core/controller/ApiController.scala b/src/main/scala/gitbucket/core/controller/ApiController.scala index a5af352..1a64da1 100644 --- a/src/main/scala/gitbucket/core/controller/ApiController.scala +++ b/src/main/scala/gitbucket/core/controller/ApiController.scala @@ -13,6 +13,7 @@ with ApiIssueCommentControllerBase with ApiIssueControllerBase with ApiIssueLabelControllerBase + with ApiIssueMilestoneControllerBase with ApiOrganizationControllerBase with ApiPullRequestControllerBase with ApiReleaseControllerBase diff --git a/src/main/scala/gitbucket/core/controller/api/ApiIssueMilestoneControllerBase.scala b/src/main/scala/gitbucket/core/controller/api/ApiIssueMilestoneControllerBase.scala new file mode 100644 index 0000000..5b38a58 --- /dev/null +++ b/src/main/scala/gitbucket/core/controller/api/ApiIssueMilestoneControllerBase.scala @@ -0,0 +1,81 @@ +package gitbucket.core.controller.api +import gitbucket.core.api._ +import gitbucket.core.controller.ControllerBase +import gitbucket.core.service.MilestonesService +import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator} +import gitbucket.core.util.Implicits._ +import org.scalatra.NoContent + +trait ApiIssueMilestoneControllerBase extends ControllerBase { + self: MilestonesService with WritableUsersAuthenticator with ReferrerAuthenticator => + + /* + * i. List milestones + * https://docs.github.com/en/rest/reference/issues#list-milestones + */ + get("/api/v3/repos/:owner/:repository/milestones")(referrersOnly { repository => + val state = params.getOrElse("state", "all") + // TODO "sort", "direction" params should be implemented. + val apiMilestones = (for (milestoneWithIssue <- getMilestonesWithIssueCount(repository.owner, repository.name) + .sortBy(p => p._1.milestoneId)) + yield { + ApiMilestone( + repository.repository, + milestoneWithIssue._1, + ApiUser(context.loginAccount.get), + milestoneWithIssue._2, + milestoneWithIssue._3 + ) + }).reverse + state match { + case "all" => JsonFormat(apiMilestones) + case "open" | "closed" => + JsonFormat( + apiMilestones.filter(p => p.state == state) + ) + case _ => NotFound() + } + }) + + /* + * ii. Create a milestone + * https://docs.github.com/en/rest/reference/issues#create-a-milestone + */ + + /* + * iii. Get a milestone + * https://docs.github.com/en/rest/reference/issues#get-a-milestone + */ + get("/api/v3/repos/:owner/:repository/milestones/:number")(referrersOnly { repository => + val milestoneId = params("number").toInt // use milestoneId as number + getMilestonesWithIssueCount(repository.owner, repository.name) + .find(p => p._1.milestoneId == milestoneId) match { + case Some(milestoneWithIssue) => + JsonFormat( + ApiMilestone( + repository.repository, + milestoneWithIssue._1, + ApiUser(context.loginAccount.get), + milestoneWithIssue._2, + milestoneWithIssue._3 + ) + ) + case _ => NotFound() + } + }) + + /* + * iv.Update a milestone + * https://docs.github.com/en/rest/reference/issues#update-a-milestone + */ + + /* + * v. Delete a milestone + * https://docs.github.com/en/rest/reference/issues#delete-a-milestone + */ + delete("/api/v3/repos/:owner/:repository/milestones/:number")(writableUsersOnly { repository => + val milestoneId = params("number").toInt // use milestoneId as number + deleteMilestone(repository.owner, repository.name, milestoneId) + NoContent() + }) +}