...

Source file src/edge-infra.dev/pkg/f8n/devinfra/jack/plugin/project/issues.go

Documentation: edge-infra.dev/pkg/f8n/devinfra/jack/plugin/project

     1  package project
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"edge-infra.dev/pkg/lib/logging"
     8  
     9  	"github.com/google/go-github/v47/github"
    10  	"github.com/shurcooL/githubv4"
    11  
    12  	"edge-infra.dev/pkg/f8n/devinfra/jack/constants"
    13  	gs "edge-infra.dev/pkg/f8n/devinfra/jack/guest_services"
    14  	"edge-infra.dev/pkg/f8n/devinfra/jack/plugin"
    15  )
    16  
    17  func addIssueToProject(hp plugin.HandlerParams, event github.IssuesEvent, id int64) error {
    18  	log := hp.Log
    19  	ctx := hp.Ctx
    20  	client := hp.Client
    21  	clientv4 := hp.ClientV4
    22  
    23  	log.Info("Adding a new issue to the default project")
    24  
    25  	labels := gs.NewLabels(event.Issue.Labels)
    26  
    27  	// if the issue is labeled with the incident label dont add to the project
    28  	if labels.Kind == constants.KindIncident {
    29  		return nil
    30  	}
    31  
    32  	// Get all the projects based on the org
    33  	org := event.GetRepo().GetOwner().GetLogin()
    34  	issueID := event.GetIssue().GetNodeID()
    35  	orgs, _, err := client.Organizations().ListProjects(ctx, org, &github.ProjectListOptions{})
    36  	if err != nil {
    37  		log.Error(err, "Failed to get orgs")
    38  		return err
    39  	}
    40  
    41  	// Find the project number that matches the one defined in the config
    42  	project := github.Project{}
    43  	for _, x := range orgs {
    44  		if int64(x.GetNumber()) == id {
    45  			project = *x
    46  		}
    47  	}
    48  
    49  	// if the project wasnt found in the list try the v4 api before returning
    50  	// the nodeid of the new card isnt really relevant but it may come in handy later
    51  	if (github.Project{}) == project {
    52  		log.Info("cant find project associated with the given number")
    53  		log.Info("trying the v4 api")
    54  
    55  		betaProjID, err := attemptV4Connection(ctx, clientv4, org, id, log, issueID)
    56  		if err != nil {
    57  			log.Error(err, "v4 connection failed")
    58  			return err
    59  		}
    60  		log.Info("added via v4 api -- card nodeid " + betaProjID)
    61  		return nil
    62  	}
    63  
    64  	// Get the project
    65  	projects, _, err := client.Projects().GetProject(ctx, project.GetID())
    66  	if err != nil {
    67  		log.Error(err, "Failed to get the orgs projects")
    68  		return err
    69  	}
    70  
    71  	// Get the columns of the project
    72  	columns, _, err := client.Projects().ListProjectColumns(ctx, projects.GetID(), &github.ListOptions{})
    73  	if err != nil {
    74  		log.Error(err, "Failed to get the projects columns")
    75  		return err
    76  	}
    77  
    78  	// check that the project has columns
    79  	if len(columns) <= 0 {
    80  		log.Info("not enough columns on the given project")
    81  		return nil
    82  	}
    83  
    84  	// Get the first project column ID, generally the backlog is in the first column
    85  	// Add a new card to the first column
    86  	projectColumnID := columns[0].GetID()
    87  	githubProjectCard := &github.ProjectCardOptions{ContentID: event.GetIssue().GetID(), ContentType: "Issue"}
    88  	_, _, err = client.Projects().CreateProjectCard(ctx, projectColumnID, githubProjectCard)
    89  	if err != nil {
    90  		log.Error(err, fmt.Sprintf("Failed to create a new project card for project %s", projects.GetName()))
    91  		return err
    92  	}
    93  
    94  	log.Info(fmt.Sprintf("issue %v was added to the %v project", event.GetIssue().GetNumber(), projects.GetName()))
    95  
    96  	return nil
    97  }
    98  
    99  // attemptV4Connection tries grabbing the nodeid of a given project
   100  // based off the nodeid we can assume if it is or isnt a beta project
   101  func attemptV4Connection(ctx context.Context, c plugin.GithubV4ClientInterface, org string, id int64, log logging.EdgeLogger, inid string) (string, error) {
   102  	pnid, err := fetchProjectNodeID(ctx, c, org, id)
   103  	if err != nil {
   104  		log.Error(err, "Failed to get orgs")
   105  		return "", err
   106  	}
   107  	return addIssueToV4Project(ctx, c, pnid, inid) // attempt to add the issue to a beta project
   108  }
   109  
   110  // fetchProjectNodeID attempt to grab the nodeid of a given project id
   111  // more or less airlifted from cmd/ghpm/main.go
   112  func fetchProjectNodeID(ctx context.Context, c plugin.GithubV4ClientInterface, org string, id int64) (string, error) {
   113  	var q struct {
   114  		Organization struct {
   115  			Project struct {
   116  				ID string
   117  			} `graphql:"projectNext(number: $id)"`
   118  		} `graphql:"organization(login: $org)"`
   119  	}
   120  
   121  	variables := map[string]interface{}{
   122  		"org": githubv4.String(org),
   123  		"id":  githubv4.Int(id), /* #nosec G115 */
   124  	}
   125  
   126  	err := c.Query(ctx, &q, variables)
   127  	return q.Organization.Project.ID, err
   128  }
   129  
   130  // addIssueToV4Project tries to add an issue to a beta project
   131  // returns the new cards nodeid
   132  func addIssueToV4Project(ctx context.Context, c plugin.GithubV4ClientInterface, pnid string, inid string) (string, error) {
   133  	var m struct {
   134  		AddProjectV2ItemByID struct {
   135  			Item struct {
   136  				ID string
   137  			}
   138  		} `graphql:"AddProjectV2ItemById(input: $input)"`
   139  	}
   140  
   141  	input := githubv4.AddProjectV2ItemByIdInput{
   142  		ProjectID: githubv4.ID(pnid), // project nodeid
   143  		ContentID: githubv4.ID(inid), // issue nodeid
   144  	}
   145  
   146  	err := c.Mutate(ctx, &m, input, nil)
   147  	if err != nil {
   148  		return "", err
   149  	}
   150  	return m.AddProjectV2ItemByID.Item.ID, nil
   151  }
   152  

View as plain text