Parse optional account_ids in run handler
This commit is contained in:
+32
-1
@@ -1,8 +1,11 @@
|
|||||||
package httpapi
|
package httpapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/vasyansk/imap-copier/internal/crypto"
|
"github.com/vasyansk/imap-copier/internal/crypto"
|
||||||
@@ -63,13 +66,41 @@ func (s *Server) handleTestAccounts(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseRunAccountIDs reads an optional {"account_ids":[...]} run body. An empty
|
||||||
|
// body means "all accounts" and yields a nil slice. Malformed JSON is an error.
|
||||||
|
func parseRunAccountIDs(r *http.Request) ([]int64, error) {
|
||||||
|
raw, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(bytes.TrimSpace(raw)) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var body struct {
|
||||||
|
AccountIDs []int64 `json:"account_ids"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return body.AccountIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleRun(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleRun(w http.ResponseWriter, r *http.Request) {
|
||||||
taskID, err := pathID(r, "id")
|
taskID, err := pathID(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "bad id", http.StatusBadRequest)
|
http.Error(w, "bad id", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runID, err := s.orch.Run(r.Context(), taskID, "manual", nil)
|
accountIDs, err := parseRunAccountIDs(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "bad request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runID, err := s.orch.Run(r.Context(), taskID, "manual", accountIDs)
|
||||||
|
if errors.Is(err, orchestrator.ErrNoAccountsSelected) {
|
||||||
|
http.Error(w, "no matching accounts selected", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
if errors.Is(err, orchestrator.ErrNotTested) {
|
if errors.Is(err, orchestrator.ErrNotTested) {
|
||||||
http.Error(w, "accounts must pass connection tests first", http.StatusConflict)
|
http.Error(w, "accounts must pass connection tests first", http.StatusConflict)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -26,3 +26,25 @@ func TestImportCSVFailsOnBadEncKey(t *testing.T) {
|
|||||||
t.Fatalf("import must fail on bad EncKey, got %d", rw.Code)
|
t.Fatalf("import must fail on bad EncKey, got %d", rw.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseRunAccountIDs(t *testing.T) {
|
||||||
|
// empty body => nil (run all)
|
||||||
|
req := httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader(""))
|
||||||
|
ids, err := parseRunAccountIDs(req)
|
||||||
|
if err != nil || ids != nil {
|
||||||
|
t.Fatalf("empty body must yield nil ids, got %v err=%v", ids, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicit selection
|
||||||
|
req = httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader(`{"account_ids":[3,7]}`))
|
||||||
|
ids, err = parseRunAccountIDs(req)
|
||||||
|
if err != nil || len(ids) != 2 || ids[0] != 3 || ids[1] != 7 {
|
||||||
|
t.Fatalf("must parse account_ids, got %v err=%v", ids, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// malformed JSON => error
|
||||||
|
req = httptest.NewRequest("POST", "/api/tasks/1/run", strings.NewReader(`{bad`))
|
||||||
|
if _, err := parseRunAccountIDs(req); err == nil {
|
||||||
|
t.Fatal("malformed body must error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user