Merge remote-tracking branch 'origin' into pr-lookup-refactor
This commit is contained in:
commit
bc3bb97c43
52 changed files with 1128 additions and 385 deletions
|
|
@ -6,13 +6,14 @@ import (
|
|||
)
|
||||
|
||||
func (issue *Issue) ExportData(fields []string) *map[string]interface{} {
|
||||
v := reflect.ValueOf(issue).Elem()
|
||||
data := map[string]interface{}{}
|
||||
|
||||
for _, f := range fields {
|
||||
switch f {
|
||||
case "milestone":
|
||||
if issue.Milestone.Title != "" {
|
||||
data[f] = &issue.Milestone
|
||||
data[f] = map[string]string{"title": issue.Milestone.Title}
|
||||
} else {
|
||||
data[f] = nil
|
||||
}
|
||||
|
|
@ -25,7 +26,6 @@ func (issue *Issue) ExportData(fields []string) *map[string]interface{} {
|
|||
case "projectCards":
|
||||
data[f] = issue.ProjectCards.Nodes
|
||||
default:
|
||||
v := reflect.ValueOf(issue).Elem()
|
||||
sf := fieldByName(v, f)
|
||||
data[f] = sf.Interface()
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ func (issue *Issue) ExportData(fields []string) *map[string]interface{} {
|
|||
}
|
||||
|
||||
func (pr *PullRequest) ExportData(fields []string) *map[string]interface{} {
|
||||
v := reflect.ValueOf(pr).Elem()
|
||||
data := map[string]interface{}{}
|
||||
|
||||
for _, f := range fields {
|
||||
|
|
@ -43,7 +44,7 @@ func (pr *PullRequest) ExportData(fields []string) *map[string]interface{} {
|
|||
data[f] = map[string]string{"name": pr.HeadRepository.Name}
|
||||
case "milestone":
|
||||
if pr.Milestone.Title != "" {
|
||||
data[f] = &pr.Milestone
|
||||
data[f] = map[string]string{"title": pr.Milestone.Title}
|
||||
} else {
|
||||
data[f] = nil
|
||||
}
|
||||
|
|
@ -75,7 +76,6 @@ func (pr *PullRequest) ExportData(fields []string) *map[string]interface{} {
|
|||
}
|
||||
data[f] = &requests
|
||||
default:
|
||||
v := reflect.ValueOf(pr).Elem()
|
||||
sf := fieldByName(v, f)
|
||||
data[f] = sf.Interface()
|
||||
}
|
||||
|
|
@ -84,22 +84,6 @@ func (pr *PullRequest) ExportData(fields []string) *map[string]interface{} {
|
|||
return &data
|
||||
}
|
||||
|
||||
func ExportIssues(issues []Issue, fields []string) *[]interface{} {
|
||||
data := make([]interface{}, len(issues))
|
||||
for i := range issues {
|
||||
data[i] = issues[i].ExportData(fields)
|
||||
}
|
||||
return &data
|
||||
}
|
||||
|
||||
func ExportPRs(prs []PullRequest, fields []string) *[]interface{} {
|
||||
data := make([]interface{}, len(prs))
|
||||
for i := range prs {
|
||||
data[i] = prs[i].ExportData(fields)
|
||||
}
|
||||
return &data
|
||||
}
|
||||
|
||||
func fieldByName(v reflect.Value, field string) reflect.Value {
|
||||
return v.FieldByNameFunc(func(s string) bool {
|
||||
return strings.EqualFold(field, s)
|
||||
|
|
|
|||
|
|
@ -90,31 +90,6 @@ func TestIssue_ExportData(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExportIssues(t *testing.T) {
|
||||
issues := []Issue{
|
||||
{Milestone: Milestone{Title: "hi"}},
|
||||
{},
|
||||
}
|
||||
exported := ExportIssues(issues, []string{"milestone"})
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetIndent("", "\t")
|
||||
require.NoError(t, enc.Encode(exported))
|
||||
assert.Equal(t, heredoc.Doc(`
|
||||
[
|
||||
{
|
||||
"milestone": {
|
||||
"title": "hi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"milestone": null
|
||||
}
|
||||
]
|
||||
`), buf.String())
|
||||
}
|
||||
|
||||
func TestPullRequest_ExportData(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
53
api/export_repo.go
Normal file
53
api/export_repo.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func (repo *Repository) ExportData(fields []string) *map[string]interface{} {
|
||||
v := reflect.ValueOf(repo).Elem()
|
||||
data := map[string]interface{}{}
|
||||
|
||||
for _, f := range fields {
|
||||
switch f {
|
||||
case "parent":
|
||||
data[f] = miniRepoExport(repo.Parent)
|
||||
case "templateRepository":
|
||||
data[f] = miniRepoExport(repo.TemplateRepository)
|
||||
case "languages":
|
||||
data[f] = repo.Languages.Edges
|
||||
case "labels":
|
||||
data[f] = repo.Labels.Nodes
|
||||
case "assignableUsers":
|
||||
data[f] = repo.AssignableUsers.Nodes
|
||||
case "mentionableUsers":
|
||||
data[f] = repo.MentionableUsers.Nodes
|
||||
case "milestones":
|
||||
data[f] = repo.Milestones.Nodes
|
||||
case "projects":
|
||||
data[f] = repo.Projects.Nodes
|
||||
case "repositoryTopics":
|
||||
var topics []RepositoryTopic
|
||||
for _, n := range repo.RepositoryTopics.Nodes {
|
||||
topics = append(topics, n.Topic)
|
||||
}
|
||||
data[f] = topics
|
||||
default:
|
||||
sf := fieldByName(v, f)
|
||||
data[f] = sf.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return &data
|
||||
}
|
||||
|
||||
func miniRepoExport(r *Repository) map[string]interface{} {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"id": r.ID,
|
||||
"name": r.Name,
|
||||
"owner": r.Owner,
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,10 @@ func (p ProjectCards) ProjectNames() []string {
|
|||
}
|
||||
|
||||
type Milestone struct {
|
||||
Title string `json:"title"`
|
||||
Number int `json:"number"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
DueOn *time.Time `json:"dueOn"`
|
||||
}
|
||||
|
||||
type IssuesDisabledError struct {
|
||||
|
|
@ -241,7 +244,6 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, e
|
|||
id
|
||||
title
|
||||
state
|
||||
closed
|
||||
body
|
||||
author {
|
||||
login
|
||||
|
|
|
|||
|
|
@ -16,25 +16,100 @@ import (
|
|||
|
||||
// Repository contains information about a GitHub repo
|
||||
type Repository struct {
|
||||
ID string
|
||||
Name string
|
||||
Description string
|
||||
URL string
|
||||
CloneURL string
|
||||
CreatedAt time.Time
|
||||
Owner RepositoryOwner
|
||||
ID string
|
||||
Name string
|
||||
NameWithOwner string
|
||||
Owner RepositoryOwner
|
||||
Parent *Repository
|
||||
TemplateRepository *Repository
|
||||
Description string
|
||||
HomepageURL string
|
||||
OpenGraphImageURL string
|
||||
UsesCustomOpenGraphImage bool
|
||||
URL string
|
||||
SSHURL string
|
||||
MirrorURL string
|
||||
SecurityPolicyURL string
|
||||
|
||||
IsPrivate bool
|
||||
HasIssuesEnabled bool
|
||||
HasWikiEnabled bool
|
||||
ViewerPermission string
|
||||
DefaultBranchRef BranchRef
|
||||
CreatedAt time.Time
|
||||
PushedAt *time.Time
|
||||
UpdatedAt time.Time
|
||||
|
||||
Parent *Repository
|
||||
IsBlankIssuesEnabled bool
|
||||
IsSecurityPolicyEnabled bool
|
||||
HasIssuesEnabled bool
|
||||
HasProjectsEnabled bool
|
||||
HasWikiEnabled bool
|
||||
MergeCommitAllowed bool
|
||||
SquashMergeAllowed bool
|
||||
RebaseMergeAllowed bool
|
||||
|
||||
MergeCommitAllowed bool
|
||||
RebaseMergeAllowed bool
|
||||
SquashMergeAllowed bool
|
||||
ForkCount int
|
||||
StargazerCount int
|
||||
Watchers struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
Issues struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
PullRequests struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
|
||||
CodeOfConduct *CodeOfConduct
|
||||
ContactLinks []ContactLink
|
||||
DefaultBranchRef BranchRef
|
||||
DeleteBranchOnMerge bool
|
||||
DiskUsage int
|
||||
FundingLinks []FundingLink
|
||||
IsArchived bool
|
||||
IsEmpty bool
|
||||
IsFork bool
|
||||
IsInOrganization bool
|
||||
IsMirror bool
|
||||
IsPrivate bool
|
||||
IsTemplate bool
|
||||
IsUserConfigurationRepository bool
|
||||
LicenseInfo *RepositoryLicense
|
||||
ViewerCanAdminister bool
|
||||
ViewerDefaultCommitEmail string
|
||||
ViewerDefaultMergeMethod string
|
||||
ViewerHasStarred bool
|
||||
ViewerPermission string
|
||||
ViewerPossibleCommitEmails []string
|
||||
ViewerSubscription string
|
||||
|
||||
RepositoryTopics struct {
|
||||
Nodes []struct {
|
||||
Topic RepositoryTopic
|
||||
}
|
||||
}
|
||||
PrimaryLanguage *CodingLanguage
|
||||
Languages struct {
|
||||
Edges []struct {
|
||||
Size int `json:"size"`
|
||||
Node CodingLanguage `json:"node"`
|
||||
}
|
||||
}
|
||||
IssueTemplates []IssueTemplate
|
||||
PullRequestTemplates []PullRequestTemplate
|
||||
Labels struct {
|
||||
Nodes []IssueLabel
|
||||
}
|
||||
Milestones struct {
|
||||
Nodes []Milestone
|
||||
}
|
||||
LatestRelease *RepositoryRelease
|
||||
|
||||
AssignableUsers struct {
|
||||
Nodes []GitHubUser
|
||||
}
|
||||
MentionableUsers struct {
|
||||
Nodes []GitHubUser
|
||||
}
|
||||
Projects struct {
|
||||
Nodes []RepoProject
|
||||
}
|
||||
|
||||
// pseudo-field that keeps track of host name of this repo
|
||||
hostname string
|
||||
|
|
@ -42,12 +117,76 @@ type Repository struct {
|
|||
|
||||
// RepositoryOwner is the owner of a GitHub repository
|
||||
type RepositoryOwner struct {
|
||||
Login string
|
||||
ID string `json:"id"`
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
type GitHubUser struct {
|
||||
ID string `json:"id"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// BranchRef is the branch name in a GitHub repository
|
||||
type BranchRef struct {
|
||||
Name string
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CodeOfConduct struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type RepositoryLicense struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
type ContactLink struct {
|
||||
About string `json:"about"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type FundingLink struct {
|
||||
Platform string `json:"platform"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type CodingLanguage struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type IssueTemplate struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
About string `json:"about"`
|
||||
}
|
||||
|
||||
type PullRequestTemplate struct {
|
||||
Filename string `json:"filename"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type RepositoryTopic struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type RepositoryRelease struct {
|
||||
Name string `json:"name"`
|
||||
TagName string `json:"tagName"`
|
||||
URL string `json:"url"`
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
}
|
||||
|
||||
type IssueLabel struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// RepoOwner is the login name of the owner
|
||||
|
|
@ -65,11 +204,6 @@ func (r Repository) RepoHost() string {
|
|||
return r.hostname
|
||||
}
|
||||
|
||||
// IsFork is true when this repository has a parent repository
|
||||
func (r Repository) IsFork() bool {
|
||||
return r.Parent != nil
|
||||
}
|
||||
|
||||
// ViewerCanPush is true when the requesting user has push access
|
||||
func (r Repository) ViewerCanPush() bool {
|
||||
switch r.ViewerPermission {
|
||||
|
|
@ -305,16 +439,26 @@ type repositoryV3 struct {
|
|||
NodeID string
|
||||
Name string
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
Owner struct {
|
||||
Login string
|
||||
}
|
||||
}
|
||||
|
||||
// ForkRepo forks the repository on GitHub and returns the new repository
|
||||
func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
|
||||
func ForkRepo(client *Client, repo ghrepo.Interface, org string) (*Repository, error) {
|
||||
path := fmt.Sprintf("repos/%s/forks", ghrepo.FullName(repo))
|
||||
body := bytes.NewBufferString(`{}`)
|
||||
|
||||
params := map[string]interface{}{}
|
||||
if org != "" {
|
||||
params["organization"] = org
|
||||
}
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(body)
|
||||
if err := enc.Encode(params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := repositoryV3{}
|
||||
err := client.REST(repo.RepoHost(), "POST", path, body, &result)
|
||||
if err != nil {
|
||||
|
|
@ -324,7 +468,6 @@ func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
|
|||
return &Repository{
|
||||
ID: result.NodeID,
|
||||
Name: result.Name,
|
||||
CloneURL: result.CloneURL,
|
||||
CreatedAt: result.CreatedAt,
|
||||
Owner: RepositoryOwner{
|
||||
Login: result.Owner.Login,
|
||||
|
|
@ -707,9 +850,10 @@ func RepoResolveMetadataIDs(client *Client, repo ghrepo.Interface, input RepoRes
|
|||
}
|
||||
|
||||
type RepoProject struct {
|
||||
ID string
|
||||
Name string
|
||||
ResourcePath string
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Number int `json:"number"`
|
||||
ResourcePath string `json:"resourcePath"`
|
||||
}
|
||||
|
||||
// RepoProjects fetches all open projects for a repository
|
||||
|
|
|
|||
|
|
@ -144,9 +144,9 @@ func Test_RepoMetadata(t *testing.T) {
|
|||
func Test_ProjectsToPaths(t *testing.T) {
|
||||
expectedProjectPaths := []string{"OWNER/REPO/PROJECT_NUMBER", "ORG/PROJECT_NUMBER"}
|
||||
projects := []RepoProject{
|
||||
{"id1", "My Project", "/OWNER/REPO/projects/PROJECT_NUMBER"},
|
||||
{"id2", "Org Project", "/orgs/ORG/projects/PROJECT_NUMBER"},
|
||||
{"id3", "Project", "/orgs/ORG/projects/PROJECT_NUMBER_2"},
|
||||
{ID: "id1", Name: "My Project", ResourcePath: "/OWNER/REPO/projects/PROJECT_NUMBER"},
|
||||
{ID: "id2", Name: "Org Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER"},
|
||||
{ID: "id3", Name: "Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER_2"},
|
||||
}
|
||||
projectNames := []string{"My Project", "Org Project"}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,3 +186,133 @@ func PullRequestGraphQL(fields []string) string {
|
|||
}
|
||||
return strings.Join(q, ",")
|
||||
}
|
||||
|
||||
var RepositoryFields = []string{
|
||||
"id",
|
||||
"name",
|
||||
"nameWithOwner",
|
||||
"owner",
|
||||
"parent",
|
||||
"templateRepository",
|
||||
"description",
|
||||
"homepageUrl",
|
||||
"openGraphImageUrl",
|
||||
"usesCustomOpenGraphImage",
|
||||
"url",
|
||||
"sshUrl",
|
||||
"mirrorUrl",
|
||||
"securityPolicyUrl",
|
||||
|
||||
"createdAt",
|
||||
"pushedAt",
|
||||
"updatedAt",
|
||||
|
||||
"isBlankIssuesEnabled",
|
||||
"isSecurityPolicyEnabled",
|
||||
"hasIssuesEnabled",
|
||||
"hasProjectsEnabled",
|
||||
"hasWikiEnabled",
|
||||
"mergeCommitAllowed",
|
||||
"squashMergeAllowed",
|
||||
"rebaseMergeAllowed",
|
||||
|
||||
"forkCount",
|
||||
"stargazerCount",
|
||||
"watchers",
|
||||
"issues",
|
||||
"pullRequests",
|
||||
|
||||
"codeOfConduct",
|
||||
"contactLinks",
|
||||
"defaultBranchRef",
|
||||
"deleteBranchOnMerge",
|
||||
"diskUsage",
|
||||
"fundingLinks",
|
||||
"isArchived",
|
||||
"isEmpty",
|
||||
"isFork",
|
||||
"isInOrganization",
|
||||
"isMirror",
|
||||
"isPrivate",
|
||||
"isTemplate",
|
||||
"isUserConfigurationRepository",
|
||||
"licenseInfo",
|
||||
"viewerCanAdminister",
|
||||
"viewerDefaultCommitEmail",
|
||||
"viewerDefaultMergeMethod",
|
||||
"viewerHasStarred",
|
||||
"viewerPermission",
|
||||
"viewerPossibleCommitEmails",
|
||||
"viewerSubscription",
|
||||
|
||||
"repositoryTopics",
|
||||
"primaryLanguage",
|
||||
"languages",
|
||||
"issueTemplates",
|
||||
"pullRequestTemplates",
|
||||
"labels",
|
||||
"milestones",
|
||||
"latestRelease",
|
||||
|
||||
"assignableUsers",
|
||||
"mentionableUsers",
|
||||
"projects",
|
||||
|
||||
// "branchProtectionRules", // too complex to expose
|
||||
// "collaborators", // does it make sense to expose without affiliation filter?
|
||||
}
|
||||
|
||||
func RepositoryGraphQL(fields []string) string {
|
||||
var q []string
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "codeOfConduct":
|
||||
q = append(q, "codeOfConduct{key,name,url}")
|
||||
case "contactLinks":
|
||||
q = append(q, "contactLinks{about,name,url}")
|
||||
case "fundingLinks":
|
||||
q = append(q, "fundingLinks{platform,url}")
|
||||
case "licenseInfo":
|
||||
q = append(q, "licenseInfo{key,name,nickname}")
|
||||
case "owner":
|
||||
q = append(q, "owner{id,login}")
|
||||
case "parent":
|
||||
q = append(q, "parent{id,name,owner{id,login}}")
|
||||
case "templateRepository":
|
||||
q = append(q, "templateRepository{id,name,owner{id,login}}")
|
||||
case "repositoryTopics":
|
||||
q = append(q, "repositoryTopics(first:100){nodes{topic{name}}}")
|
||||
case "issueTemplates":
|
||||
q = append(q, "issueTemplates{name,title,body,about}")
|
||||
case "pullRequestTemplates":
|
||||
q = append(q, "pullRequestTemplates{body,filename}")
|
||||
case "labels":
|
||||
q = append(q, "labels(first:100){nodes{id,color,name,description}}")
|
||||
case "languages":
|
||||
q = append(q, "languages(first:100){edges{size,node{name}}}")
|
||||
case "primaryLanguage":
|
||||
q = append(q, "primaryLanguage{name}")
|
||||
case "latestRelease":
|
||||
q = append(q, "latestRelease{publishedAt,tagName,name,url}")
|
||||
case "milestones":
|
||||
q = append(q, "milestones(first:100,states:OPEN){nodes{number,title,description,dueOn}}")
|
||||
case "assignableUsers":
|
||||
q = append(q, "assignableUsers(first:100){nodes{id,login,name}}")
|
||||
case "mentionableUsers":
|
||||
q = append(q, "mentionableUsers(first:100){nodes{id,login,name}}")
|
||||
case "projects":
|
||||
q = append(q, "projects(first:100,states:OPEN){nodes{id,name,number,body,resourcePath}}")
|
||||
case "watchers":
|
||||
q = append(q, "watchers{totalCount}")
|
||||
case "issues":
|
||||
q = append(q, "issues(states:OPEN){totalCount}")
|
||||
case "pullRequests":
|
||||
q = append(q, "pullRequests(states:OPEN){totalCount}")
|
||||
case "defaultBranchRef":
|
||||
q = append(q, "defaultBranchRef{name}")
|
||||
default:
|
||||
q = append(q, field)
|
||||
}
|
||||
}
|
||||
return strings.Join(q, ",")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue