code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/milestone.go (about)

     1  // Copyright 2016 The Gogs Authors. All rights reserved.
     2  // Copyright 2020 The Gitea Authors.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package repo
     6  
     7  import (
     8  	"net/http"
     9  	"strconv"
    10  	"time"
    11  
    12  	issues_model "code.gitea.io/gitea/models/issues"
    13  	"code.gitea.io/gitea/modules/context"
    14  	api "code.gitea.io/gitea/modules/structs"
    15  	"code.gitea.io/gitea/modules/timeutil"
    16  	"code.gitea.io/gitea/modules/web"
    17  	"code.gitea.io/gitea/routers/api/v1/utils"
    18  	"code.gitea.io/gitea/services/convert"
    19  )
    20  
    21  // ListMilestones list milestones for a repository
    22  func ListMilestones(ctx *context.APIContext) {
    23  	// swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestonesList
    24  	// ---
    25  	// summary: Get all of a repository's opened milestones
    26  	// produces:
    27  	// - application/json
    28  	// parameters:
    29  	// - name: owner
    30  	//   in: path
    31  	//   description: owner of the repo
    32  	//   type: string
    33  	//   required: true
    34  	// - name: repo
    35  	//   in: path
    36  	//   description: name of the repo
    37  	//   type: string
    38  	//   required: true
    39  	// - name: state
    40  	//   in: query
    41  	//   description: Milestone state, Recognized values are open, closed and all. Defaults to "open"
    42  	//   type: string
    43  	// - name: name
    44  	//   in: query
    45  	//   description: filter by milestone name
    46  	//   type: string
    47  	// - name: page
    48  	//   in: query
    49  	//   description: page number of results to return (1-based)
    50  	//   type: integer
    51  	// - name: limit
    52  	//   in: query
    53  	//   description: page size of results
    54  	//   type: integer
    55  	// responses:
    56  	//   "200":
    57  	//     "$ref": "#/responses/MilestoneList"
    58  	//   "404":
    59  	//     "$ref": "#/responses/notFound"
    60  
    61  	milestones, total, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
    62  		ListOptions: utils.GetListOptions(ctx),
    63  		RepoID:      ctx.Repo.Repository.ID,
    64  		State:       api.StateType(ctx.FormString("state")),
    65  		Name:        ctx.FormString("name"),
    66  	})
    67  	if err != nil {
    68  		ctx.Error(http.StatusInternalServerError, "GetMilestones", err)
    69  		return
    70  	}
    71  
    72  	apiMilestones := make([]*api.Milestone, len(milestones))
    73  	for i := range milestones {
    74  		apiMilestones[i] = convert.ToAPIMilestone(milestones[i])
    75  	}
    76  
    77  	ctx.SetTotalCountHeader(total)
    78  	ctx.JSON(http.StatusOK, &apiMilestones)
    79  }
    80  
    81  // GetMilestone get a milestone for a repository by ID and if not available by name
    82  func GetMilestone(ctx *context.APIContext) {
    83  	// swagger:operation GET /repos/{owner}/{repo}/milestones/{id} issue issueGetMilestone
    84  	// ---
    85  	// summary: Get a milestone
    86  	// produces:
    87  	// - application/json
    88  	// parameters:
    89  	// - name: owner
    90  	//   in: path
    91  	//   description: owner of the repo
    92  	//   type: string
    93  	//   required: true
    94  	// - name: repo
    95  	//   in: path
    96  	//   description: name of the repo
    97  	//   type: string
    98  	//   required: true
    99  	// - name: id
   100  	//   in: path
   101  	//   description: the milestone to get, identified by ID and if not available by name
   102  	//   type: string
   103  	//   required: true
   104  	// responses:
   105  	//   "200":
   106  	//     "$ref": "#/responses/Milestone"
   107  	//   "404":
   108  	//     "$ref": "#/responses/notFound"
   109  
   110  	milestone := getMilestoneByIDOrName(ctx)
   111  	if ctx.Written() {
   112  		return
   113  	}
   114  
   115  	ctx.JSON(http.StatusOK, convert.ToAPIMilestone(milestone))
   116  }
   117  
   118  // CreateMilestone create a milestone for a repository
   119  func CreateMilestone(ctx *context.APIContext) {
   120  	// swagger:operation POST /repos/{owner}/{repo}/milestones issue issueCreateMilestone
   121  	// ---
   122  	// summary: Create a milestone
   123  	// consumes:
   124  	// - application/json
   125  	// produces:
   126  	// - application/json
   127  	// parameters:
   128  	// - name: owner
   129  	//   in: path
   130  	//   description: owner of the repo
   131  	//   type: string
   132  	//   required: true
   133  	// - name: repo
   134  	//   in: path
   135  	//   description: name of the repo
   136  	//   type: string
   137  	//   required: true
   138  	// - name: body
   139  	//   in: body
   140  	//   schema:
   141  	//     "$ref": "#/definitions/CreateMilestoneOption"
   142  	// responses:
   143  	//   "201":
   144  	//     "$ref": "#/responses/Milestone"
   145  	//   "404":
   146  	//     "$ref": "#/responses/notFound"
   147  	form := web.GetForm(ctx).(*api.CreateMilestoneOption)
   148  
   149  	if form.Deadline == nil {
   150  		defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
   151  		form.Deadline = &defaultDeadline
   152  	}
   153  
   154  	milestone := &issues_model.Milestone{
   155  		RepoID:       ctx.Repo.Repository.ID,
   156  		Name:         form.Title,
   157  		Content:      form.Description,
   158  		DeadlineUnix: timeutil.TimeStamp(form.Deadline.Unix()),
   159  	}
   160  
   161  	if form.State == "closed" {
   162  		milestone.IsClosed = true
   163  		milestone.ClosedDateUnix = timeutil.TimeStampNow()
   164  	}
   165  
   166  	if err := issues_model.NewMilestone(ctx, milestone); err != nil {
   167  		ctx.Error(http.StatusInternalServerError, "NewMilestone", err)
   168  		return
   169  	}
   170  	ctx.JSON(http.StatusCreated, convert.ToAPIMilestone(milestone))
   171  }
   172  
   173  // EditMilestone modify a milestone for a repository by ID and if not available by name
   174  func EditMilestone(ctx *context.APIContext) {
   175  	// swagger:operation PATCH /repos/{owner}/{repo}/milestones/{id} issue issueEditMilestone
   176  	// ---
   177  	// summary: Update a milestone
   178  	// consumes:
   179  	// - application/json
   180  	// produces:
   181  	// - application/json
   182  	// parameters:
   183  	// - name: owner
   184  	//   in: path
   185  	//   description: owner of the repo
   186  	//   type: string
   187  	//   required: true
   188  	// - name: repo
   189  	//   in: path
   190  	//   description: name of the repo
   191  	//   type: string
   192  	//   required: true
   193  	// - name: id
   194  	//   in: path
   195  	//   description: the milestone to edit, identified by ID and if not available by name
   196  	//   type: string
   197  	//   required: true
   198  	// - name: body
   199  	//   in: body
   200  	//   schema:
   201  	//     "$ref": "#/definitions/EditMilestoneOption"
   202  	// responses:
   203  	//   "200":
   204  	//     "$ref": "#/responses/Milestone"
   205  	//   "404":
   206  	//     "$ref": "#/responses/notFound"
   207  	form := web.GetForm(ctx).(*api.EditMilestoneOption)
   208  	milestone := getMilestoneByIDOrName(ctx)
   209  	if ctx.Written() {
   210  		return
   211  	}
   212  
   213  	if len(form.Title) > 0 {
   214  		milestone.Name = form.Title
   215  	}
   216  	if form.Description != nil {
   217  		milestone.Content = *form.Description
   218  	}
   219  	if form.Deadline != nil && !form.Deadline.IsZero() {
   220  		milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
   221  	}
   222  
   223  	oldIsClosed := milestone.IsClosed
   224  	if form.State != nil {
   225  		milestone.IsClosed = *form.State == string(api.StateClosed)
   226  	}
   227  
   228  	if err := issues_model.UpdateMilestone(ctx, milestone, oldIsClosed); err != nil {
   229  		ctx.Error(http.StatusInternalServerError, "UpdateMilestone", err)
   230  		return
   231  	}
   232  	ctx.JSON(http.StatusOK, convert.ToAPIMilestone(milestone))
   233  }
   234  
   235  // DeleteMilestone delete a milestone for a repository by ID and if not available by name
   236  func DeleteMilestone(ctx *context.APIContext) {
   237  	// swagger:operation DELETE /repos/{owner}/{repo}/milestones/{id} issue issueDeleteMilestone
   238  	// ---
   239  	// summary: Delete a milestone
   240  	// parameters:
   241  	// - name: owner
   242  	//   in: path
   243  	//   description: owner of the repo
   244  	//   type: string
   245  	//   required: true
   246  	// - name: repo
   247  	//   in: path
   248  	//   description: name of the repo
   249  	//   type: string
   250  	//   required: true
   251  	// - name: id
   252  	//   in: path
   253  	//   description: the milestone to delete, identified by ID and if not available by name
   254  	//   type: string
   255  	//   required: true
   256  	// responses:
   257  	//   "204":
   258  	//     "$ref": "#/responses/empty"
   259  	//   "404":
   260  	//     "$ref": "#/responses/notFound"
   261  
   262  	m := getMilestoneByIDOrName(ctx)
   263  	if ctx.Written() {
   264  		return
   265  	}
   266  
   267  	if err := issues_model.DeleteMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, m.ID); err != nil {
   268  		ctx.Error(http.StatusInternalServerError, "DeleteMilestoneByRepoID", err)
   269  		return
   270  	}
   271  	ctx.Status(http.StatusNoContent)
   272  }
   273  
   274  // getMilestoneByIDOrName get milestone by ID and if not available by name
   275  func getMilestoneByIDOrName(ctx *context.APIContext) *issues_model.Milestone {
   276  	mile := ctx.Params(":id")
   277  	mileID, _ := strconv.ParseInt(mile, 0, 64)
   278  
   279  	if mileID != 0 {
   280  		milestone, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, mileID)
   281  		if err == nil {
   282  			return milestone
   283  		} else if !issues_model.IsErrMilestoneNotExist(err) {
   284  			ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
   285  			return nil
   286  		}
   287  	}
   288  
   289  	milestone, err := issues_model.GetMilestoneByRepoIDANDName(ctx, ctx.Repo.Repository.ID, mile)
   290  	if err != nil {
   291  		if issues_model.IsErrMilestoneNotExist(err) {
   292  			ctx.NotFound()
   293  			return nil
   294  		}
   295  		ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
   296  		return nil
   297  	}
   298  
   299  	return milestone
   300  }