Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/sync_ooo_to_gcal/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strings"
"time"
Expand Down Expand Up @@ -163,6 +164,7 @@ func (e *Event) Run(ctx context.Context) {

calendarIDs := []string{"primary"}
if err := core.InsertOOOEvents(ctx, jwtCfg, env.Requests, calendarIDs); err != nil {
log.Printf("Sync completed with errors: %v", err)
core.Die("insert events: %v", err)
}

Expand Down
12 changes: 11 additions & 1 deletion core/calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"context"
"errors"
"fmt"
"log"
"time"
Expand All @@ -12,22 +13,27 @@ import (
)

func InsertOOOEvents(ctx context.Context, jwtCfg *jwt.Config, requests []ClockifyRequest, calendarIDs []string) error {
var errs []error

for _, r := range requests {
// Load user's local timezone
loc, err := time.LoadLocation(r.UserTimeZone)
if err != nil {
log.Printf("skip %s: unknown tz %q: %v", r.ID, r.UserTimeZone, err)
errs = append(errs, fmt.Errorf("req=%s user=%s: unknown tz %q: %w", r.ID, r.UserEmail, r.UserTimeZone, err))
continue
}

startUTC, err := ParseTimeAny(r.TimeOffPeriod.Period.Start)
if err != nil {
log.Printf("skip %s: bad period.start: %v", r.ID, err)
errs = append(errs, fmt.Errorf("req=%s user=%s: bad period.start: %w", r.ID, r.UserEmail, err))
continue
}
endUTC, err := ParseTimeAny(r.TimeOffPeriod.Period.End)
if err != nil {
log.Printf("skip %s: bad period.end: %v", r.ID, err)
errs = append(errs, fmt.Errorf("req=%s user=%s: bad period.end: %w", r.ID, r.UserEmail, err))
continue
}

Expand Down Expand Up @@ -55,6 +61,7 @@ func InsertOOOEvents(ctx context.Context, jwtCfg *jwt.Config, requests []Clockif
srv, err := calendar.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Printf("user %s: calendar service error: %v", r.UserEmail, err)
errs = append(errs, fmt.Errorf("req=%s user=%s: calendar service error: %w", r.ID, r.UserEmail, err))
continue
}

Expand Down Expand Up @@ -85,6 +92,7 @@ func InsertOOOEvents(ctx context.Context, jwtCfg *jwt.Config, requests []Clockif
if err != nil {
log.Printf("lookup %s (user=%s cal=%s) failed: %v",
r.ID, r.UserEmail, calID, err)
errs = append(errs, fmt.Errorf("req=%s user=%s cal=%s: lookup failed: %w", r.ID, r.UserEmail, calID, err))
continue
}

Expand All @@ -108,6 +116,7 @@ func InsertOOOEvents(ctx context.Context, jwtCfg *jwt.Config, requests []Clockif
if err != nil {
log.Printf("insert %s (user=%s cal=%s) failed: %v",
r.ID, r.UserEmail, calID, err)
errs = append(errs, fmt.Errorf("req=%s user=%s cal=%s: insert failed: %w", r.ID, r.UserEmail, calID, err))
continue
}

Expand All @@ -118,5 +127,6 @@ func InsertOOOEvents(ctx context.Context, jwtCfg *jwt.Config, requests []Clockif
}

}
return nil

return errors.Join(errs...)
}
41 changes: 41 additions & 0 deletions core/calendar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package core

import (
"context"
"errors"
"testing"

"golang.org/x/oauth2/jwt"
)

func TestInsertOOOEvents_CollectsErrors(t *testing.T) {
ctx := context.Background()
jwtCfg := &jwt.Config{}
calendarIDs := []string{"primary"}

reqs := []ClockifyRequest{
fixtureBadTimeZone(),
fixtureInvalidStartDate(),
fixtureInvalidEndDate(),
}

err := InsertOOOEvents(ctx, jwtCfg, reqs, calendarIDs)
if err == nil {
t.Fatalf("expected non-nil error")
}

var multiErrors interface{ Unwrap() []error }
if !errors.As(err, &multiErrors) {
t.Fatalf("expected joined error with Unwrap() []error, got: %T", err)
}

collectedErrCount := len(multiErrors.Unwrap())
expectedErrCount := len(reqs)

if collectedErrCount != expectedErrCount {
t.Fatalf("expected %d collected errors, got %d",
expectedErrCount,
collectedErrCount,
)
}
}
28 changes: 28 additions & 0 deletions core/clockify_fixtures_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package core

import "time"

func makeRequest(id, tz, start, end string) ClockifyRequest {
var r ClockifyRequest

r.ID = id
r.UserEmail = "fixture@example.com"
r.UserTimeZone = tz
r.CreatedAt = time.Date(2025, 12, 1, 12, 0, 0, 0, time.UTC).Format(time.RFC3339)
r.PolicyName = "Vacation"

r.TimeOffPeriod.Period.Start = start
r.TimeOffPeriod.Period.End = end

return r
}

func fixtureBadTimeZone() ClockifyRequest {
return makeRequest("fixture-bad-time-zone", "Not/A_Timezone", "2025-12-10T00:00:00Z", "2025-12-10T23:59:59Z")
}
func fixtureInvalidStartDate() ClockifyRequest {
return makeRequest("fixture-bad-start", "America/New_York", "not-a-date", "2025-12-10T23:59:59Z")
}
func fixtureInvalidEndDate() ClockifyRequest {
return makeRequest("fixture-bad-end", "America/New_York", "2025-12-10T00:00:00Z", "not-a-date")
}