refactor(issue edit): wire up search-based assignee selection
Add AssigneeSearchFunc to gh issue edit interactive flow, matching the pattern already used in gh pr edit. This eliminates the bulk RepositoryAssignableActors fetch for interactive assignee selection, using dynamic SuggestedAssignableActors search instead. Also clean up pr edit assigneeSearchFunc signature to remove the unused editable parameter (no longer needed after removing the actor accumulation hack). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
e24f55d5a4
commit
947f8fb1b7
4 changed files with 53 additions and 32 deletions
|
|
@ -12,6 +12,7 @@ import (
|
|||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/internal/text"
|
||||
shared "github.com/cli/cli/v2/pkg/cmd/issue/shared"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
|
|
@ -248,6 +249,13 @@ func editRun(opts *EditOptions) error {
|
|||
|
||||
// Fetch editable shared fields once for all issues.
|
||||
apiClient := api.NewClientFromHTTP(httpClient)
|
||||
|
||||
// Wire up search function for assignees when ActorIsAssignable is available.
|
||||
// Interactive mode only supports a single issue, so we use its ID for the search query.
|
||||
if issueFeatures.ActorIsAssignable && opts.Interactive && len(issues) == 1 {
|
||||
editable.AssigneeSearchFunc = assigneeSearchFunc(apiClient, baseRepo, issues[0].ID)
|
||||
}
|
||||
|
||||
opts.IO.StartProgressIndicatorWithLabel("Fetching repository information")
|
||||
err = opts.FetchOptions(apiClient, baseRepo, &editable, opts.Detector.ProjectsV1())
|
||||
opts.IO.StopProgressIndicator()
|
||||
|
|
@ -351,3 +359,36 @@ func editRun(opts *EditOptions) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func assigneeSearchFunc(apiClient *api.Client, repo ghrepo.Interface, assignableID string) func(string) prompter.MultiSelectSearchResult {
|
||||
return func(input string) prompter.MultiSelectSearchResult {
|
||||
actors, availableAssigneesCount, err := api.SuggestedAssignableActors(
|
||||
apiClient,
|
||||
repo,
|
||||
assignableID,
|
||||
input)
|
||||
if err != nil {
|
||||
return prompter.MultiSelectSearchResult{Err: err}
|
||||
}
|
||||
|
||||
logins := make([]string, 0, len(actors))
|
||||
displayNames := make([]string, 0, len(actors))
|
||||
|
||||
for _, a := range actors {
|
||||
if a.Login() == "" {
|
||||
continue
|
||||
}
|
||||
logins = append(logins, a.Login())
|
||||
if a.DisplayName() != "" {
|
||||
displayNames = append(displayNames, a.DisplayName())
|
||||
} else {
|
||||
displayNames = append(displayNames, a.Login())
|
||||
}
|
||||
}
|
||||
return prompter.MultiSelectSearchResult{
|
||||
Keys: logins,
|
||||
Labels: displayNames,
|
||||
MoreResults: availableAssigneesCount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -607,17 +607,6 @@ func Test_editRun(t *testing.T) {
|
|||
mockIssueGet(t, reg)
|
||||
mockIssueProjectItemsGet(t, reg)
|
||||
mockRepoMetadata(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query RepositoryAssignableActors\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "suggestedActors": {
|
||||
"nodes": [
|
||||
{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
|
||||
{ "login": "monalisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
|
||||
],
|
||||
"pageInfo": { "hasNextPage": false }
|
||||
} } } }
|
||||
`))
|
||||
mockIssueUpdate(t, reg)
|
||||
mockIssueUpdateActorAssignees(t, reg)
|
||||
mockIssueUpdateLabels(t, reg)
|
||||
|
|
@ -649,17 +638,6 @@ func Test_editRun(t *testing.T) {
|
|||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIsssueNumberGetWithAssignedActors(t, reg, 123)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query RepositoryAssignableActors\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "suggestedActors": {
|
||||
"nodes": [
|
||||
{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
|
||||
{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
|
||||
],
|
||||
"pageInfo": { "hasNextPage": false }
|
||||
} } } }
|
||||
`))
|
||||
mockIssueUpdate(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ func editRun(opts *EditOptions) error {
|
|||
// to legacy reviewer/assignee fetching.
|
||||
// TODO actorIsAssignableCleanup
|
||||
if issueFeatures.ActorIsAssignable {
|
||||
editable.AssigneeSearchFunc = assigneeSearchFunc(apiClient, repo, &editable, pr.ID)
|
||||
editable.AssigneeSearchFunc = assigneeSearchFunc(apiClient, repo, pr.ID)
|
||||
editable.ReviewerSearchFunc = reviewerSearchFunc(apiClient, repo, &editable, pr.ID)
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ func editRun(opts *EditOptions) error {
|
|||
|
||||
// assigneeSearchFunc is intended to be an arg for MultiSelectWithSearch
|
||||
// to return potential assignee actors.
|
||||
func assigneeSearchFunc(apiClient *api.Client, repo ghrepo.Interface, editable *shared.Editable, assignableID string) func(string) prompter.MultiSelectSearchResult {
|
||||
func assigneeSearchFunc(apiClient *api.Client, repo ghrepo.Interface, assignableID string) func(string) prompter.MultiSelectSearchResult {
|
||||
searchFunc := func(input string) prompter.MultiSelectSearchResult {
|
||||
actors, availableAssigneesCount, err := api.SuggestedAssignableActors(
|
||||
apiClient,
|
||||
|
|
|
|||
|
|
@ -267,14 +267,16 @@ func (e Editable) MilestoneId() (*string, error) {
|
|||
// go routines. Fields that would be mutated will be copied.
|
||||
func (e *Editable) Clone() Editable {
|
||||
return Editable{
|
||||
Title: e.Title.clone(),
|
||||
Body: e.Body.clone(),
|
||||
Base: e.Base.clone(),
|
||||
Reviewers: e.Reviewers.clone(),
|
||||
Assignees: e.Assignees.clone(),
|
||||
Labels: e.Labels.clone(),
|
||||
Projects: e.Projects.clone(),
|
||||
Milestone: e.Milestone.clone(),
|
||||
Title: e.Title.clone(),
|
||||
Body: e.Body.clone(),
|
||||
Base: e.Base.clone(),
|
||||
Reviewers: e.Reviewers.clone(),
|
||||
ReviewerSearchFunc: e.ReviewerSearchFunc,
|
||||
Assignees: e.Assignees.clone(),
|
||||
AssigneeSearchFunc: e.AssigneeSearchFunc,
|
||||
Labels: e.Labels.clone(),
|
||||
Projects: e.Projects.clone(),
|
||||
Milestone: e.Milestone.clone(),
|
||||
// Shallow copy since no mutation.
|
||||
Metadata: e.Metadata,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue