소스 검색

feat: add config patch endpoint to control api

tags/v0.3.0-pre^0
Jan Svabenik 1 개월 전
부모
커밋
fbd4a7e4c0
3개의 변경된 파일103개의 추가작업 그리고 0개의 파일을 삭제
  1. +18
    -0
      docs/README.md
  2. +66
    -0
      internal/control/control.go
  3. +19
    -0
      internal/control/control_test.go

+ 18
- 0
docs/README.md 파일 보기

@@ -17,6 +17,24 @@ The current no-hardware source can be parameterized via config:
- `audio.toneRightHz`
- `audio.toneAmplitude`

### HTTP control surface

Available endpoints:
- `GET /healthz`
- `GET /status`
- `GET /dry-run`
- `GET /config`
- `POST /config`

Current patchable runtime fields via `POST /config`:
- `frequencyMHz`
- `outputDrive`
- `toneLeftHz`
- `toneRightHz`
- `toneAmplitude`
- `ps`
- `radioText`

### Internal DSP module
- `cd internal`
- `go test ./...`


+ 66
- 0
internal/control/control.go 파일 보기

@@ -14,6 +14,16 @@ type Server struct {
cfg config.Config
}

type ConfigPatch struct {
FrequencyMHz *float64 `json:"frequencyMHz,omitempty"`
OutputDrive *float64 `json:"outputDrive,omitempty"`
ToneLeftHz *float64 `json:"toneLeftHz,omitempty"`
ToneRightHz *float64 `json:"toneRightHz,omitempty"`
ToneAmplitude *float64 `json:"toneAmplitude,omitempty"`
PS *string `json:"ps,omitempty"`
RadioText *string `json:"radioText,omitempty"`
}

func NewServer(cfg config.Config) *Server { return &Server{cfg: cfg} }

func (s *Server) Handler() http.Handler {
@@ -21,6 +31,7 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("/healthz", s.handleHealth)
mux.HandleFunc("/status", s.handleStatus)
mux.HandleFunc("/dry-run", s.handleDryRun)
mux.HandleFunc("/config", s.handleConfig)
return mux
}

@@ -41,6 +52,8 @@ func (s *Server) handleStatus(w http.ResponseWriter, _ *http.Request) {
"frequencyMHz": cfg.FM.FrequencyMHz,
"stereoEnabled": cfg.FM.StereoEnabled,
"rdsEnabled": cfg.RDS.Enabled,
"toneLeftHz": cfg.Audio.ToneLeftHz,
"toneRightHz": cfg.Audio.ToneRightHz,
})
}

@@ -52,3 +65,56 @@ func (s *Server) handleDryRun(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(drypkg.Generate(cfg))
}

func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
s.mu.RLock()
cfg := s.cfg
s.mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(cfg)
case http.MethodPost:
var patch ConfigPatch
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

s.mu.Lock()
next := s.cfg
if patch.FrequencyMHz != nil {
next.FM.FrequencyMHz = *patch.FrequencyMHz
}
if patch.OutputDrive != nil {
next.FM.OutputDrive = *patch.OutputDrive
}
if patch.ToneLeftHz != nil {
next.Audio.ToneLeftHz = *patch.ToneLeftHz
}
if patch.ToneRightHz != nil {
next.Audio.ToneRightHz = *patch.ToneRightHz
}
if patch.ToneAmplitude != nil {
next.Audio.ToneAmplitude = *patch.ToneAmplitude
}
if patch.PS != nil {
next.RDS.PS = *patch.PS
}
if patch.RadioText != nil {
next.RDS.RadioText = *patch.RadioText
}
if err := next.Validate(); err != nil {
s.mu.Unlock()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
s.cfg = next
s.mu.Unlock()

w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}

+ 19
- 0
internal/control/control_test.go 파일 보기

@@ -1,6 +1,7 @@
package control

import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -52,3 +53,21 @@ func TestDryRunEndpoint(t *testing.T) {
t.Fatalf("unexpected mode: %v", body["mode"])
}
}

func TestConfigPatch(t *testing.T) {
srv := NewServer(cfgpkg.Default())
body := []byte(`{"toneLeftHz":900,"radioText":"hello world"}`)
req := httptest.NewRequest(http.MethodPost, "/config", bytes.NewReader(body))
rec := httptest.NewRecorder()
srv.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("unexpected status: %d body=%s", rec.Code, rec.Body.String())
}

getReq := httptest.NewRequest(http.MethodGet, "/config", nil)
getRec := httptest.NewRecorder()
srv.Handler().ServeHTTP(getRec, getReq)
if getRec.Code != http.StatusOK {
t.Fatalf("unexpected status: %d", getRec.Code)
}
}

불러오는 중...
취소
저장