155 lines
3.7 KiB
Go
155 lines
3.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"xorm.io/xorm"
|
|
|
|
"github.com/forgeo/forgebucket/internal/api/middleware"
|
|
"github.com/forgeo/forgebucket/internal/models"
|
|
)
|
|
|
|
type IssueHandler struct {
|
|
db *xorm.Engine
|
|
}
|
|
|
|
func NewIssueHandler(db *xorm.Engine) *IssueHandler {
|
|
return &IssueHandler{db: db}
|
|
}
|
|
|
|
type issueResponse struct {
|
|
models.Issue
|
|
AuthorName string `json:"authorName"`
|
|
}
|
|
|
|
func (h *IssueHandler) enrichIssue(issue *models.Issue) issueResponse {
|
|
var author models.User
|
|
h.db.ID(issue.AuthorID).Get(&author)
|
|
return issueResponse{Issue: *issue, AuthorName: author.Username}
|
|
}
|
|
|
|
func (h *IssueHandler) List(w http.ResponseWriter, r *http.Request) {
|
|
repoID, ok := h.repoID(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
state := r.URL.Query().Get("state")
|
|
if state == "" {
|
|
state = "open"
|
|
}
|
|
|
|
var issues []models.Issue
|
|
if err := h.db.Where("repo_id = ? AND state = ?", repoID, state).
|
|
OrderBy("id DESC").Find(&issues); err != nil {
|
|
jsonError(w, "could not list issues", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
result := make([]issueResponse, len(issues))
|
|
for i := range issues {
|
|
result[i] = h.enrichIssue(&issues[i])
|
|
}
|
|
if result == nil {
|
|
result = []issueResponse{}
|
|
}
|
|
jsonOK(w, result)
|
|
}
|
|
|
|
func (h *IssueHandler) Create(w http.ResponseWriter, r *http.Request) {
|
|
repoID, ok := h.repoID(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
authorID, _ := middleware.UserIDFromContext(r.Context())
|
|
|
|
var body struct {
|
|
Title string `json:"title"`
|
|
Body string `json:"body"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Title == "" {
|
|
jsonError(w, "title is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Auto-increment issue number per repo
|
|
count, _ := h.db.Where("repo_id = ?", repoID).Count(&models.Issue{})
|
|
number := int(count) + 1
|
|
|
|
issue := &models.Issue{
|
|
RepoID: repoID,
|
|
AuthorID: authorID,
|
|
Number: number,
|
|
Title: body.Title,
|
|
Body: body.Body,
|
|
State: models.IssueStateOpen,
|
|
}
|
|
if _, err := h.db.Insert(issue); err != nil {
|
|
jsonError(w, "could not create issue", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(h.enrichIssue(issue))
|
|
}
|
|
|
|
func (h *IssueHandler) Get(w http.ResponseWriter, r *http.Request) {
|
|
issue, ok := h.lookupIssue(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
jsonOK(w, h.enrichIssue(issue))
|
|
}
|
|
|
|
func (h *IssueHandler) Close(w http.ResponseWriter, r *http.Request) {
|
|
issue, ok := h.lookupIssue(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
issue.State = models.IssueStateClosed
|
|
if _, err := h.db.ID(issue.ID).Cols("state").Update(issue); err != nil {
|
|
jsonError(w, "could not close issue", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
jsonOK(w, h.enrichIssue(issue))
|
|
}
|
|
|
|
func (h *IssueHandler) Reopen(w http.ResponseWriter, r *http.Request) {
|
|
issue, ok := h.lookupIssue(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
issue.State = models.IssueStateOpen
|
|
if _, err := h.db.ID(issue.ID).Cols("state").Update(issue); err != nil {
|
|
jsonError(w, "could not reopen issue", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
jsonOK(w, h.enrichIssue(issue))
|
|
}
|
|
|
|
func (h *IssueHandler) repoID(w http.ResponseWriter, r *http.Request) (int64, bool) {
|
|
return resolveRepoID(h.db, w, r)
|
|
}
|
|
|
|
func (h *IssueHandler) lookupIssue(w http.ResponseWriter, r *http.Request) (*models.Issue, bool) {
|
|
repoID, ok := h.repoID(w, r)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
issueNum, err := strconv.Atoi(chi.URLParam(r, "issueNum"))
|
|
if err != nil {
|
|
jsonError(w, "invalid issue number", http.StatusBadRequest)
|
|
return nil, false
|
|
}
|
|
var issue models.Issue
|
|
if found, _ := h.db.Where("repo_id = ? AND number = ?", repoID, issueNum).Get(&issue); !found {
|
|
jsonError(w, "issue not found", http.StatusNotFound)
|
|
return nil, false
|
|
}
|
|
return &issue, true
|
|
}
|