valiation and logging
This commit is contained in:
parent
fdbefeffe2
commit
cb500b7558
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue