package model import ( "encoding/json" "fmt" "io" "net/http" ) // --- Redmine API structs --- type IssueRef struct { ID int `json:"id"` Title string `json:"name"` } type Issue struct { ID int `json:"id"` Subject string `json:"subject"` Status IssueRef `json:"status"` Parent *IssueRef `json:"parent"` } type IssuesResponse struct { Issues []Issue `json:"issues"` TotalCount int `json:"total_count"` Offset int `json:"offset"` Limit int `json:"limit"` } // Fetch all issues with pagination func FetchAllIssues(baseURL, apiKey, projectID string) ([]Issue, error) { var all []Issue limit := 100 offset := 0 client := &http.Client{} for { url := fmt.Sprintf("%s/issues.json?project_id=%s&limit=%d&offset=%d&status_id=*", baseURL, projectID, limit, offset) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Set("X-Redmine-API-Key", apiKey) resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("HTTP request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, string(body)) } var result IssuesResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("JSON decode error: %w", err) } all = append(all, result.Issues...) if offset+limit >= result.TotalCount { break } offset += limit } return all, nil }