summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--main.go26
4 files changed, 20 insertions, 15 deletions
diff --git a/README.md b/README.md
index 656590c..1087f5d 100644
--- a/README.md
+++ b/README.md
@@ -52,5 +52,3 @@ The lines will be formatted like a cron file, but instead of a command, we will
## Future work
It turns out jobs can be added AND removed by an ID that is returned from the Add function. It could be interesting to add/remove based on the last seen map (with the job IDs), instead of completely recreating all the jobs if the input file changes.
-
-We should probably retry failed network requests. It would not be great to have a temporary connectivity issue lead to missing an important task!
diff --git a/go.mod b/go.mod
index d761957..5694ea0 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,7 @@ module davidschlachter.com/todoist-repeater
go 1.24.1
-require github.com/robfig/cron/v3 v3.0.1
+require (
+ github.com/cenkalti/backoff/v5 v5.0.3
+ github.com/robfig/cron/v3 v3.0.1
+)
diff --git a/go.sum b/go.sum
index 0667807..512dd4d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,4 @@
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
diff --git a/main.go b/main.go
index 8d104b7..00a319f 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
import (
"bufio"
"bytes"
+ "context"
"fmt"
"io"
"log"
@@ -14,6 +15,7 @@ import (
"strings"
"time"
+ "github.com/cenkalti/backoff/v5"
"github.com/robfig/cron/v3"
)
@@ -31,7 +33,10 @@ type Task struct {
}
func (t Task) Run() {
- createTask(t.Name)
+ _, err := backoff.Retry(context.Background(), func() (bool, error) { return true, createTask(t.Name) }, backoff.WithBackOff(backoff.NewExponentialBackOff()))
+ if err != nil {
+ log.Printf("Failed to create task: %s", err)
+ }
}
var lastSeenInput = map[InputLine]TaskAddingJob{}
@@ -164,7 +169,7 @@ func inputChanged(old, new map[InputLine]TaskAddingJob) bool {
return false
}
-func createTask(task string) {
+func createTask(task string) error {
jsonBody := []byte(fmt.Sprintf(`{"content": %s}`, strconv.Quote(task)))
req, err := http.NewRequest(
http.MethodPost,
@@ -172,29 +177,26 @@ func createTask(task string) {
bytes.NewBuffer(jsonBody),
)
if err != nil {
- log.Printf("Failed to create request: %s", err)
- return
+ return fmt.Errorf("creating request: %w", err)
}
req.Header.Add("Authorization", "Bearer "+todoistToken)
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
- log.Printf("Failed to Do request: %s", err)
- return
+ return fmt.Errorf("doing request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- log.Printf("Got status code %d, expected 200 when creating task", resp.StatusCode)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
- log.Printf("Failed to read body: %s", err)
- return
+ return fmt.Errorf("reading body when preparing error message (HTTP status was %d): %w", resp.StatusCode, err)
}
- log.Printf("Got body: %s", string(bodyBytes))
- } else {
- log.Printf("Created task '%s'", task)
+ return fmt.Errorf("expected 200, got status code %d: body was %s", resp.StatusCode, strconv.Quote(string(bodyBytes)))
}
+
+ log.Printf("Created task '%s'", task)
+ return nil
}
func addJobs(c *cron.Cron, tasks map[InputLine]TaskAddingJob) {