84 lines
3.0 KiB
Go
84 lines
3.0 KiB
Go
package controllers
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/aoagents/agent-orchestrator/backend/internal/httpd/apispec"
|
|
"github.com/aoagents/agent-orchestrator/backend/internal/httpd/envelope"
|
|
prsvc "github.com/aoagents/agent-orchestrator/backend/internal/service/pr"
|
|
)
|
|
|
|
// PRsController owns the /prs action routes.
|
|
type PRsController struct {
|
|
Svc prsvc.ActionManager
|
|
}
|
|
|
|
// Register mounts the PR action routes on the supplied router.
|
|
func (c *PRsController) Register(r chi.Router) {
|
|
r.Post("/prs/{id}/merge", c.merge)
|
|
r.Post("/prs/{id}/resolve-comments", c.resolveComments)
|
|
}
|
|
|
|
func (c *PRsController) merge(w http.ResponseWriter, r *http.Request) {
|
|
if c.Svc == nil {
|
|
apispec.NotImplemented(w, r, "POST", "/api/v1/prs/{id}/merge")
|
|
return
|
|
}
|
|
prID := chi.URLParam(r, "id")
|
|
res, err := c.Svc.Merge(r.Context(), prID)
|
|
if err != nil {
|
|
writePRError(w, r, err)
|
|
return
|
|
}
|
|
envelope.WriteJSON(w, http.StatusOK, MergePRResponse{OK: true, PRNumber: res.PRNumber, Method: res.Method})
|
|
}
|
|
|
|
func (c *PRsController) resolveComments(w http.ResponseWriter, r *http.Request) {
|
|
if c.Svc == nil {
|
|
apispec.NotImplemented(w, r, "POST", "/api/v1/prs/{id}/resolve-comments")
|
|
return
|
|
}
|
|
prID := chi.URLParam(r, "id")
|
|
|
|
// Body is optional: omitting it resolves all unresolved threads.
|
|
var in ResolveCommentsRequest
|
|
if err := decodeJSON(r, &in); err != nil && !isEmptyBody(err) {
|
|
envelope.WriteAPIError(w, r, http.StatusBadRequest, "bad_request", "INVALID_JSON", "Invalid JSON body", nil)
|
|
return
|
|
}
|
|
|
|
res, err := c.Svc.ResolveComments(r.Context(), prID, in.CommentIDs)
|
|
if err != nil {
|
|
writePRError(w, r, err)
|
|
return
|
|
}
|
|
envelope.WriteJSON(w, http.StatusOK, ResolveCommentsResponse{OK: true, Resolved: res.Resolved})
|
|
}
|
|
|
|
// writePRError maps PR sentinel errors to their locked HTTP envelopes,
|
|
// falling back to 500 for unexpected failures.
|
|
func writePRError(w http.ResponseWriter, r *http.Request, err error) {
|
|
switch {
|
|
case errors.Is(err, prsvc.ErrPRNotFound):
|
|
envelope.WriteAPIError(w, r, http.StatusNotFound, "not_found", "PR_NOT_FOUND", "Unknown PR", nil)
|
|
case errors.Is(err, prsvc.ErrPRNotMergeable):
|
|
envelope.WriteAPIError(w, r, http.StatusConflict, "conflict", "PR_NOT_MERGEABLE", "PR is not mergeable", nil)
|
|
case errors.Is(err, prsvc.ErrPRPreconditions):
|
|
envelope.WriteAPIError(w, r, http.StatusUnprocessableEntity, "unprocessable", "PR_PRECONDITIONS_UNMET", "PR merge preconditions are not met", nil)
|
|
case errors.Is(err, prsvc.ErrNothingToResolve):
|
|
envelope.WriteAPIError(w, r, http.StatusUnprocessableEntity, "unprocessable", "NOTHING_TO_RESOLVE", "No unresolved review threads to resolve", nil)
|
|
default:
|
|
envelope.WriteAPIError(w, r, http.StatusInternalServerError, "internal", "PR_OPERATION_FAILED", "PR operation failed", nil)
|
|
}
|
|
}
|
|
|
|
// isEmptyBody reports whether err signals an absent or empty request body.
|
|
// io.ErrUnexpectedEOF means a truncated/malformed body — bad request, not absent.
|
|
func isEmptyBody(err error) bool {
|
|
return errors.Is(err, io.EOF)
|
|
}
|