Skip to content

Commit

Permalink
feat: update to use the Toggl v9 API (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
danstis authored Jun 23, 2024
1 parent 32360e6 commit f93ef0f
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 28 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ require (
github.com/tevino/abool v1.2.0 // indirect
golang.org/x/image v0.10.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sys v0.21.0 // indirect
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
fyne.io/systray v1.10.0 h1:Yr1D9Lxeiw3+vSuZWPlaHC8BMjIHZXJKkek706AfYQk=
fyne.io/systray v1.10.0/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9 h1:1ltqoej5GtaWF8jaiA49HwsZD459jqm9YFz9ZtMFpQA=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
Expand All @@ -17,6 +23,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco=
golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand All @@ -26,6 +34,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -38,6 +48,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
Expand Down
42 changes: 15 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,26 +142,21 @@ func onExit() {

func (c *Settings) getUserDetail() error {
type UserResponse struct {
Data struct {
ID int32 `json:"id"`
Workspaces []Workspaces `json:"workspaces"`
} `json:"data"`
ID int32 `json:"id"`
Workspaces []Workspaces `json:"workspaces"`
}
var ur UserResponse
toggl := resty.New().SetHostURL("https://api.track.toggl.com/api/v8").SetBasicAuth(c.Token, "api_token")
toggl := resty.New().SetHostURL("https://api.track.toggl.com/api/v9").SetBasicAuth(c.Token, "api_token")

_, err := toggl.R().
SetQueryParams(map[string]string{
"user_agent": c.Email,
}).
SetResult(&ur).
Get("/me?with_related_data=true")
if err != nil {
return fmt.Errorf("unable to get user details from the Toggl API: %v", err)
}

c.UserID = fmt.Sprint(ur.Data.ID)
c.Workspaces = ur.Data.Workspaces
c.UserID = fmt.Sprint(ur.ID)
c.Workspaces = ur.Workspaces
return nil
}

Expand Down Expand Up @@ -194,7 +189,7 @@ func getClosedTimeEntries(c *Settings, w string) (time.Duration, error) {
"user_agent": c.Email,
"workspace_id": w,
"user_ids": c.UserID,
"since": getLastMonday(),
"since": getLastMonday(time.Now()),
}).
SetResult(&ct).
Get("/weekly")
Expand All @@ -207,43 +202,36 @@ func getClosedTimeEntries(c *Settings, w string) (time.Duration, error) {

func getOpenTimeEntry(c *Settings, w string) (time.Duration, error) {
type TimeEntriesResponse struct {
Data struct {
WID int32 `json:"wid"`
Duration int32 `json:"duration"`
} `json:"data"`
WID int32 `json:"wid"`
Duration int32 `json:"duration"`
}
var ot TimeEntriesResponse

toggl := resty.New().SetHostURL("https://api.track.toggl.com/api/v8").SetBasicAuth(c.Token, "api_token")
toggl := resty.New().SetHostURL("https://api.track.toggl.com/api/v9").SetBasicAuth(c.Token, "api_token")

_, err := toggl.R().
SetQueryParams(map[string]string{
"user_agent": c.Email,
"wid": w,
}).
SetResult(&ot).
Get("/time_entries/current")
Get("/me/time_entries/current")
if err != nil {
return time.Duration(0), fmt.Errorf("unable to get current time entry from the Toggl API: %v", err)
}

// if the returned duration is not negative then there is no open entry.
// we also filter entries that do not match the workspace here.
if ot.Data.Duration >= 0 || fmt.Sprint(ot.Data.WID) != w {
if ot.Duration >= 0 || fmt.Sprint(ot.WID) != w {
return 0, nil
}

// Calculate the number of seconds based on the input data.
// Unix epoch plus returned value of duration = seconds the current entry has been running for.
od := int32(time.Now().Unix()) + ot.Data.Duration
od := int32(time.Now().Unix()) + ot.Duration

return time.Duration(od) * time.Second, nil
}

func getLastMonday() string {
t := time.Now()
delta := (int(t.Weekday()) - 1) * -1
t = t.AddDate(0, 0, delta)
func getLastMonday(t time.Time) string {
delta := (int(t.Weekday()) + 6) % 7
t = t.AddDate(0, 0, -delta)

return t.Format("2006-01-02")
}
Expand Down
181 changes: 181 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package main

import (
"testing"
"time"
)

func TestGetLastMonday(t *testing.T) {
tests := []struct {
name string
date time.Time
expected string
}{
{
name: "Monday",
date: time.Date(2022, time.January, 3, 0, 0, 0, 0, time.UTC),
expected: "2022-01-03",
},
{
name: "Tuesday",
date: time.Date(2022, time.January, 4, 0, 0, 0, 0, time.UTC),
expected: "2022-01-03",
},
{
name: "Sunday",
date: time.Date(2022, time.January, 9, 0, 0, 0, 0, time.UTC),
expected: "2022-01-03",
},
{
name: "Leap year February 29",
date: time.Date(2024, time.February, 29, 0, 0, 0, 0, time.UTC),
expected: "2024-02-26",
},
{
name: "Week spanning two months",
date: time.Date(2022, time.January, 31, 0, 0, 0, 0, time.UTC),
expected: "2022-01-31",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getLastMonday(tt.date)
if result != tt.expected {
t.Errorf("Expected %s, but got %s", tt.expected, result)
}
})
}
}
func TestGetHours(t *testing.T) {
tests := []struct {
name string
duration time.Duration
expected int
}{
{
name: "Zero duration",
duration: 0,
expected: 0,
},
{
name: "Less than an hour",
duration: time.Minute * 30,
expected: 0,
},
{
name: "Exactly one hour",
duration: time.Hour,
expected: 1,
},
{
name: "More than one hour",
duration: time.Hour + time.Minute*30,
expected: 1,
},
{
name: "Multiple hours",
duration: time.Hour*3 + time.Minute*45,
expected: 3,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getHours(tt.duration)
if result != tt.expected {
t.Errorf("Expected %d, but got %d", tt.expected, result)
}
})
}
}
func TestGetMinutes(t *testing.T) {
tests := []struct {
name string
duration time.Duration
expected int
}{
{
name: "Zero duration",
duration: 0,
expected: 0,
},
{
name: "Less than a minute rounds up",
duration: time.Second * 30,
expected: 1,
},
{
name: "Less than a minute rounds down",
duration: time.Second * 20,
expected: 0,
},
{
name: "Exactly one minute",
duration: time.Minute,
expected: 1,
},
{
name: "More than one minute",
duration: time.Minute + time.Second*30,
expected: 2,
},
{
name: "Multiple minutes",
duration: time.Minute*3 + time.Second*45,
expected: 4,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getMinutes(tt.duration)
if result != tt.expected {
t.Errorf("Expected %d, but got %d", tt.expected, result)
}
})
}
}

func TestAdd(t *testing.T) {
tests := []struct {
name string
t1 togglTime
t2 togglTime
expected togglTime
}{
{
name: "Adding zero time",
t1: togglTime{hours: 1, minutes: 30},
t2: togglTime{hours: 0, minutes: 0},
expected: togglTime{hours: 1, minutes: 30},
},
{
name: "Adding minutes",
t1: togglTime{hours: 1, minutes: 30},
t2: togglTime{hours: 0, minutes: 45},
expected: togglTime{hours: 2, minutes: 15},
},
{
name: "Adding hours",
t1: togglTime{hours: 1, minutes: 30},
t2: togglTime{hours: 2, minutes: 0},
expected: togglTime{hours: 3, minutes: 30},
},
{
name: "Adding hours and minutes",
t1: togglTime{hours: 1, minutes: 30},
t2: togglTime{hours: 2, minutes: 45},
expected: togglTime{hours: 4, minutes: 15},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := togglTime{}
result.add(tt.t1)
result.add(tt.t2)
if result != tt.expected {
t.Errorf("Expected %v, but got %v", tt.expected, result)
}
})
}
}

0 comments on commit f93ef0f

Please sign in to comment.