internal/workflow + cli:gitea.py 工作流命令 Go 化(#6)
- internal/workflow:Client 介面+判定語義(最後留言、作者/審核者跟進、 assignee 交集、最久未更新優先),含假 client 單元測試 - internal/workflow/giteaapi.go:以 internal/gitea(#5)實作 Client - internal/cli:註冊 whoami/orgs/members/mine/pulls/next;全域選項允許 出現在命令之後;結束碼 0/2/3 - 與 gitea.py 交叉驗證:next/mine/pulls/members(--has-work) 拾取一致 - 依 #5 分支 feature/issue5-gitea-client 為基底
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
// giteaapi.go — 以 internal/gitea 的 HTTP 用戶端實作 workflow.Client。
|
||||
//
|
||||
// 端點與查詢參數對照 gitea.py:清單一律帶 limit 分頁(ListAll 固定 50);
|
||||
// issues 端點帶 type=issues(排除 PR)與 state=open;pulls 帶 state=open。
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"gitea.alterminal.com/alterminal/teai/internal/gitea"
|
||||
)
|
||||
|
||||
// errNoUsername 表示 /user 回應中找不到帳號名(伺服端回應異常)。
|
||||
var errNoUsername = errors.New("gitea: GET /user: response has no username")
|
||||
|
||||
// APIClient 是 Client 介面的 HTTP 實作,包裝 internal/gitea.Client。
|
||||
type APIClient struct {
|
||||
Client *gitea.Client
|
||||
}
|
||||
|
||||
// 確認 APIClient 實作 Client 介面(編譯期檢查)。
|
||||
var _ Client = (*APIClient)(nil)
|
||||
|
||||
func (a *APIClient) CurrentUser() (string, error) {
|
||||
var u User
|
||||
if err := a.Client.GetJSON(context.Background(), "/user", &u); err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := u.Name()
|
||||
if name == "" {
|
||||
return "", errNoUsername
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListOrgs() ([]string, error) {
|
||||
var orgs []Org
|
||||
if err := a.Client.ListAll(context.Background(), "/user/orgs", gitea.ListOptions{}, &orgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, 0, len(orgs))
|
||||
for _, o := range orgs {
|
||||
if n := o.Account(); n != "" {
|
||||
names = append(names, n)
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListTeams(org string) ([]Team, error) {
|
||||
var teams []Team
|
||||
path := "/orgs/" + url.PathEscape(org) + "/teams"
|
||||
if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &teams); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListTeamMembers(teamID int64) ([]string, error) {
|
||||
var users []User
|
||||
path := "/teams/" + strconv.FormatInt(teamID, 10) + "/members"
|
||||
if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, 0, len(users))
|
||||
for i := range users {
|
||||
if n := users[i].Name(); n != "" {
|
||||
names = append(names, n)
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListOrgRepos(org string) ([]Repo, error) {
|
||||
var repos []Repo
|
||||
path := "/orgs/" + url.PathEscape(org) + "/repos"
|
||||
if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &repos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListIssues(owner, repo string) ([]Issue, error) {
|
||||
var issues []Issue
|
||||
path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) + "/issues"
|
||||
opts := gitea.ListOptions{Extra: url.Values{
|
||||
"type": {"issues"},
|
||||
"state": {"open"},
|
||||
}}
|
||||
if err := a.Client.ListAll(context.Background(), path, opts, &issues); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// /issues 也會回傳 PR(帶 pull_request 欄位);一律排除。
|
||||
out := make([]Issue, 0, len(issues))
|
||||
for i := range issues {
|
||||
if issues[i].PullRequest == nil {
|
||||
out = append(out, issues[i])
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListPulls(owner, repo string) ([]PullRequest, error) {
|
||||
var pulls []PullRequest
|
||||
path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) + "/pulls"
|
||||
opts := gitea.ListOptions{Extra: url.Values{"state": {"open"}}}
|
||||
if err := a.Client.ListAll(context.Background(), path, opts, &pulls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pulls, nil
|
||||
}
|
||||
|
||||
func (a *APIClient) ListComments(owner, repo string, number int64) ([]Comment, error) {
|
||||
var comments []Comment
|
||||
path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) +
|
||||
"/issues/" + strconv.FormatInt(number, 10) + "/comments"
|
||||
if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &comments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return comments, nil
|
||||
}
|
||||
Reference in New Issue
Block a user