From e1df2dea3b9397ce934b09349dfc38e2986aa989 Mon Sep 17 00:00:00 2001 From: Justin Hawkins Date: Fri, 20 Jun 2025 16:40:28 +0200 Subject: [PATCH] Complete time tracker API access --- mite/mite.go | 100 ++++++++++++++++++++++++++++++++++++++++++++++ mite/mite_test.go | 64 ++++++++++++++++++++++------- 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/mite/mite.go b/mite/mite.go index 4cf8af6..921f9ca 100644 --- a/mite/mite.go +++ b/mite/mite.go @@ -264,6 +264,7 @@ type apiTimeTrackerEntry struct { type timeTrackerHolder struct { TrackingTimeEntry *TrackingTimeEntry `json:"tracking_time_entry"` + StoppedTimeEntry *StoppedTimeEntry `json:"stopped_time_entry"` } type TrackingTimeEntry struct { @@ -272,6 +273,25 @@ type TrackingTimeEntry struct { Since time.Time `json:"since"` } +type StoppedTimeEntry struct { + ID int `json:"id"` + Minutes int `json:"minutes"` +} + +// { +// "tracker": { +// "tracking_time_entry": { +// "id": 36135322, +// "minutes": 0, +// "since": "2015-10-15T17:33:52+02:00" +// }, +// "stopped_time_entry": { +// "id": 36134329, +// "minutes": 46 +// } +// } +// } + // { // "tracker": { // "tracking_time_entry": { @@ -298,6 +318,86 @@ func (a APIClient) GetTimeTracker() (TrackingTimeEntry, error) { return *r.TimeTrackerHolder.TrackingTimeEntry, nil } +func (a APIClient) StartTimeTracker(id int) (TrackingTimeEntry, *StoppedTimeEntry, error) { + url := fmt.Sprintf("/tracker/%d.json", id) + r := apiTimeTrackerEntry{} + + err := a.patch(url, &r) + + if err != nil { + return TrackingTimeEntry{}, nil, err + } + if r.TimeTrackerHolder.TrackingTimeEntry == nil { + // I don't think this should happen, the patch should have been a 404? + + panic(fmt.Sprintf("unexpected failure to find a tracking entry in a successful PATCH\n%#v", r.TimeTrackerHolder)) + } + return *r.TimeTrackerHolder.TrackingTimeEntry, r.TimeTrackerHolder.StoppedTimeEntry, nil +} + +func (a APIClient) StopTimeTracker(id int) (StoppedTimeEntry, error) { + url := fmt.Sprintf("/tracker/%d.json", id) + r := apiTimeTrackerEntry{} + + err := a.delete(url, &r) + + if err != nil { + return StoppedTimeEntry{}, err + } + if r.TimeTrackerHolder.StoppedTimeEntry == nil { + // I don't think this should happen, the patch should have been a 404? + + panic(fmt.Sprintf("unexpected failure to find a tracking entry in a successful DELETE\n%#v", r.TimeTrackerHolder)) + } + return *r.TimeTrackerHolder.StoppedTimeEntry, nil +} + +func (a APIClient) delete(path string, data any) error { + + req, err := http.NewRequest("DELETE", baseurl(a.domain, path), nil) + if err != nil { + return err + } + req.Header.Add("X-MiteApiKey", a.apiKey) + resp, err := http.DefaultClient.Do(req) + + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode >= 300 { + return fmt.Errorf("expected 2XX, got %d", resp.StatusCode) + } + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return err + } + return nil +} + +func (a APIClient) patch(path string, data any) error { + + req, err := http.NewRequest("PATCH", baseurl(a.domain, path), nil) + if err != nil { + return err + } + req.Header.Add("X-MiteApiKey", a.apiKey) + resp, err := http.DefaultClient.Do(req) + + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode >= 300 { + return fmt.Errorf("expected 2XX, got %d", resp.StatusCode) + } + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return err + } + return nil +} + func (a APIClient) post(path string, data any) error { b, err := json.Marshal(data) diff --git a/mite/mite_test.go b/mite/mite_test.go index 517d163..ddc47a5 100644 --- a/mite/mite_test.go +++ b/mite/mite_test.go @@ -6,19 +6,10 @@ import ( ) func TestUnmarshalTimeTracking(t *testing.T) { - trackerOn := []byte(` -{ - "tracker": { - "tracking_time_entry": { - "id": 36135321, - "minutes": 247, - "since": "2015-10-15T17:05:04+02:00" - } - } - }`) + trackerOff := []byte(`{ - "tracker": {} -}`) + "tracker": {} + }`) off := apiTimeTrackerEntry{} err := json.Unmarshal(trackerOff, &off) if err != nil { @@ -28,14 +19,23 @@ func TestUnmarshalTimeTracking(t *testing.T) { t.Error("expected nil, but is not") } } - + trackerOn := []byte(` + { + "tracker": { + "tracking_time_entry": { + "id": 36135321, + "minutes": 247, + "since": "2015-10-15T17:05:04+02:00" + } + } + }`) on := apiTimeTrackerEntry{} err = json.Unmarshal(trackerOn, &on) if err != nil { t.Error(err) } else { if on.TimeTrackerHolder.TrackingTimeEntry == nil { - t.Error("expected note nil, but is not") + t.Error("expected not nil, but is not") } else { if on.TimeTrackerHolder.TrackingTimeEntry.ID != 36135321 { t.Error("bad unmarshal?") @@ -43,4 +43,40 @@ func TestUnmarshalTimeTracking(t *testing.T) { } } + trackerStart := []byte(`{ + "tracker": { + "tracking_time_entry": { + "id": 36135322, + "minutes": 0, + "since": "2015-10-15T17:33:52+02:00" + }, + "stopped_time_entry": { + "id": 36134329, + "minutes": 46 + } + } +}`) + start := apiTimeTrackerEntry{} + err = json.Unmarshal(trackerStart, &start) + if err != nil { + t.Error(err) + } else { + if start.TimeTrackerHolder.TrackingTimeEntry == nil { + t.Error("expected not nil, but is not") + } else { + if start.TimeTrackerHolder.TrackingTimeEntry.ID != 36135322 { + t.Error("bad unmarshal?") + } + } + + if start.TimeTrackerHolder.StoppedTimeEntry == nil { + t.Error("expected not nil, but is nil") + } else { + if start.TimeTrackerHolder.StoppedTimeEntry.ID != 36134329 { + t.Error("bad unmarshal") + } + } + + } + }