133 lines
2.8 KiB
Go
133 lines
2.8 KiB
Go
package service
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"database/sql"
|
||
|
|
"encoding/json/v2"
|
||
|
|
"errors"
|
||
|
|
"log/slog"
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
"code.chimeric.al/chimerical/odidere/internal/db"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (svc *Service) listExecutions(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var (
|
||
|
|
ctx = r.Context()
|
||
|
|
log = ctx.Value(logKey).(*slog.Logger)
|
||
|
|
)
|
||
|
|
|
||
|
|
rows, err := svc.dbr.SelectPendingExecutions(ctx)
|
||
|
|
if err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to list pending executions",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(
|
||
|
|
w,
|
||
|
|
http.StatusText(http.StatusInternalServerError),
|
||
|
|
http.StatusInternalServerError,
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
if err := json.MarshalWrite(w, rows); err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to encode executions response",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (svc *Service) updateExecution(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var (
|
||
|
|
ctx = r.Context()
|
||
|
|
log = ctx.Value(logKey).(*slog.Logger)
|
||
|
|
req struct {
|
||
|
|
Status db.ExecutionStatus `json:"status"`
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 32<<20)
|
||
|
|
if err := json.UnmarshalRead(r.Body, &req); err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to decode request",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(w, "invalid request", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if req.Status != db.ExecutionStatusApproved &&
|
||
|
|
req.Status != db.ExecutionStatusDenied {
|
||
|
|
http.Error(w, "invalid status", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := svc.db.Writer.BeginTx(ctx, nil)
|
||
|
|
if err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to begin execution transaction",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(
|
||
|
|
w,
|
||
|
|
http.StatusText(http.StatusInternalServerError),
|
||
|
|
http.StatusInternalServerError,
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer tx.Rollback()
|
||
|
|
|
||
|
|
qtx := svc.dbw.WithTx(tx)
|
||
|
|
execution, err := qtx.SelectExecutionId(ctx, r.PathValue("id"))
|
||
|
|
if errors.Is(err, sql.ErrNoRows) {
|
||
|
|
http.NotFound(w, r)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to load execution",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(
|
||
|
|
w,
|
||
|
|
http.StatusText(http.StatusInternalServerError),
|
||
|
|
http.StatusInternalServerError,
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if execution.Status != db.ExecutionStatusPending {
|
||
|
|
http.Error(w, "execution is not pending", http.StatusConflict)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := qtx.UpdateExecutionStatus(ctx, db.UpdateExecutionStatusParams{
|
||
|
|
Id: execution.Id,
|
||
|
|
Status: req.Status,
|
||
|
|
}); err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to update execution status",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(
|
||
|
|
w,
|
||
|
|
http.StatusText(http.StatusInternalServerError),
|
||
|
|
http.StatusInternalServerError,
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := tx.Commit(); err != nil {
|
||
|
|
log.ErrorContext(
|
||
|
|
ctx, "failed to commit execution transaction",
|
||
|
|
slog.Any("error", err),
|
||
|
|
)
|
||
|
|
http.Error(
|
||
|
|
w,
|
||
|
|
http.StatusText(http.StatusInternalServerError),
|
||
|
|
http.StatusInternalServerError,
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.WriteHeader(http.StatusNoContent)
|
||
|
|
}
|