From 84b56c3c53fb931f569f9d0b5e2bf3832aba6abe Mon Sep 17 00:00:00 2001 From: lstahlman Date: Wed, 24 Aug 2016 15:18:56 -0700 Subject: [PATCH] Additional API support for milestones (#3383) --- routers/api/v1/api.go | 12 ++ routers/api/v1/repo/issue_milestone.go | 76 +++++++++++++ routers/api/v1/repo/milestone.go | 150 +++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 routers/api/v1/repo/issue_milestone.go create mode 100644 routers/api/v1/repo/milestone.go diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index ce0452a247..eb15acc313 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -259,6 +259,11 @@ func RegisterRoutes(m *macaron.Macaron) { Delete(repo.ClearIssueLabels) m.Delete("/:id", repo.DeleteIssueLabel) }) + m.Group("/milestone", func() { + m.Combo("").Get(repo.GetIssueMilestone). + Post(bind(api.SetIssueMilestoneOption{}), repo.SetIssueMilestone). + Delete(repo.DeleteIssueMilestone) + }) }) }, mustEnableIssues) @@ -268,6 +273,13 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/:id").Get(repo.GetLabel).Patch(bind(api.EditLabelOption{}), repo.EditLabel). Delete(repo.DeleteLabel) }) + m.Group("/milestones", func() { + m.Combo("").Get(repo.ListMilestones). + Post(bind(api.CreateMilestoneOption{}), repo.CreateMilestone) + m.Combo("/:id").Get(repo.GetMilestone).Patch(bind(api.EditMilestoneOption{}), repo.EditMilestone). + Delete(repo.DeleteMilestone) + m.Post("/:id/:action", repo.ChangeMilestoneStatus) + }) }, repoAssignment()) }, reqToken()) diff --git a/routers/api/v1/repo/issue_milestone.go b/routers/api/v1/repo/issue_milestone.go new file mode 100644 index 0000000000..8dfdf5ef53 --- /dev/null +++ b/routers/api/v1/repo/issue_milestone.go @@ -0,0 +1,76 @@ +// Copyright 2016 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/context" + "github.com/gogits/gogs/routers/api/v1/convert" +) + +func GetIssueMilestone(ctx *context.APIContext) { + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + return + } + + apiMilestone := convert.ToMilestone(issue.Milestone) + ctx.JSON(200, &apiMilestone) +} + +func SetIssueMilestone(ctx *context.APIContext, form api.SetIssueMilestoneOption) { + if !ctx.Repo.IsWriter() { + ctx.Status(403) + return + } + + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + return + } + + oldMid := issue.MilestoneID + if oldMid != form.ID { + issue.MilestoneID = form.ID + if err = models.ChangeMilestoneAssign(oldMid, issue); err != nil { + ctx.Error(500, "ChangeMilestoneAssign", err) + return + } + } + + // Refresh issue to return updated milestone + issue, err = models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + return + } + + apiMilestone := convert.ToMilestone(issue.Milestone) + ctx.JSON(200, &apiMilestone) +} + +func DeleteIssueMilestone(ctx *context.APIContext) { + form := api.SetIssueMilestoneOption{ + ID: 0, + } + + SetIssueMilestone(ctx, form) +} diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go new file mode 100644 index 0000000000..416d9da90f --- /dev/null +++ b/routers/api/v1/repo/milestone.go @@ -0,0 +1,150 @@ +// Copyright 2016 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/context" + "github.com/gogits/gogs/routers/api/v1/convert" + "time" +) + +func ListMilestones(ctx *context.APIContext) { + milestones, err := models.GetAllRepoMilestones(ctx.Repo.Repository.ID) + if err != nil { + ctx.Error(500, "GetAllRepoMilestones", err) + return + } + + apiMilestones := make([]*api.Milestone, len(milestones)) + for i := range milestones { + apiMilestones[i] = convert.ToMilestone(milestones[i]) + } + ctx.JSON(200, &apiMilestones) +} + +func GetMilestone(ctx *context.APIContext) { + milestone, err := models.GetRepoMilestoneByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetRepoMilestoneByID", err) + } + return + } + ctx.JSON(200, convert.ToMilestone(milestone)) +} + +func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { + if !ctx.Repo.IsWriter() { + ctx.Status(403) + return + } + + if form.Deadline == nil { + defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local) + form.Deadline = &defaultDeadline + } + + milestone := &models.Milestone{ + RepoID: ctx.Repo.Repository.ID, + Name: form.Title, + Content: form.Description, + Deadline: *form.Deadline, + } + + if err := models.NewMilestone(milestone); err != nil { + ctx.Error(500, "NewMilestone", err) + return + } + ctx.JSON(201, convert.ToMilestone(milestone)) +} + +func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { + if !ctx.Repo.IsWriter() { + ctx.Status(403) + return + } + + milestone, err := models.GetRepoMilestoneByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetRepoMilestoneByID", err) + } + return + } + + if len(form.Title) > 0 { + milestone.Name = form.Title + } + if len(form.Description) > 0 { + milestone.Content = form.Description + } + if !form.Deadline.IsZero() { + milestone.Deadline = *form.Deadline + } + if err := models.UpdateMilestone(milestone); err != nil { + ctx.Handle(500, "UpdateMilestone", err) + return + } + ctx.JSON(200, convert.ToMilestone(milestone)) +} + +func DeleteMilestone(ctx *context.APIContext) { + if !ctx.Repo.IsWriter() { + ctx.Status(403) + return + } + + if err := models.DeleteMilestoneByID(ctx.ParamsInt64(":id")); err != nil { + ctx.Error(500, "DeleteMilestoneByID", err) + return + } + ctx.Status(204) +} + +func ChangeMilestoneStatus(ctx *context.APIContext) { + if !ctx.Repo.IsWriter() { + ctx.Status(403) + return + } + + m, err := models.GetMilestoneByID(ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.Handle(404, "GetMilestoneByID", err) + } else { + ctx.Handle(500, "GetMilestoneByID", err) + } + return + } + + switch ctx.Params(":action") { + case "open": + if m.IsClosed { + if err = models.ChangeMilestoneStatus(m, false); err != nil { + ctx.Handle(500, "ChangeMilestoneStatus", err) + return + } + } + ctx.JSON(200, convert.ToMilestone(m)) + case "close": + if !m.IsClosed { + m.ClosedDate = time.Now() + if err = models.ChangeMilestoneStatus(m, true); err != nil { + ctx.Handle(500, "ChangeMilestoneStatus", err) + return + } + } + ctx.JSON(200, convert.ToMilestone(m)) + default: + ctx.Status(400) + } +}