dmitri.shuralyov.com/service/change/gerritapi

Update implementation for new changes.Service.
dmitshur committed 6 years ago commit 3183db689a0242496e5b81d66ea4c848f7ed2773
Showing partial commit. Full Commit
Collapse all
gerritapi/gerritapi.go
@@ -6,14 +6,14 @@ import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"
	"unicode"

	"dmitri.shuralyov.com/changes"
	"github.com/andygrunwald/go-gerrit"
	"github.com/shurcooL/issues"
	"github.com/shurcooL/users"
)

// NewService creates a Gerrit-backed issues.Service using given Gerrit client.
// client must be non-nil.
@@ -243,48 +243,126 @@ func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *changes.
		}
		return []byte(diff), nil
	}
}

func (s service) ListComments(ctx context.Context, _ string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Comment, error) {
func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) {
	// TODO: Pagination. Respect opt.Start and opt.Length, if given.

	change, _, err := s.cl.Changes.GetChangeDetail(fmt.Sprint(id), nil)
	if err != nil {
		return nil, err
	}
	var comments []issues.Comment
	var timeline []interface{}
	{
		timeline = append(timeline, changes.Comment{
			ID:        0,
			User:      s.gerritUser(change.Owner),
			CreatedAt: time.Time(change.Created),
			Body:      "", // THINK: Include commit message or no?
			Editable:  false,
		})
	}
	for idx, message := range change.Messages {
		if strings.HasPrefix(message.Tag, "autogenerated:") {
			continue
		}
		comments = append(comments, issues.Comment{
		label, body, ok := parseMessage(message.Message)
		if !ok {
			continue
		}
		switch label {
		case "Code-Review+2":
			timeline = append(timeline, changes.TimelineItem{
				Actor:     s.gerritUser(message.Author),
				CreatedAt: time.Time(message.Date),
				Payload:   changes.ApprovedEvent{},
			})
		case "Code-Review-2":
			timeline = append(timeline, changes.TimelineItem{
				Actor:     s.gerritUser(message.Author),
				CreatedAt: time.Time(message.Date),
				Payload:   changes.ChangesRequestedEvent{},
			})
		}
		if body == "" {
			continue
		}
		timeline = append(timeline, changes.Comment{
			ID:        uint64(idx), // TODO: message.ID is not uint64; e.g., "bfba753d015916303152305cee7152ea7a112fe0".
			User:      s.gerritUser(message.Author),
			CreatedAt: time.Time(message.Date),
			Body:      message.Message,
			Body:      body,
			Editable:  false,
		})
	}
	return comments, nil
	return timeline, nil
}

func (s service) ListEvents(ctx context.Context, _ string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Event, error) {
	// TODO.
	return nil, nil
func parseMessage(m string) (label string, body string, ok bool) {
	// "Patch Set ".
	if !strings.HasPrefix(m, "Patch Set ") {
		return "", "", false
	}
	m = m[len("Patch Set "):]

	// "123".
	i := strings.IndexFunc(m, func(c rune) bool { return !unicode.IsNumber(c) })
	if i == -1 {
		return "", "", false
	}
	m = m[i:]

	// ":".
	if len(m) < 1 || m[0] != ':' {
		return "", "", false
	}
	m = m[1:]

	switch i = strings.IndexByte(m, '\n'); i {
	case -1:
		label = m
	default:
		label = m[:i]
		body = m[i+1:]
	}

	if label != "" {
		// " ".
		if len(label) < 1 || label[0] != ' ' {
			return "", "", false
		}
		label = label[1:]
	}

	if body != "" {
		// "\n".
		if len(body) < 1 || body[0] != '\n' {
			return "", "", false
		}
		body = body[1:]
	}

	return label, body, true
}

func (s service) gerritUser(user gerrit.AccountInfo) users.User {
	var avatarURL string
	for _, avatar := range user.Avatars {
		if avatar.Height == 100 {
			avatarURL = avatar.URL
		}
	}
	return users.User{
		UserSpec: users.UserSpec{
			ID:     uint64(user.AccountID),
			Domain: s.domain,
		},
		Login: user.Name, //user.Username, // TODO.
		Name:  user.Name,
		//Email:     user.Email,
		AvatarURL: fmt.Sprintf("https://%s/accounts/%d/avatar?s=96", s.domain, user.AccountID),
		AvatarURL: avatarURL,
	}
}

func project(repo string) string {
	if i := strings.IndexByte(repo, '/'); i != -1 {
gerritapi/gerritapi_test.go
@@ -0,0 +1,41 @@
package gerritapi

import (
	"testing"
)

func TestParseMessage(t *testing.T) {
	tests := []struct {
		in        string
		wantLabel string
		wantBody  string
	}{
		{
			in:        "Patch Set 2: Code-Review+2",
			wantLabel: "Code-Review+2",
			wantBody:  "",
		},
		{
			in:        "Patch Set 2: Code-Review+2\n\nThanks.",
			wantLabel: "Code-Review+2",
			wantBody:  "Thanks.",
		},
		{
			in:        "Patch Set 1:\n\nFirst contribution — trying to get my feet wet. Please review.",
			wantLabel: "",
			wantBody:  "First contribution — trying to get my feet wet. Please review.",
		},
	}
	for i, tc := range tests {
		gotLabel, gotBody, ok := parseMessage(tc.in)
		if !ok {
			t.Fatalf("%d: not ok", i)
		}
		if gotLabel != tc.wantLabel {
			t.Errorf("%d: got label: %q, want: %q", i, gotLabel, tc.wantLabel)
		}
		if gotBody != tc.wantBody {
			t.Errorf("%d: got body: %q, want: %q", i, gotBody, tc.wantBody)
		}
	}
}