From d0f035d405844f5e21ed78b50a3cd576a54617d8 Mon Sep 17 00:00:00 2001 From: Mandresy RABENJAHARISON Date: Mon, 25 Aug 2025 13:57:29 +0300 Subject: [PATCH] feat: add support for extracting tracked time and creation date from event JSON --- main.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index 6def899..953eedb 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "os" "strconv" "strings" + "time" ) type TimesheetPayload struct { @@ -58,6 +59,70 @@ func parseOdooTicketID(ref string) (int64, error) { return 0, fmt.Errorf("no ticket- segment found in ref: %s", ref) } +// readEventTrackedTime tries to extract tracked_time.time and tracked_time.created +// from the event JSON file if explicit env vars are missing. It supports both +// GITEA_EVENT_PATH and GITHUB_EVENT_PATH for compatibility. +func readEventTrackedTime(logger *log.Logger) (seconds string, created string) { + paths := []string{os.Getenv("GITEA_EVENT_PATH"), os.Getenv("GITHUB_EVENT_PATH")} + for _, p := range paths { + if strings.TrimSpace(p) == "" { + continue + } + b, err := os.ReadFile(p) + if err != nil { + logger.Printf("Could not read event file '%s': %v", p, err) + continue + } + var evt map[string]any + if err := json.Unmarshal(b, &evt); err != nil { + logger.Printf("Could not parse event JSON: %v", err) + continue + } + // tracked_time is expected at top-level for time tracking related issue events + if ttRaw, ok := evt["tracked_time"]; ok { + if tt, ok := ttRaw.(map[string]any); ok { + // time + if v, ok := tt["time"]; ok { + switch t := v.(type) { + case float64: + seconds = strconv.FormatInt(int64(t), 10) + case string: + seconds = t + } + } + // created + if v, ok := tt["created"]; ok { + created, _ = v.(string) + } + } + } + if seconds != "" && created != "" { + return seconds, created + } + } + return "", "" +} + +func normalizeDate(created string) string { + created = strings.TrimSpace(created) + if created == "" { + return "" + } + // Try RFC3339 + if t, err := time.Parse(time.RFC3339, created); err == nil { + return t.Format("2006-01-02") + } + // If numeric (unix seconds) + if n, err := strconv.ParseInt(created, 10, 64); err == nil { + return time.Unix(n, 0).UTC().Format("2006-01-02") + } + // Fallback: if contains T split, otherwise return as-is + if idx := strings.Index(created, "T"); idx > 0 { + return created[:idx] + } + return created +} + func main() { baseURL := os.Getenv("BASE_URL") branchRef := os.Getenv("BRANCH_REF") @@ -67,12 +132,12 @@ func main() { trackedCreated := os.Getenv("TRACKED_CREATED") action := os.Getenv("EVENT_ACTION") - if baseURL == "" || branchRef == "" || sender == "" || trackedSeconds == "" || trackedCreated == "" { - fmt.Println("Some required environment variables are missing.") + if baseURL == "" || sender == "" { + fmt.Println("Some required environment variables are missing (BASE_URL or SENDER_LOGIN).") os.Exit(1) } if action != "add_time" && action != "edited" && action != "time_tracked" && action != "time_added" { - // Best-effort: only proceed for time-related actions; allow edited if your instance sends edited + // Only proceed for time-related actions; allow edited if your instance sends edited fmt.Printf("Skipping action: %s\n", action) return } @@ -86,28 +151,52 @@ func main() { defer logFile.Close() logger := log.New(io.MultiWriter(os.Stdout, logFile), "", log.LstdFlags) - // Extract task ID from branch ref - taskID, err := parseOdooTicketID(branchRef) - if err != nil { - logger.Printf("Failed to parse Odoo ticket from BRANCH_REF '%s': %v\n", branchRef, err) - os.Exit(1) + logger.Printf("Event action: %s, initial TRACKED_SECONDS='%s', TRACKED_CREATED='%s'\n", action, trackedSeconds, trackedCreated) + + // Ensure tracked time fields exist; otherwise, try to read from event JSON file + if strings.TrimSpace(trackedSeconds) == "" || strings.TrimSpace(trackedCreated) == "" { + sec2, crt2 := readEventTrackedTime(logger) + logger.Printf("Fallback from event file: seconds='%s', created='%s'\n", sec2, crt2) + if trackedSeconds == "" { + trackedSeconds = sec2 + } + if trackedCreated == "" { + trackedCreated = crt2 + } + } + if strings.TrimSpace(trackedSeconds) == "" || strings.TrimSpace(trackedCreated) == "" { + logger.Println("No tracked_time information found in event. Nothing to do.") + return } - seconds, err := strconv.ParseInt(trackedSeconds, 10, 64) + secondsVal, err := strconv.ParseInt(trackedSeconds, 10, 64) if err != nil { - logger.Printf("Invalid TRACKED_SECONDS: %v\n", err) + logger.Printf("Invalid TRACKED_SECONDS '%s': %v\n", trackedSeconds, err) os.Exit(1) } - hours := float64(seconds) / 3600.0 + hours := float64(secondsVal) / 3600.0 desc := title if strings.TrimSpace(desc) == "" { desc = "/" } - date := trackedCreated - if idx := strings.Index(date, "T"); idx > 0 { - date = date[:idx] + date := normalizeDate(trackedCreated) + if date == "" { + logger.Println("Unable to derive date from tracked_time.created") + os.Exit(1) + } + + // Determine task_id (Odoo Ticket ID) from BRANCH_REF using ticket- + if strings.TrimSpace(branchRef) == "" { + logger.Println("BRANCH_REF is missing; cannot determine Odoo ticket id") + os.Exit(1) + } + var taskID int64 + taskID, err = parseOdooTicketID(branchRef) + if err != nil || taskID == 0 { + logger.Printf("Failed to parse Odoo ticket from BRANCH_REF '%s': %v\n", branchRef, err) + os.Exit(1) } payload := TimesheetPayload{