package model import ( "encoding/json" "fmt" "io" "net/http" ) // --- Redmine Project API structs --- type Project struct { ID int `json:"id"` Name string `json:"name"` } type ProjectResponse struct { Project Project `json:"project"` } type ProjectsResponse struct { Projects []Project `json:"projects"` TotalCount int `json:"total_count"` Offset int `json:"offset"` Limit int `json:"limit"` } // Fetch a single project's details func FetchProject(baseURL, apiKey, projectID string) (*Project, error) { client := &http.Client{} url := fmt.Sprintf("%s/projects/%s.json", baseURL, projectID) 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 ProjectResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("JSON decode error: %w", err) } return &result.Project, nil } // Fetch all projects with pagination func FetchAllProjects(baseURL, apiKey string) ([]Project, error) { var all []Project limit := 100 offset := 0 client := &http.Client{} for { url := fmt.Sprintf("%s/projects.json?limit=%d&offset=%d", baseURL, 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 ProjectsResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("JSON decode error: %w", err) } all = append(all, result.Projects...) if offset+limit >= result.TotalCount { break } offset += limit } return all, nil }