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

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package repo
     5  
     6  import (
     7  	"errors"
     8  	"net/http"
     9  
    10  	issues_model "code.gitea.io/gitea/models/issues"
    11  	"code.gitea.io/gitea/modules/context"
    12  	"code.gitea.io/gitea/routers/api/v1/utils"
    13  	"code.gitea.io/gitea/services/convert"
    14  )
    15  
    16  // StartIssueStopwatch creates a stopwatch for the given issue.
    17  func StartIssueStopwatch(ctx *context.APIContext) {
    18  	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch
    19  	// ---
    20  	// summary: Start stopwatch on an issue.
    21  	// consumes:
    22  	// - application/json
    23  	// produces:
    24  	// - application/json
    25  	// parameters:
    26  	// - name: owner
    27  	//   in: path
    28  	//   description: owner of the repo
    29  	//   type: string
    30  	//   required: true
    31  	// - name: repo
    32  	//   in: path
    33  	//   description: name of the repo
    34  	//   type: string
    35  	//   required: true
    36  	// - name: index
    37  	//   in: path
    38  	//   description: index of the issue to create the stopwatch on
    39  	//   type: integer
    40  	//   format: int64
    41  	//   required: true
    42  	// responses:
    43  	//   "201":
    44  	//     "$ref": "#/responses/empty"
    45  	//   "403":
    46  	//     description: Not repo writer, user does not have rights to toggle stopwatch
    47  	//   "404":
    48  	//     "$ref": "#/responses/notFound"
    49  	//   "409":
    50  	//     description: Cannot start a stopwatch again if it already exists
    51  
    52  	issue, err := prepareIssueStopwatch(ctx, false)
    53  	if err != nil {
    54  		return
    55  	}
    56  
    57  	if err := issues_model.CreateIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
    58  		ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
    59  		return
    60  	}
    61  
    62  	ctx.Status(http.StatusCreated)
    63  }
    64  
    65  // StopIssueStopwatch stops a stopwatch for the given issue.
    66  func StopIssueStopwatch(ctx *context.APIContext) {
    67  	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopStopWatch
    68  	// ---
    69  	// summary: Stop an issue's existing stopwatch.
    70  	// consumes:
    71  	// - application/json
    72  	// produces:
    73  	// - application/json
    74  	// parameters:
    75  	// - name: owner
    76  	//   in: path
    77  	//   description: owner of the repo
    78  	//   type: string
    79  	//   required: true
    80  	// - name: repo
    81  	//   in: path
    82  	//   description: name of the repo
    83  	//   type: string
    84  	//   required: true
    85  	// - name: index
    86  	//   in: path
    87  	//   description: index of the issue to stop the stopwatch on
    88  	//   type: integer
    89  	//   format: int64
    90  	//   required: true
    91  	// responses:
    92  	//   "201":
    93  	//     "$ref": "#/responses/empty"
    94  	//   "403":
    95  	//     description: Not repo writer, user does not have rights to toggle stopwatch
    96  	//   "404":
    97  	//     "$ref": "#/responses/notFound"
    98  	//   "409":
    99  	//     description:  Cannot stop a non existent stopwatch
   100  
   101  	issue, err := prepareIssueStopwatch(ctx, true)
   102  	if err != nil {
   103  		return
   104  	}
   105  
   106  	if err := issues_model.FinishIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
   107  		ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
   108  		return
   109  	}
   110  
   111  	ctx.Status(http.StatusCreated)
   112  }
   113  
   114  // DeleteIssueStopwatch delete a specific stopwatch
   115  func DeleteIssueStopwatch(ctx *context.APIContext) {
   116  	// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/stopwatch/delete issue issueDeleteStopWatch
   117  	// ---
   118  	// summary: Delete an issue's existing stopwatch.
   119  	// consumes:
   120  	// - application/json
   121  	// produces:
   122  	// - application/json
   123  	// parameters:
   124  	// - name: owner
   125  	//   in: path
   126  	//   description: owner of the repo
   127  	//   type: string
   128  	//   required: true
   129  	// - name: repo
   130  	//   in: path
   131  	//   description: name of the repo
   132  	//   type: string
   133  	//   required: true
   134  	// - name: index
   135  	//   in: path
   136  	//   description: index of the issue to stop the stopwatch on
   137  	//   type: integer
   138  	//   format: int64
   139  	//   required: true
   140  	// responses:
   141  	//   "204":
   142  	//     "$ref": "#/responses/empty"
   143  	//   "403":
   144  	//     description: Not repo writer, user does not have rights to toggle stopwatch
   145  	//   "404":
   146  	//     "$ref": "#/responses/notFound"
   147  	//   "409":
   148  	//     description:  Cannot cancel a non existent stopwatch
   149  
   150  	issue, err := prepareIssueStopwatch(ctx, true)
   151  	if err != nil {
   152  		return
   153  	}
   154  
   155  	if err := issues_model.CancelStopwatch(ctx, ctx.Doer, issue); err != nil {
   156  		ctx.Error(http.StatusInternalServerError, "CancelStopwatch", err)
   157  		return
   158  	}
   159  
   160  	ctx.Status(http.StatusNoContent)
   161  }
   162  
   163  func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*issues_model.Issue, error) {
   164  	issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
   165  	if err != nil {
   166  		if issues_model.IsErrIssueNotExist(err) {
   167  			ctx.NotFound()
   168  		} else {
   169  			ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
   170  		}
   171  
   172  		return nil, err
   173  	}
   174  
   175  	if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
   176  		ctx.Status(http.StatusForbidden)
   177  		return nil, errors.New("Unable to write to PRs")
   178  	}
   179  
   180  	if !ctx.Repo.CanUseTimetracker(issue, ctx.Doer) {
   181  		ctx.Status(http.StatusForbidden)
   182  		return nil, errors.New("Cannot use time tracker")
   183  	}
   184  
   185  	if issues_model.StopwatchExists(ctx, ctx.Doer.ID, issue.ID) != shouldExist {
   186  		if shouldExist {
   187  			ctx.Error(http.StatusConflict, "StopwatchExists", "cannot stop/cancel a non existent stopwatch")
   188  			err = errors.New("cannot stop/cancel a non existent stopwatch")
   189  		} else {
   190  			ctx.Error(http.StatusConflict, "StopwatchExists", "cannot start a stopwatch again if it already exists")
   191  			err = errors.New("cannot start a stopwatch again if it already exists")
   192  		}
   193  		return nil, err
   194  	}
   195  
   196  	return issue, nil
   197  }
   198  
   199  // GetStopwatches get all stopwatches
   200  func GetStopwatches(ctx *context.APIContext) {
   201  	// swagger:operation GET /user/stopwatches user userGetStopWatches
   202  	// ---
   203  	// summary: Get list of all existing stopwatches
   204  	// parameters:
   205  	// - name: page
   206  	//   in: query
   207  	//   description: page number of results to return (1-based)
   208  	//   type: integer
   209  	// - name: limit
   210  	//   in: query
   211  	//   description: page size of results
   212  	//   type: integer
   213  	// consumes:
   214  	// - application/json
   215  	// produces:
   216  	// - application/json
   217  	// responses:
   218  	//   "200":
   219  	//     "$ref": "#/responses/StopWatchList"
   220  
   221  	sws, err := issues_model.GetUserStopwatches(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
   222  	if err != nil {
   223  		ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err)
   224  		return
   225  	}
   226  
   227  	count, err := issues_model.CountUserStopwatches(ctx, ctx.Doer.ID)
   228  	if err != nil {
   229  		ctx.InternalServerError(err)
   230  		return
   231  	}
   232  
   233  	apiSWs, err := convert.ToStopWatches(sws)
   234  	if err != nil {
   235  		ctx.Error(http.StatusInternalServerError, "APIFormat", err)
   236  		return
   237  	}
   238  
   239  	ctx.SetTotalCountHeader(count)
   240  	ctx.JSON(http.StatusOK, apiSWs)
   241  }