github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/api/queries_issue.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
     8  )
     9  
    10  type IssuesPayload struct {
    11  	Assigned  IssuesAndTotalCount
    12  	Mentioned IssuesAndTotalCount
    13  	Authored  IssuesAndTotalCount
    14  }
    15  
    16  type IssuesAndTotalCount struct {
    17  	Issues       []Issue
    18  	TotalCount   int
    19  	SearchCapped bool
    20  }
    21  
    22  type Issue struct {
    23  	Typename       string `json:"__typename"`
    24  	ID             string
    25  	Number         int
    26  	Title          string
    27  	URL            string
    28  	State          string
    29  	StateReason    string
    30  	Closed         bool
    31  	Body           string
    32  	CreatedAt      time.Time
    33  	UpdatedAt      time.Time
    34  	ClosedAt       *time.Time
    35  	Comments       Comments
    36  	Author         Author
    37  	Assignees      Assignees
    38  	Labels         Labels
    39  	ProjectCards   ProjectCards
    40  	Milestone      *Milestone
    41  	ReactionGroups ReactionGroups
    42  	IsPinned       bool
    43  }
    44  
    45  func (i Issue) IsPullRequest() bool {
    46  	return i.Typename == "PullRequest"
    47  }
    48  
    49  type Assignees struct {
    50  	Nodes      []GitHubUser
    51  	TotalCount int
    52  }
    53  
    54  func (a Assignees) Logins() []string {
    55  	logins := make([]string, len(a.Nodes))
    56  	for i, a := range a.Nodes {
    57  		logins[i] = a.Login
    58  	}
    59  	return logins
    60  }
    61  
    62  type Labels struct {
    63  	Nodes      []IssueLabel
    64  	TotalCount int
    65  }
    66  
    67  func (l Labels) Names() []string {
    68  	names := make([]string, len(l.Nodes))
    69  	for i, l := range l.Nodes {
    70  		names[i] = l.Name
    71  	}
    72  	return names
    73  }
    74  
    75  type ProjectCards struct {
    76  	Nodes      []*ProjectInfo
    77  	TotalCount int
    78  }
    79  
    80  type ProjectInfo struct {
    81  	Project struct {
    82  		Name string `json:"name"`
    83  	} `json:"project"`
    84  	Column struct {
    85  		Name string `json:"name"`
    86  	} `json:"column"`
    87  }
    88  
    89  func (p ProjectCards) ProjectNames() []string {
    90  	names := make([]string, len(p.Nodes))
    91  	for i, c := range p.Nodes {
    92  		names[i] = c.Project.Name
    93  	}
    94  	return names
    95  }
    96  
    97  type Milestone struct {
    98  	Number      int        `json:"number"`
    99  	Title       string     `json:"title"`
   100  	Description string     `json:"description"`
   101  	DueOn       *time.Time `json:"dueOn"`
   102  }
   103  
   104  type IssuesDisabledError struct {
   105  	error
   106  }
   107  
   108  type Owner struct {
   109  	ID    string `json:"id,omitempty"`
   110  	Name  string `json:"name,omitempty"`
   111  	Login string `json:"login"`
   112  }
   113  
   114  type Author struct {
   115  	// adding these breaks generated GraphQL requests
   116  	//ID    string `json:"id,omitempty"`
   117  	//Name  string `json:"name,omitempty"`
   118  	Login string `json:"login"`
   119  }
   120  
   121  // IssueCreate creates an issue in a GitHub repository
   122  func IssueCreate(client *Client, repo *Repository, params map[string]interface{}) (*Issue, error) {
   123  	query := `
   124  	mutation IssueCreate($input: CreateIssueInput!) {
   125  		createIssue(input: $input) {
   126  			issue {
   127  				url
   128  			}
   129  		}
   130  	}`
   131  
   132  	inputParams := map[string]interface{}{
   133  		"repositoryId": repo.ID,
   134  	}
   135  	for key, val := range params {
   136  		inputParams[key] = val
   137  	}
   138  	variables := map[string]interface{}{
   139  		"input": inputParams,
   140  	}
   141  
   142  	result := struct {
   143  		CreateIssue struct {
   144  			Issue Issue
   145  		}
   146  	}{}
   147  
   148  	err := client.GraphQL(repo.RepoHost(), query, variables, &result)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	return &result.CreateIssue.Issue, nil
   154  }
   155  
   156  type IssueStatusOptions struct {
   157  	Username string
   158  	Fields   []string
   159  }
   160  
   161  func IssueStatus(client *Client, repo ghrepo.Interface, options IssueStatusOptions) (*IssuesPayload, error) {
   162  	type response struct {
   163  		Repository struct {
   164  			Assigned struct {
   165  				TotalCount int
   166  				Nodes      []Issue
   167  			}
   168  			Mentioned struct {
   169  				TotalCount int
   170  				Nodes      []Issue
   171  			}
   172  			Authored struct {
   173  				TotalCount int
   174  				Nodes      []Issue
   175  			}
   176  			HasIssuesEnabled bool
   177  		}
   178  	}
   179  
   180  	fragments := fmt.Sprintf("fragment issue on Issue{%s}", IssueGraphQL(options.Fields))
   181  	query := fragments + `
   182  	query IssueStatus($owner: String!, $repo: String!, $viewer: String!, $per_page: Int = 10) {
   183  		repository(owner: $owner, name: $repo) {
   184  			hasIssuesEnabled
   185  			assigned: issues(filterBy: {assignee: $viewer, states: OPEN}, first: $per_page, orderBy: {field: UPDATED_AT, direction: DESC}) {
   186  				totalCount
   187  				nodes {
   188  					...issue
   189  				}
   190  			}
   191  			mentioned: issues(filterBy: {mentioned: $viewer, states: OPEN}, first: $per_page, orderBy: {field: UPDATED_AT, direction: DESC}) {
   192  				totalCount
   193  				nodes {
   194  					...issue
   195  				}
   196  			}
   197  			authored: issues(filterBy: {createdBy: $viewer, states: OPEN}, first: $per_page, orderBy: {field: UPDATED_AT, direction: DESC}) {
   198  				totalCount
   199  				nodes {
   200  					...issue
   201  				}
   202  			}
   203  		}
   204      }`
   205  
   206  	variables := map[string]interface{}{
   207  		"owner":  repo.RepoOwner(),
   208  		"repo":   repo.RepoName(),
   209  		"viewer": options.Username,
   210  	}
   211  
   212  	var resp response
   213  	err := client.GraphQL(repo.RepoHost(), query, variables, &resp)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	if !resp.Repository.HasIssuesEnabled {
   219  		return nil, fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(repo))
   220  	}
   221  
   222  	payload := IssuesPayload{
   223  		Assigned: IssuesAndTotalCount{
   224  			Issues:     resp.Repository.Assigned.Nodes,
   225  			TotalCount: resp.Repository.Assigned.TotalCount,
   226  		},
   227  		Mentioned: IssuesAndTotalCount{
   228  			Issues:     resp.Repository.Mentioned.Nodes,
   229  			TotalCount: resp.Repository.Mentioned.TotalCount,
   230  		},
   231  		Authored: IssuesAndTotalCount{
   232  			Issues:     resp.Repository.Authored.Nodes,
   233  			TotalCount: resp.Repository.Authored.TotalCount,
   234  		},
   235  	}
   236  
   237  	return &payload, nil
   238  }
   239  
   240  func (i Issue) Link() string {
   241  	return i.URL
   242  }
   243  
   244  func (i Issue) Identifier() string {
   245  	return i.ID
   246  }
   247  
   248  func (i Issue) CurrentUserComments() []Comment {
   249  	return i.Comments.CurrentUserComments()
   250  }