valiation and logging

This commit is contained in:
Erik Winter 2024-09-09 07:56:01 +02:00
parent fdbefeffe2
commit cb500b7558
3 changed files with 53 additions and 24 deletions

View File

@ -33,7 +33,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", s.apiKey) { if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", s.apiKey) {
http.Error(w, `{"error":"not authorized"}`, http.StatusUnauthorized) msg := "not authorized"
http.Error(w, fmtError(msg), http.StatusUnauthorized)
s.logger.Info(msg)
return return
} }
@ -46,7 +48,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case head == "sync" && r.Method == http.MethodPost: case head == "sync" && r.Method == http.MethodPost:
s.SyncPost(w, r) s.SyncPost(w, r)
default: default:
http.Error(w, `{"error":"not found"}`, http.StatusNotFound) msg := "not found"
http.Error(w, fmtError(msg), http.StatusNotFound)
s.logger.Info(msg)
} }
} }
@ -56,20 +60,26 @@ func (s *Server) SyncGet(w http.ResponseWriter, r *http.Request) {
if tsStr != "" { if tsStr != "" {
var err error var err error
if timestamp, err = time.Parse(time.RFC3339, tsStr); err != nil { if timestamp, err = time.Parse(time.RFC3339, tsStr); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
} }
items, err := s.syncer.Updated(timestamp) items, err := s.syncer.Updated(timestamp)
if err != nil { if err != nil {
http.Error(w, fmtError(err), http.StatusInternalServerError) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusInternalServerError)
s.logger.Error(msg)
return return
} }
body, err := json.Marshal(items) body, err := json.Marshal(items)
if err != nil { if err != nil {
http.Error(w, fmtError(err), http.StatusInternalServerError) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusInternalServerError)
s.logger.Error(msg)
return return
} }
@ -80,33 +90,45 @@ func (s *Server) SyncGet(w http.ResponseWriter, r *http.Request) {
func (s *Server) SyncPost(w http.ResponseWriter, r *http.Request) { func (s *Server) SyncPost(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, fmtError(err), http.StatusBadRequest) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
defer r.Body.Close() defer r.Body.Close()
var items []Item var items []Item
if err := json.Unmarshal(body, &items); err != nil { if err := json.Unmarshal(body, &items); err != nil {
http.Error(w, fmtError(err), http.StatusBadRequest) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
for _, item := range items { for _, item := range items {
if item.ID == "" { if item.ID == "" {
http.Error(w, `{"error":"item without an id"}`, http.StatusBadRequest) msg := "item without an id"
http.Error(w, fmtError(msg), http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
if item.Kind == "" { if item.Kind == "" {
http.Error(w, fmt.Sprintf(`{"error":"item %s does not have a kind"}`, item.ID), http.StatusBadRequest) msg := fmt.Sprintf("item %s does not have a kind", item.ID)
http.Error(w, fmtError(msg), http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
if item.Body == "" { if item.Body == "" {
http.Error(w, fmt.Sprintf(`{"error":"item %s does not have a body"}`, item.ID), http.StatusBadRequest) msg := fmt.Sprintf(`{"error":"item %s does not have a body"}`, item.ID)
http.Error(w, msg, http.StatusBadRequest)
s.logger.Info(msg)
return return
} }
item.Updated = time.Now() item.Updated = time.Now()
if err := s.syncer.Update(item); err != nil { if err := s.syncer.Update(item); err != nil {
http.Error(w, fmtError(err), http.StatusInternalServerError) msg := err.Error()
http.Error(w, fmtError(msg), http.StatusInternalServerError)
s.logger.Error(msg)
return return
} }
} }
@ -132,6 +154,6 @@ func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `{"status":"ok"}`) fmt.Fprint(w, `{"status":"ok"}`)
} }
func fmtError(err error) string { func fmtError(msg string) string {
return fmt.Sprintf(`{"error":%q}`, err.Error()) return fmt.Sprintf(`{"error":%q}`, msg)
} }

View File

@ -55,7 +55,7 @@ func TestSyncGet(t *testing.T) {
now := time.Now() now := time.Now()
mem := NewMemory() mem := NewMemory()
items := []Syncable{ items := []Item{
{ID: "id-0", Updated: now.Add(-10 * time.Minute)}, {ID: "id-0", Updated: now.Add(-10 * time.Minute)},
{ID: "id-1", Updated: now.Add(-5 * time.Minute)}, {ID: "id-1", Updated: now.Add(-5 * time.Minute)},
{ID: "id-2", Updated: now.Add(time.Minute)}, {ID: "id-2", Updated: now.Add(time.Minute)},
@ -74,7 +74,7 @@ func TestSyncGet(t *testing.T) {
name string name string
ts time.Time ts time.Time
expStatus int expStatus int
expItems []Syncable expItems []Item
}{ }{
{ {
name: "full", name: "full",
@ -85,7 +85,7 @@ func TestSyncGet(t *testing.T) {
name: "normal", name: "normal",
ts: now.Add(-6 * time.Minute), ts: now.Add(-6 * time.Minute),
expStatus: http.StatusOK, expStatus: http.StatusOK,
expItems: []Syncable{items[1], items[2]}, expItems: []Item{items[1], items[2]},
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@ -101,7 +101,7 @@ func TestSyncGet(t *testing.T) {
if res.Result().StatusCode != tc.expStatus { if res.Result().StatusCode != tc.expStatus {
t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode) t.Errorf("exp %v, got %v", tc.expStatus, res.Result().StatusCode)
} }
var actItems []Syncable var actItems []Item
actBody, err := io.ReadAll(res.Result().Body) actBody, err := io.ReadAll(res.Result().Body)
if err != nil { if err != nil {
t.Errorf("exp nil, got %v", err) t.Errorf("exp nil, got %v", err)
@ -135,25 +135,32 @@ func TestSyncPost(t *testing.T) {
name string name string
reqBody []byte reqBody []byte
expStatus int expStatus int
expItems []Syncable expItems []Item
}{ }{
{ {
name: "empty", name: "empty",
expStatus: http.StatusBadRequest, expStatus: http.StatusBadRequest,
}, },
{ {
name: "invalid", name: "invalid json",
reqBody: []byte(`{"fail}`), reqBody: []byte(`{"fail}`),
expStatus: http.StatusBadRequest, expStatus: http.StatusBadRequest,
}, },
{
name: "invalid item",
reqBody: []byte(`[
{"id":"id-1","kind":"test","updated":"2024-09-06T08:00:00Z"},
]`),
expStatus: http.StatusBadRequest,
},
{ {
name: "normal", name: "normal",
reqBody: []byte(`[ reqBody: []byte(`[
{"ID":"id-1","Updated":"2024-09-06T08:00:00Z","Deleted":false,"Item":""}, {"id":"id-1","kind":"test","updated":"2024-09-06T08:00:00Z","deleted":false,"body":"item"},
{"ID":"id-2","Updated":"2024-09-06T08:12:00Z","Deleted":false,"Item":""} {"id":"id-2","kind":"test","updated":"2024-09-06T08:12:00Z","deleted":false,"body":"item2"}
]`), ]`),
expStatus: http.StatusNoContent, expStatus: http.StatusNoContent,
expItems: []Syncable{ expItems: []Item{
{ID: "id-1", Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)}, {ID: "id-1", Updated: time.Date(2024, 9, 6, 8, 0, 0, 0, time.UTC)},
{ID: "id-2", Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)}, {ID: "id-2", Updated: time.Date(2024, 9, 6, 12, 0, 0, 0, time.UTC)},
}, },

View File

@ -20,7 +20,7 @@ func TestMemoryItem(t *testing.T) {
} }
t.Log("add one") t.Log("add one")
t1 := NewSyncable("test") t1 := NewItem("test")
if actErr := mem.Update(t1); actErr != nil { if actErr := mem.Update(t1); actErr != nil {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }
@ -38,7 +38,7 @@ func TestMemoryItem(t *testing.T) {
before := time.Now() before := time.Now()
t.Log("add second") t.Log("add second")
t2 := NewSyncable("test 2") t2 := NewItem("test 2")
if actErr := mem.Update(t2); actErr != nil { if actErr := mem.Update(t2); actErr != nil {
t.Errorf("exp nil, got %v", actErr) t.Errorf("exp nil, got %v", actErr)
} }