feat: add support for extracting tracked time and creation date from event JSON

This commit is contained in:
Mandresy RABENJAHARISON 2025-08-25 13:57:29 +03:00
parent e27b7cc233
commit d0f035d405

117
main.go
View File

@ -10,6 +10,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
) )
type TimesheetPayload struct { type TimesheetPayload struct {
@ -58,6 +59,70 @@ func parseOdooTicketID(ref string) (int64, error) {
return 0, fmt.Errorf("no ticket-<number> segment found in ref: %s", ref) return 0, fmt.Errorf("no ticket-<number> 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() { func main() {
baseURL := os.Getenv("BASE_URL") baseURL := os.Getenv("BASE_URL")
branchRef := os.Getenv("BRANCH_REF") branchRef := os.Getenv("BRANCH_REF")
@ -67,12 +132,12 @@ func main() {
trackedCreated := os.Getenv("TRACKED_CREATED") trackedCreated := os.Getenv("TRACKED_CREATED")
action := os.Getenv("EVENT_ACTION") action := os.Getenv("EVENT_ACTION")
if baseURL == "" || branchRef == "" || sender == "" || trackedSeconds == "" || trackedCreated == "" { if baseURL == "" || sender == "" {
fmt.Println("Some required environment variables are missing.") fmt.Println("Some required environment variables are missing (BASE_URL or SENDER_LOGIN).")
os.Exit(1) os.Exit(1)
} }
if action != "add_time" && action != "edited" && action != "time_tracked" && action != "time_added" { 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) fmt.Printf("Skipping action: %s\n", action)
return return
} }
@ -86,28 +151,52 @@ func main() {
defer logFile.Close() defer logFile.Close()
logger := log.New(io.MultiWriter(os.Stdout, logFile), "", log.LstdFlags) logger := log.New(io.MultiWriter(os.Stdout, logFile), "", log.LstdFlags)
// Extract task ID from branch ref logger.Printf("Event action: %s, initial TRACKED_SECONDS='%s', TRACKED_CREATED='%s'\n", action, trackedSeconds, trackedCreated)
taskID, err := parseOdooTicketID(branchRef)
if err != nil { // Ensure tracked time fields exist; otherwise, try to read from event JSON file
logger.Printf("Failed to parse Odoo ticket from BRANCH_REF '%s': %v\n", branchRef, err) if strings.TrimSpace(trackedSeconds) == "" || strings.TrimSpace(trackedCreated) == "" {
os.Exit(1) 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 { if err != nil {
logger.Printf("Invalid TRACKED_SECONDS: %v\n", err) logger.Printf("Invalid TRACKED_SECONDS '%s': %v\n", trackedSeconds, err)
os.Exit(1) os.Exit(1)
} }
hours := float64(seconds) / 3600.0 hours := float64(secondsVal) / 3600.0
desc := title desc := title
if strings.TrimSpace(desc) == "" { if strings.TrimSpace(desc) == "" {
desc = "/" desc = "/"
} }
date := trackedCreated date := normalizeDate(trackedCreated)
if idx := strings.Index(date, "T"); idx > 0 { if date == "" {
date = date[:idx] logger.Println("Unable to derive date from tracked_time.created")
os.Exit(1)
}
// Determine task_id (Odoo Ticket ID) from BRANCH_REF using ticket-<number>
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{ payload := TimesheetPayload{