refactor(completion): complete assignees and tags through the SDK

Completion fetched repository assignees and tags with raw GETs. List them
through new Repositories.Assignees and Repositories.Tags service methods on
the SDK, and remove the raw fetch helper.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-12 09:55:36 +02:00
1 parent ae0ea27bb0
commit 03aa64233f
3 files changed
+46 -32

No files matched your search

+4 -26
View File
@@ -5,10 +5,8 @@ package app
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"maps" "maps"
"net/http"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@@ -18,7 +16,6 @@ import (
"gitea.dev/tea/internal/services" "gitea.dev/tea/internal/services"
"gitea.dev/tea/internal/settings" "gitea.dev/tea/internal/settings"
"gitea.dev/tea/internal/terminal" "gitea.dev/tea/internal/terminal"
"gitea.dev/tea/internal/transport"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@@ -337,16 +334,6 @@ func (a *application) completionTarget(ctx context.Context, requireRepo bool) (t
return t, nil return t, nil
} }
// fetchJSON decodes the response to a GET of endpoint, for values that no service lists.
func fetchJSON(ctx context.Context, client *transport.Client, endpoint string, out any) error {
return services.API{Client: client}.Visit(ctx, services.APIOptions{Method: http.MethodGet, Endpoint: endpoint}, func(response *http.Response) error {
if response.StatusCode != http.StatusOK {
return nil // Visit reports the status.
}
return json.NewDecoder(response.Body).Decode(out)
})
}
// completeIssues completes issue numbers in state (open, closed or all) with their titles. // completeIssues completes issue numbers in state (open, closed or all) with their titles.
func (a *application) completeIssues(state string) cobra.CompletionFunc { func (a *application) completeIssues(state string) cobra.CompletionFunc {
return a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) { return a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
@@ -390,13 +377,8 @@ func (a *application) completeMilestones() cobra.CompletionFunc {
// completeUsers completes comma-separated usernames that can be assigned in the repository. // completeUsers completes comma-separated usernames that can be assigned in the repository.
func (a *application) completeUsers() cobra.CompletionFunc { func (a *application) completeUsers() cobra.CompletionFunc {
return commaSeparated(a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) { return commaSeparated(a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
type user struct { users, err := services.Repositories{Client: t.client}.Assignees(ctx, t.Repo)
Login string `json:"login"` return describe(users, func(item services.Assignee) (string, string) { return item.Username, item.FullName }), err
FullName string `json:"full_name"`
}
var users []user
err := fetchJSON(ctx, t.client, "repos/"+t.Repo+"/assignees", &users)
return describe(users, func(item user) (string, string) { return item.Login, item.FullName }), err
})) }))
} }
@@ -416,12 +398,8 @@ func (a *application) completeBranches() cobra.CompletionFunc {
// completeTags completes the repository's tags. // completeTags completes the repository's tags.
func (a *application) completeTags() cobra.CompletionFunc { func (a *application) completeTags() cobra.CompletionFunc {
return a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) { return a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
type tag struct { tags, err := services.Repositories{Client: t.client}.Tags(ctx, t.Repo, completionLimit, 1)
Name string `json:"name"` return describe(tags, func(name string) (string, string) { return name, "" }), err
}
var tags []tag
err := fetchJSON(ctx, t.client, "repos/"+t.Repo+"/tags?limit="+strconv.Itoa(completionLimit), &tags)
return describe(tags, func(item tag) (string, string) { return item.Name, "" }), err
}) })
} }
+27 -3
View File
@@ -145,15 +145,39 @@ func (s Repositories) viewPermissions(ctx context.Context, repo string) (Reposit
return RepositoryPermissions{Admin: item.Permissions.Admin, Push: item.Permissions.Push, Pull: item.Permissions.Pull}, nil return RepositoryPermissions{Admin: item.Permissions.Admin, Push: item.Permissions.Push, Pull: item.Permissions.Pull}, nil
} }
// assignees lists usernames that can be assigned on repository issues. // Assignee is a user that can be assigned on repository issues.
func (s Repositories) assignees(ctx context.Context, repo string) ([]string, error) { type Assignee struct {
Username string `json:"username"`
FullName string `json:"full_name"`
}
// Assignees lists the users that can be assigned on repository issues.
func (s Repositories) Assignees(ctx context.Context, repo string) ([]Assignee, error) {
users, err := repoCall(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) ([]*gitea.User, *gitea.Response, error) { users, err := repoCall(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) ([]*gitea.User, *gitea.Response, error) {
return c.Repositories.GetAssignees(ctx, owner, name) return c.Repositories.GetAssignees(ctx, owner, name)
}) })
return convertAll(users, func(user *gitea.User) Assignee { return Assignee{Username: user.UserName, FullName: user.FullName} }), err
}
// assignees lists usernames that can be assigned on repository issues.
func (s Repositories) assignees(ctx context.Context, repo string) ([]string, error) {
users, err := s.Assignees(ctx, repo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return convertAll(users, login), nil names := make([]string, len(users))
for i, user := range users {
names[i] = user.Username
}
return names, nil
}
// Tags lists a page of the repository's tag names.
func (s Repositories) Tags(ctx context.Context, repo string, limit, page int) ([]string, error) {
tags, err := repoCall(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) ([]*gitea.Tag, *gitea.Response, error) {
return c.Repositories.ListRepoTags(ctx, owner, name, gitea.ListRepoTagsOptions{ListOptions: listOptions(limit, page)})
})
return convertAll(tags, func(tag *gitea.Tag) string { return tag.Name }), err
} }
// Create creates a repository for the account or the named organization. // Create creates a repository for the account or the named organization.
+15 -3
View File
@@ -51,10 +51,16 @@ func TestRepositoryForkSendsOrganizationOnlyWhenSelected(t *testing.T) {
} }
} }
func TestRepositoryAssigneesAndMissingPermissions(t *testing.T) { func TestRepositoryAssigneesTagsAndMissingPermissions(t *testing.T) {
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/repos/alice/project/assignees" { switch r.URL.Path {
_, _ = w.Write([]byte(`[{"login":"alice"},{"login":"bob"}]`)) case "/api/v1/repos/alice/project/assignees":
_, _ = w.Write([]byte(`[{"login":"alice","full_name":"Alice A."},{"login":"bob"}]`))
return
case "/api/v1/repos/alice/project/tags":
assert.Equal(t, "2", r.URL.Query().Get("page"))
assert.Equal(t, "5", r.URL.Query().Get("limit"))
_, _ = w.Write([]byte(`[{"name":"v1.0"},{"name":"v0.9"}]`))
return return
} }
_, _ = w.Write([]byte(`{"id":1}`)) _, _ = w.Write([]byte(`{"id":1}`))
@@ -63,6 +69,12 @@ func TestRepositoryAssigneesAndMissingPermissions(t *testing.T) {
names, err := service.assignees(context.Background(), "alice/project") names, err := service.assignees(context.Background(), "alice/project")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"alice", "bob"}, names) require.Equal(t, []string{"alice", "bob"}, names)
users, err := service.Assignees(context.Background(), "alice/project")
require.NoError(t, err)
require.Equal(t, []Assignee{{Username: "alice", FullName: "Alice A."}, {Username: "bob"}}, users)
tags, err := service.Tags(context.Background(), "alice/project", 5, 2)
require.NoError(t, err)
require.Equal(t, []string{"v1.0", "v0.9"}, tags)
permissions, err := service.viewPermissions(context.Background(), "alice/project") permissions, err := service.viewPermissions(context.Background(), "alice/project")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, RepositoryPermissions{}, permissions) require.Equal(t, RepositoryPermissions{}, permissions)