| Index: tools/bug_chomper/src/issue_tracker/issue_tracker.go
|
| diff --git a/tools/bug_chomper/src/issue_tracker/issue_tracker.go b/tools/bug_chomper/src/issue_tracker/issue_tracker.go
|
| deleted file mode 100644
|
| index 42e2e53681a31d9042f2aa1896bfba4b04f2cdb2..0000000000000000000000000000000000000000
|
| --- a/tools/bug_chomper/src/issue_tracker/issue_tracker.go
|
| +++ /dev/null
|
| @@ -1,304 +0,0 @@
|
| -// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -/*
|
| - Utilities for interacting with the GoogleCode issue tracker.
|
| -
|
| - Example usage:
|
| - issueTracker := issue_tracker.MakeIssueTraker(myOAuthConfigFile)
|
| - authURL := issueTracker.MakeAuthRequestURL()
|
| - // Visit the authURL to obtain an authorization code.
|
| - issueTracker.UpgradeCode(code)
|
| - // Now issueTracker can be used to retrieve and edit issues.
|
| -*/
|
| -package issue_tracker
|
| -
|
| -import (
|
| - "bytes"
|
| - "encoding/json"
|
| - "fmt"
|
| - "io/ioutil"
|
| - "net/http"
|
| - "net/url"
|
| - "strconv"
|
| - "strings"
|
| -
|
| - "code.google.com/p/goauth2/oauth"
|
| -)
|
| -
|
| -// BugPriorities are the possible values for "Priority-*" labels for issues.
|
| -var BugPriorities = []string{"Critical", "High", "Medium", "Low", "Never"}
|
| -
|
| -var apiScope = []string{
|
| - "https://www.googleapis.com/auth/projecthosting",
|
| - "https://www.googleapis.com/auth/userinfo.email",
|
| -}
|
| -
|
| -const issueApiURL = "https://www.googleapis.com/projecthosting/v2/projects/"
|
| -const issueURL = "https://code.google.com/p/skia/issues/detail?id="
|
| -const personApiURL = "https://www.googleapis.com/userinfo/v2/me"
|
| -
|
| -// Enum for determining whether a label has been added, removed, or is
|
| -// unchanged.
|
| -const (
|
| - labelAdded = iota
|
| - labelRemoved
|
| - labelUnchanged
|
| -)
|
| -
|
| -// loadOAuthConfig reads the OAuth given config file path and returns an
|
| -// appropriate oauth.Config.
|
| -func loadOAuthConfig(oauthConfigFile string) (*oauth.Config, error) {
|
| - errFmt := "failed to read OAuth config file: %s"
|
| - fileContents, err := ioutil.ReadFile(oauthConfigFile)
|
| - if err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - var decodedJson map[string]struct {
|
| - AuthURL string `json:"auth_uri"`
|
| - ClientId string `json:"client_id"`
|
| - ClientSecret string `json:"client_secret"`
|
| - TokenURL string `json:"token_uri"`
|
| - }
|
| - if err := json.Unmarshal(fileContents, &decodedJson); err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - config, ok := decodedJson["web"]
|
| - if !ok {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - return &oauth.Config{
|
| - ClientId: config.ClientId,
|
| - ClientSecret: config.ClientSecret,
|
| - Scope: strings.Join(apiScope, " "),
|
| - AuthURL: config.AuthURL,
|
| - TokenURL: config.TokenURL,
|
| - }, nil
|
| -}
|
| -
|
| -// Issue contains information about an issue.
|
| -type Issue struct {
|
| - Id int `json:"id"`
|
| - Project string `json:"projectId"`
|
| - Title string `json:"title"`
|
| - Labels []string `json:"labels"`
|
| -}
|
| -
|
| -// URL returns the URL of a given issue.
|
| -func (i Issue) URL() string {
|
| - return issueURL + strconv.Itoa(i.Id)
|
| -}
|
| -
|
| -// IssueList represents a list of issues from the IssueTracker.
|
| -type IssueList struct {
|
| - TotalResults int `json:"totalResults"`
|
| - Items []*Issue `json:"items"`
|
| -}
|
| -
|
| -// IssueTracker is the primary point of contact with the issue tracker,
|
| -// providing methods for authenticating to and interacting with it.
|
| -type IssueTracker struct {
|
| - OAuthConfig *oauth.Config
|
| - OAuthTransport *oauth.Transport
|
| -}
|
| -
|
| -// MakeIssueTracker creates and returns an IssueTracker with authentication
|
| -// configuration from the given authConfigFile.
|
| -func MakeIssueTracker(authConfigFile string, redirectURL string) (*IssueTracker, error) {
|
| - oauthConfig, err := loadOAuthConfig(authConfigFile)
|
| - if err != nil {
|
| - return nil, fmt.Errorf(
|
| - "failed to create IssueTracker: %s", err)
|
| - }
|
| - oauthConfig.RedirectURL = redirectURL
|
| - return &IssueTracker{
|
| - OAuthConfig: oauthConfig,
|
| - OAuthTransport: &oauth.Transport{Config: oauthConfig},
|
| - }, nil
|
| -}
|
| -
|
| -// MakeAuthRequestURL returns an authentication request URL which can be used
|
| -// to obtain an authorization code via user sign-in.
|
| -func (it IssueTracker) MakeAuthRequestURL() string {
|
| - // NOTE: Need to add XSRF protection if we ever want to run this on a public
|
| - // server.
|
| - return it.OAuthConfig.AuthCodeURL(it.OAuthConfig.RedirectURL)
|
| -}
|
| -
|
| -// IsAuthenticated determines whether the IssueTracker has sufficient
|
| -// permissions to retrieve and edit Issues.
|
| -func (it IssueTracker) IsAuthenticated() bool {
|
| - return it.OAuthTransport.Token != nil
|
| -}
|
| -
|
| -// UpgradeCode exchanges the single-use authorization code, obtained by
|
| -// following the URL obtained from IssueTracker.MakeAuthRequestURL, for a
|
| -// multi-use, session token. This is required before IssueTracker can retrieve
|
| -// and edit issues.
|
| -func (it *IssueTracker) UpgradeCode(code string) error {
|
| - token, err := it.OAuthTransport.Exchange(code)
|
| - if err == nil {
|
| - it.OAuthTransport.Token = token
|
| - return nil
|
| - } else {
|
| - return fmt.Errorf(
|
| - "failed to exchange single-user auth code: %s", err)
|
| - }
|
| -}
|
| -
|
| -// GetLoggedInUser retrieves the email address of the authenticated user.
|
| -func (it IssueTracker) GetLoggedInUser() (string, error) {
|
| - errFmt := "error retrieving user email: %s"
|
| - if !it.IsAuthenticated() {
|
| - return "", fmt.Errorf(errFmt, "User is not authenticated!")
|
| - }
|
| - resp, err := it.OAuthTransport.Client().Get(personApiURL)
|
| - if err != nil {
|
| - return "", fmt.Errorf(errFmt, err)
|
| - }
|
| - defer resp.Body.Close()
|
| - body, _ := ioutil.ReadAll(resp.Body)
|
| - if resp.StatusCode != http.StatusOK {
|
| - return "", fmt.Errorf(errFmt, fmt.Sprintf(
|
| - "user data API returned code %d: %v", resp.StatusCode, string(body)))
|
| - }
|
| - userInfo := struct {
|
| - Email string `json:"email"`
|
| - }{}
|
| - if err := json.Unmarshal(body, &userInfo); err != nil {
|
| - return "", fmt.Errorf(errFmt, err)
|
| - }
|
| - return userInfo.Email, nil
|
| -}
|
| -
|
| -// GetBug retrieves the Issue with the given ID from the IssueTracker.
|
| -func (it IssueTracker) GetBug(project string, id int) (*Issue, error) {
|
| - errFmt := fmt.Sprintf("error retrieving issue %d: %s", id, "%s")
|
| - if !it.IsAuthenticated() {
|
| - return nil, fmt.Errorf(errFmt, "user is not authenticated!")
|
| - }
|
| - requestURL := issueApiURL + project + "/issues/" + strconv.Itoa(id)
|
| - resp, err := it.OAuthTransport.Client().Get(requestURL)
|
| - if err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - defer resp.Body.Close()
|
| - body, _ := ioutil.ReadAll(resp.Body)
|
| - if resp.StatusCode != http.StatusOK {
|
| - return nil, fmt.Errorf(errFmt, fmt.Sprintf(
|
| - "issue tracker returned code %d:%v", resp.StatusCode, string(body)))
|
| - }
|
| - var issue Issue
|
| - if err := json.Unmarshal(body, &issue); err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - return &issue, nil
|
| -}
|
| -
|
| -// GetBugs retrieves all Issues with the given owner from the IssueTracker,
|
| -// returning an IssueList.
|
| -func (it IssueTracker) GetBugs(project string, owner string) (*IssueList, error) {
|
| - errFmt := "error retrieving issues: %s"
|
| - if !it.IsAuthenticated() {
|
| - return nil, fmt.Errorf(errFmt, "user is not authenticated!")
|
| - }
|
| - params := map[string]string{
|
| - "owner": url.QueryEscape(owner),
|
| - "can": "open",
|
| - "maxResults": "9999",
|
| - }
|
| - requestURL := issueApiURL + project + "/issues?"
|
| - first := true
|
| - for k, v := range params {
|
| - if first {
|
| - first = false
|
| - } else {
|
| - requestURL += "&"
|
| - }
|
| - requestURL += k + "=" + v
|
| - }
|
| - resp, err := it.OAuthTransport.Client().Get(requestURL)
|
| - if err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - defer resp.Body.Close()
|
| - body, _ := ioutil.ReadAll(resp.Body)
|
| - if resp.StatusCode != http.StatusOK {
|
| - return nil, fmt.Errorf(errFmt, fmt.Sprintf(
|
| - "issue tracker returned code %d:%v", resp.StatusCode, string(body)))
|
| - }
|
| -
|
| - var bugList IssueList
|
| - if err := json.Unmarshal(body, &bugList); err != nil {
|
| - return nil, fmt.Errorf(errFmt, err)
|
| - }
|
| - return &bugList, nil
|
| -}
|
| -
|
| -// SubmitIssueChanges creates a comment on the given Issue which modifies it
|
| -// according to the contents of the passed-in Issue struct.
|
| -func (it IssueTracker) SubmitIssueChanges(issue *Issue, comment string) error {
|
| - errFmt := "Error updating issue " + strconv.Itoa(issue.Id) + ": %s"
|
| - if !it.IsAuthenticated() {
|
| - return fmt.Errorf(errFmt, "user is not authenticated!")
|
| - }
|
| - oldIssue, err := it.GetBug(issue.Project, issue.Id)
|
| - if err != nil {
|
| - return fmt.Errorf(errFmt, err)
|
| - }
|
| - postData := struct {
|
| - Content string `json:"content"`
|
| - Updates struct {
|
| - Title *string `json:"summary"`
|
| - Labels []string `json:"labels"`
|
| - } `json:"updates"`
|
| - }{
|
| - Content: comment,
|
| - }
|
| - if issue.Title != oldIssue.Title {
|
| - postData.Updates.Title = &issue.Title
|
| - }
|
| - // TODO(borenet): Add other issue attributes, eg. Owner.
|
| - labels := make(map[string]int)
|
| - for _, label := range issue.Labels {
|
| - labels[label] = labelAdded
|
| - }
|
| - for _, label := range oldIssue.Labels {
|
| - if _, ok := labels[label]; ok {
|
| - labels[label] = labelUnchanged
|
| - } else {
|
| - labels[label] = labelRemoved
|
| - }
|
| - }
|
| - labelChanges := make([]string, 0)
|
| - for labelName, present := range labels {
|
| - if present == labelRemoved {
|
| - labelChanges = append(labelChanges, "-"+labelName)
|
| - } else if present == labelAdded {
|
| - labelChanges = append(labelChanges, labelName)
|
| - }
|
| - }
|
| - if len(labelChanges) > 0 {
|
| - postData.Updates.Labels = labelChanges
|
| - }
|
| -
|
| - postBytes, err := json.Marshal(&postData)
|
| - if err != nil {
|
| - return fmt.Errorf(errFmt, err)
|
| - }
|
| - requestURL := issueApiURL + issue.Project + "/issues/" +
|
| - strconv.Itoa(issue.Id) + "/comments"
|
| - resp, err := it.OAuthTransport.Client().Post(
|
| - requestURL, "application/json", bytes.NewReader(postBytes))
|
| - if err != nil {
|
| - return fmt.Errorf(errFmt, err)
|
| - }
|
| - defer resp.Body.Close()
|
| - body, _ := ioutil.ReadAll(resp.Body)
|
| - if resp.StatusCode != http.StatusOK {
|
| - return fmt.Errorf(errFmt, fmt.Sprintf(
|
| - "Issue tracker returned code %d:%v", resp.StatusCode, string(body)))
|
| - }
|
| - return nil
|
| -}
|
|
|