cli/pkg/cmd/pr/shared/editable.go
2021-02-12 10:08:44 -08:00

428 lines
9.3 KiB
Go

package shared
import (
"fmt"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/pkg/surveyext"
"github.com/shurcooL/githubv4"
)
type Editable struct {
Title string
TitleDefault string
TitleEdited bool
Body string
BodyDefault string
BodyEdited bool
Reviewers []string
ReviewersDefault api.ReviewRequests
ReviewersOptions []string
ReviewersEdited bool
ReviewersAllowed bool
Assignees []string
AssigneesDefault api.Assignees
AssigneesOptions []string
AssigneesEdited bool
Labels []string
LabelsDefault api.Labels
LabelsOptions []string
LabelsEdited bool
Projects []string
ProjectsDefault api.ProjectCards
ProjectsOptions []string
ProjectsEdited bool
Milestone string
MilestoneDefault api.Milestone
MilestoneOptions []string
MilestoneEdited bool
Metadata api.RepoMetadataResult
}
func (e Editable) Dirty() bool {
return e.TitleEdited ||
e.BodyEdited ||
e.ReviewersEdited ||
e.AssigneesEdited ||
e.LabelsEdited ||
e.ProjectsEdited ||
e.MilestoneEdited
}
func (e Editable) TitleParam() *githubv4.String {
if !e.TitleEdited {
return nil
}
s := githubv4.String(e.Title)
return &s
}
func (e Editable) BodyParam() *githubv4.String {
if !e.BodyEdited {
return nil
}
s := githubv4.String(e.Body)
return &s
}
func (e Editable) ReviewersParams() (*[]githubv4.ID, *[]githubv4.ID, error) {
if !e.ReviewersEdited {
return nil, nil, nil
}
var userReviewers []string
var teamReviewers []string
for _, r := range e.Reviewers {
if strings.ContainsRune(r, '/') {
teamReviewers = append(teamReviewers, r)
} else {
userReviewers = append(userReviewers, r)
}
}
userIds, err := toParams(userReviewers, e.Metadata.MembersToIDs)
if err != nil {
return nil, nil, err
}
teamIds, err := toParams(teamReviewers, e.Metadata.TeamsToIDs)
if err != nil {
return nil, nil, err
}
return userIds, teamIds, nil
}
func (e Editable) AssigneesParam(client *api.Client, repo ghrepo.Interface) (*[]githubv4.ID, error) {
if !e.AssigneesEdited {
return nil, nil
}
meReplacer := NewMeReplacer(client, repo.RepoHost())
assignees, err := meReplacer.ReplaceSlice(e.Assignees)
if err != nil {
return nil, err
}
return toParams(assignees, e.Metadata.MembersToIDs)
}
func (e Editable) LabelsParam() (*[]githubv4.ID, error) {
if !e.LabelsEdited {
return nil, nil
}
return toParams(e.Labels, e.Metadata.LabelsToIDs)
}
func (e Editable) ProjectsParam() (*[]githubv4.ID, error) {
if !e.ProjectsEdited {
return nil, nil
}
return toParams(e.Projects, e.Metadata.ProjectsToIDs)
}
func (e Editable) MilestoneParam() (*githubv4.ID, error) {
if !e.MilestoneEdited {
return nil, nil
}
if e.Milestone == noMilestone || e.Milestone == "" {
return githubv4.NewID(nil), nil
}
return toParam(e.Milestone, e.Metadata.MilestoneToID)
}
func EditFieldsSurvey(editable *Editable, editorCommand string) error {
var err error
if editable.TitleEdited {
editable.Title, err = titleSurvey(editable.TitleDefault)
if err != nil {
return err
}
}
if editable.BodyEdited {
editable.Body, err = bodySurvey(editable.BodyDefault, editorCommand)
if err != nil {
return err
}
}
if editable.ReviewersEdited {
editable.Reviewers, err = reviewersSurvey(editable.ReviewersDefault, editable.ReviewersOptions)
if err != nil {
return err
}
}
if editable.AssigneesEdited {
editable.Assignees, err = assigneesSurvey(editable.AssigneesDefault, editable.AssigneesOptions)
if err != nil {
return err
}
}
if editable.LabelsEdited {
editable.Labels, err = labelsSurvey(editable.LabelsDefault, editable.LabelsOptions)
if err != nil {
return err
}
}
if editable.ProjectsEdited {
editable.Projects, err = projectsSurvey(editable.ProjectsDefault, editable.ProjectsOptions)
if err != nil {
return err
}
}
if editable.MilestoneEdited {
editable.Milestone, err = milestoneSurvey(editable.MilestoneDefault, editable.MilestoneOptions)
if err != nil {
return err
}
}
confirm, err := confirmSurvey()
if err != nil {
return err
}
if !confirm {
return fmt.Errorf("Discarding...")
}
return nil
}
func FieldsToEditSurvey(editable *Editable) error {
contains := func(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}
results := []string{}
opts := []string{"Title", "Body"}
if editable.ReviewersAllowed {
opts = append(opts, "Reviewers")
}
opts = append(opts, "Assignees", "Labels", "Projects", "Milestone")
q := &survey.MultiSelect{
Message: "What would you like to edit?",
Options: opts,
}
err := survey.AskOne(q, &results)
if err != nil {
return err
}
if contains(results, "Title") {
editable.TitleEdited = true
}
if contains(results, "Body") {
editable.BodyEdited = true
}
if contains(results, "Reviewers") {
editable.ReviewersEdited = true
}
if contains(results, "Assignees") {
editable.AssigneesEdited = true
}
if contains(results, "Labels") {
editable.LabelsEdited = true
}
if contains(results, "Projects") {
editable.ProjectsEdited = true
}
if contains(results, "Milestone") {
editable.MilestoneEdited = true
}
return nil
}
func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable) error {
input := api.RepoMetadataInput{
Reviewers: editable.ReviewersEdited,
Assignees: editable.AssigneesEdited,
Labels: editable.LabelsEdited,
Projects: editable.ProjectsEdited,
Milestones: editable.MilestoneEdited,
}
metadata, err := api.RepoMetadata(client, repo, input)
if err != nil {
return err
}
var users []string
for _, u := range metadata.AssignableUsers {
users = append(users, u.Login)
}
var teams []string
for _, t := range metadata.Teams {
teams = append(teams, fmt.Sprintf("%s/%s", repo.RepoOwner(), t.Slug))
}
var labels []string
for _, l := range metadata.Labels {
labels = append(labels, l.Name)
}
var projects []string
for _, l := range metadata.Projects {
projects = append(projects, l.Name)
}
milestones := []string{noMilestone}
for _, m := range metadata.Milestones {
milestones = append(milestones, m.Title)
}
editable.Metadata = *metadata
editable.ReviewersOptions = append(users, teams...)
editable.AssigneesOptions = users
editable.LabelsOptions = labels
editable.ProjectsOptions = projects
editable.MilestoneOptions = milestones
return nil
}
func titleSurvey(title string) (string, error) {
var result string
q := &survey.Input{
Message: "Title",
Default: title,
}
err := survey.AskOne(q, &result)
return result, err
}
func bodySurvey(body, editorCommand string) (string, error) {
var result string
q := &surveyext.GhEditor{
BlankAllowed: true,
EditorCommand: editorCommand,
Editor: &survey.Editor{Message: "Body",
FileName: "*.md",
Default: body,
HideDefault: true,
AppendDefault: true,
},
}
err := survey.AskOne(q, &result)
return result, err
}
func reviewersSurvey(reviewers api.ReviewRequests, opts []string) ([]string, error) {
if len(opts) == 0 {
return nil, nil
}
logins := []string{}
for _, a := range reviewers.Nodes {
logins = append(logins, a.RequestedReviewer.Login)
}
var results []string
q := &survey.MultiSelect{
Message: "Reviewers",
Options: opts,
Default: logins,
}
err := survey.AskOne(q, &results)
return results, err
}
func assigneesSurvey(assignees api.Assignees, opts []string) ([]string, error) {
if len(opts) == 0 {
return nil, nil
}
logins := []string{}
for _, a := range assignees.Nodes {
logins = append(logins, a.Login)
}
var results []string
q := &survey.MultiSelect{
Message: "Assignees",
Options: opts,
Default: logins,
}
err := survey.AskOne(q, &results)
return results, err
}
func labelsSurvey(labels api.Labels, opts []string) ([]string, error) {
if len(opts) == 0 {
return nil, nil
}
names := []string{}
for _, l := range labels.Nodes {
names = append(names, l.Name)
}
var results []string
q := &survey.MultiSelect{
Message: "Labels",
Options: opts,
Default: names,
}
err := survey.AskOne(q, &results)
return results, err
}
func projectsSurvey(projectCards api.ProjectCards, opts []string) ([]string, error) {
if len(opts) == 0 {
return nil, nil
}
names := []string{}
for _, c := range projectCards.Nodes {
names = append(names, c.Project.Name)
}
var results []string
q := &survey.MultiSelect{
Message: "Projects",
Options: opts,
Default: names,
}
err := survey.AskOne(q, &results)
return results, err
}
func milestoneSurvey(milestone api.Milestone, opts []string) (string, error) {
if len(opts) == 0 {
return "", nil
}
var result string
q := &survey.Select{
Message: "Milestone",
Options: opts,
Default: milestone.Title,
}
err := survey.AskOne(q, &result)
return result, err
}
func confirmSurvey() (bool, error) {
var result bool
q := &survey.Confirm{
Message: "Submit?",
Default: true,
}
err := survey.AskOne(q, &result)
return result, err
}
func toParams(s []string, mapper func([]string) ([]string, error)) (*[]githubv4.ID, error) {
ids, err := mapper(s)
if err != nil {
return nil, err
}
gIds := make([]githubv4.ID, len(ids))
for i, v := range ids {
gIds[i] = v
}
return &gIds, nil
}
func toParam(s string, mapper func(string) (string, error)) (*githubv4.ID, error) {
id, err := mapper(s)
if err != nil {
return nil, err
}
gId := githubv4.ID(id)
return &gId, nil
}