package gitea import ( "context" "encoding/json" "fmt" "net/url" "strconv" ) type Issue struct { Number int `json:"number"` Title string `json:"title"` Body string `json:"body"` HTMLURL string `json:"html_url"` State string `json:"state"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` Labels []Label `json:"labels"` Assignees []User `json:"assignees"` Comments int `json:"comments"` } type Label struct { ID int64 `json:"id"` Name string `json:"name"` } type User struct { Login string `json:"login"` } type CreateIssueArgs struct { Title string `json:"title"` Body string `json:"body"` Labels []int64 `json:"labels,omitempty"` Assignees []string `json:"assignees,omitempty"` Milestone int64 `json:"milestone,omitempty"` } func (c *Client) GetIssue(ctx context.Context, owner, repo string, number int) (*Issue, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner, repo, number) body, status, err := c.GetJSON(ctx, p) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var iss Issue if err := json.Unmarshal(body, &iss); err != nil { return nil, err } return &iss, nil } func (c *Client) CreateIssue(ctx context.Context, owner, repo string, args CreateIssueArgs) (*Issue, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner, repo) payload, err := json.Marshal(args) if err != nil { return nil, err } body, status, err := c.PostJSON(ctx, p, payload) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var iss Issue if err := json.Unmarshal(body, &iss); err != nil { return nil, err } return &iss, nil } // ListIssuesArgs captures the optional query params for ListIssues. type ListIssuesArgs struct { State string // "open" | "closed" | "all" Labels string // comma-separated label names Since string // ISO 8601 Page int Limit int } // ListIssues fetches issues for a repo. Pulls are excluded server-side // (type=issues) so they don't leak through the same endpoint. func (c *Client) ListIssues(ctx context.Context, owner, repo string, args ListIssuesArgs) ([]Issue, error) { q := url.Values{} q.Set("type", "issues") if args.State != "" { q.Set("state", args.State) } if args.Labels != "" { q.Set("labels", args.Labels) } if args.Since != "" { q.Set("since", args.Since) } if args.Page > 0 { q.Set("page", strconv.Itoa(args.Page)) } if args.Limit > 0 { q.Set("limit", strconv.Itoa(args.Limit)) } p := fmt.Sprintf("/api/v1/repos/%s/%s/issues?%s", owner, repo, q.Encode()) body, status, err := c.GetJSON(ctx, p) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var issues []Issue if err := json.Unmarshal(body, &issues); err != nil { return nil, err } return issues, nil } // SetIssueState flips an issue between "open" and "closed" via PATCH. // Gitea uses the same endpoint for both transitions. func (c *Client) SetIssueState(ctx context.Context, owner, repo string, number int, state string) (*Issue, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner, repo, number) payload, err := json.Marshal(map[string]string{"state": state}) if err != nil { return nil, err } body, status, err := c.PatchJSON(ctx, p, payload) if err != nil { return nil, err } if err := MapStatus(status, body); err != nil { return nil, err } var iss Issue if err := json.Unmarshal(body, &iss); err != nil { return nil, err } return &iss, nil } type IssueComment struct { ID int64 `json:"id"` Body string `json:"body"` HTMLURL string `json:"html_url"` } // CreateIssueComment posts to /issues/{index}/comments. Per Gitea, this same endpoint // works for both issues and pull requests (PRs share index space with issues). func (c *Client) CreateIssueComment(ctx context.Context, owner, repo string, index int, body string) (*IssueComment, error) { p := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", owner, repo, index) payload, err := json.Marshal(map[string]string{"body": body}) if err != nil { return nil, err } respBody, status, err := c.PostJSON(ctx, p, payload) if err != nil { return nil, err } if err := MapStatus(status, respBody); err != nil { return nil, err } var c2 IssueComment if err := json.Unmarshal(respBody, &c2); err != nil { return nil, err } return &c2, nil }