github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/pr/shared/editable_http.go (about) 1 package shared 2 3 import ( 4 "net/http" 5 6 "github.com/ungtb10d/cli/v2/api" 7 "github.com/ungtb10d/cli/v2/internal/ghrepo" 8 "github.com/shurcooL/githubv4" 9 "golang.org/x/sync/errgroup" 10 ) 11 12 func UpdateIssue(httpClient *http.Client, repo ghrepo.Interface, id string, isPR bool, options Editable) error { 13 var wg errgroup.Group 14 15 // Labels are updated through discrete mutations to avoid having to replace the entire list of labels 16 // and risking race conditions. 17 if options.Labels.Edited { 18 if len(options.Labels.Add) > 0 { 19 wg.Go(func() error { 20 addedLabelIds, err := options.Metadata.LabelsToIDs(options.Labels.Add) 21 if err != nil { 22 return err 23 } 24 return addLabels(httpClient, id, repo, addedLabelIds) 25 }) 26 } 27 if len(options.Labels.Remove) > 0 { 28 wg.Go(func() error { 29 removeLabelIds, err := options.Metadata.LabelsToIDs(options.Labels.Remove) 30 if err != nil { 31 return err 32 } 33 return removeLabels(httpClient, id, repo, removeLabelIds) 34 }) 35 } 36 } 37 38 if dirtyExcludingLabels(options) { 39 wg.Go(func() error { 40 return replaceIssueFields(httpClient, repo, id, isPR, options) 41 }) 42 } 43 44 return wg.Wait() 45 } 46 47 func replaceIssueFields(httpClient *http.Client, repo ghrepo.Interface, id string, isPR bool, options Editable) error { 48 apiClient := api.NewClientFromHTTP(httpClient) 49 assigneeIds, err := options.AssigneeIds(apiClient, repo) 50 if err != nil { 51 return err 52 } 53 54 projectIds, err := options.ProjectIds() 55 if err != nil { 56 return err 57 } 58 59 milestoneId, err := options.MilestoneId() 60 if err != nil { 61 return err 62 } 63 64 if isPR { 65 params := githubv4.UpdatePullRequestInput{ 66 PullRequestID: id, 67 Title: ghString(options.TitleValue()), 68 Body: ghString(options.BodyValue()), 69 AssigneeIDs: ghIds(assigneeIds), 70 ProjectIDs: ghIds(projectIds), 71 MilestoneID: ghId(milestoneId), 72 } 73 if options.Base.Edited { 74 params.BaseRefName = ghString(&options.Base.Value) 75 } 76 return updatePullRequest(httpClient, repo, params) 77 } 78 79 params := githubv4.UpdateIssueInput{ 80 ID: id, 81 Title: ghString(options.TitleValue()), 82 Body: ghString(options.BodyValue()), 83 AssigneeIDs: ghIds(assigneeIds), 84 ProjectIDs: ghIds(projectIds), 85 MilestoneID: ghId(milestoneId), 86 } 87 return updateIssue(httpClient, repo, params) 88 } 89 90 func dirtyExcludingLabels(e Editable) bool { 91 return e.Title.Edited || 92 e.Body.Edited || 93 e.Base.Edited || 94 e.Reviewers.Edited || 95 e.Assignees.Edited || 96 e.Projects.Edited || 97 e.Milestone.Edited 98 } 99 100 func addLabels(httpClient *http.Client, id string, repo ghrepo.Interface, labels []string) error { 101 params := githubv4.AddLabelsToLabelableInput{ 102 LabelableID: id, 103 LabelIDs: *ghIds(&labels), 104 } 105 106 var mutation struct { 107 AddLabelsToLabelable struct { 108 Typename string `graphql:"__typename"` 109 } `graphql:"addLabelsToLabelable(input: $input)"` 110 } 111 112 variables := map[string]interface{}{"input": params} 113 gql := api.NewClientFromHTTP(httpClient) 114 return gql.Mutate(repo.RepoHost(), "LabelAdd", &mutation, variables) 115 } 116 117 func removeLabels(httpClient *http.Client, id string, repo ghrepo.Interface, labels []string) error { 118 params := githubv4.RemoveLabelsFromLabelableInput{ 119 LabelableID: id, 120 LabelIDs: *ghIds(&labels), 121 } 122 123 var mutation struct { 124 RemoveLabelsFromLabelable struct { 125 Typename string `graphql:"__typename"` 126 } `graphql:"removeLabelsFromLabelable(input: $input)"` 127 } 128 129 variables := map[string]interface{}{"input": params} 130 gql := api.NewClientFromHTTP(httpClient) 131 return gql.Mutate(repo.RepoHost(), "LabelRemove", &mutation, variables) 132 } 133 134 func updateIssue(httpClient *http.Client, repo ghrepo.Interface, params githubv4.UpdateIssueInput) error { 135 var mutation struct { 136 UpdateIssue struct { 137 Typename string `graphql:"__typename"` 138 } `graphql:"updateIssue(input: $input)"` 139 } 140 variables := map[string]interface{}{"input": params} 141 gql := api.NewClientFromHTTP(httpClient) 142 return gql.Mutate(repo.RepoHost(), "IssueUpdate", &mutation, variables) 143 } 144 145 func updatePullRequest(httpClient *http.Client, repo ghrepo.Interface, params githubv4.UpdatePullRequestInput) error { 146 var mutation struct { 147 UpdatePullRequest struct { 148 Typename string `graphql:"__typename"` 149 } `graphql:"updatePullRequest(input: $input)"` 150 } 151 variables := map[string]interface{}{"input": params} 152 gql := api.NewClientFromHTTP(httpClient) 153 err := gql.Mutate(repo.RepoHost(), "PullRequestUpdate", &mutation, variables) 154 return err 155 } 156 157 func ghIds(s *[]string) *[]githubv4.ID { 158 if s == nil { 159 return nil 160 } 161 ids := make([]githubv4.ID, len(*s)) 162 for i, v := range *s { 163 ids[i] = v 164 } 165 return &ids 166 } 167 168 func ghId(s *string) *githubv4.ID { 169 if s == nil { 170 return nil 171 } 172 if *s == "" { 173 r := githubv4.ID(nil) 174 return &r 175 } 176 r := githubv4.ID(*s) 177 return &r 178 } 179 180 func ghString(s *string) *githubv4.String { 181 if s == nil { 182 return nil 183 } 184 r := githubv4.String(*s) 185 return &r 186 }