event.go

  1package gitlab
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"time"
  7
  8	"github.com/MichaelMure/git-bug/util/text"
  9	"github.com/xanzy/go-gitlab"
 10)
 11
 12// Event represents a unified GitLab event (note, label or state event).
 13type Event interface {
 14	ID() string
 15	UserID() int
 16	Kind() EventKind
 17	CreatedAt() time.Time
 18}
 19
 20type EventKind int
 21
 22const (
 23	EventUnknown EventKind = iota
 24	EventError
 25	EventComment
 26	EventTitleChanged
 27	EventDescriptionChanged
 28	EventClosed
 29	EventReopened
 30	EventLocked
 31	EventUnlocked
 32	EventChangedDuedate
 33	EventRemovedDuedate
 34	EventAssigned
 35	EventUnassigned
 36	EventChangedMilestone
 37	EventRemovedMilestone
 38	EventAddLabel
 39	EventRemoveLabel
 40	EventMentionedInIssue
 41	EventMentionedInMergeRequest
 42)
 43
 44var _ Event = &NoteEvent{}
 45
 46type NoteEvent struct{ gitlab.Note }
 47
 48func (n NoteEvent) ID() string           { return fmt.Sprintf("%d", n.Note.ID) }
 49func (n NoteEvent) UserID() int          { return n.Author.ID }
 50func (n NoteEvent) CreatedAt() time.Time { return *n.Note.CreatedAt }
 51func (n NoteEvent) Kind() EventKind {
 52
 53	switch {
 54	case !n.System:
 55		return EventComment
 56
 57	case n.Body == "closed":
 58		return EventClosed
 59
 60	case n.Body == "reopened":
 61		return EventReopened
 62
 63	case n.Body == "changed the description":
 64		return EventDescriptionChanged
 65
 66	case n.Body == "locked this issue":
 67		return EventLocked
 68
 69	case n.Body == "unlocked this issue":
 70		return EventUnlocked
 71
 72	case strings.HasPrefix(n.Body, "changed title from"):
 73		return EventTitleChanged
 74
 75	case strings.HasPrefix(n.Body, "changed due date to"):
 76		return EventChangedDuedate
 77
 78	case n.Body == "removed due date":
 79		return EventRemovedDuedate
 80
 81	case strings.HasPrefix(n.Body, "assigned to @"):
 82		return EventAssigned
 83
 84	case strings.HasPrefix(n.Body, "unassigned @"):
 85		return EventUnassigned
 86
 87	case strings.HasPrefix(n.Body, "changed milestone to %"):
 88		return EventChangedMilestone
 89
 90	case strings.HasPrefix(n.Body, "removed milestone"):
 91		return EventRemovedMilestone
 92
 93	case strings.HasPrefix(n.Body, "mentioned in issue"):
 94		return EventMentionedInIssue
 95
 96	case strings.HasPrefix(n.Body, "mentioned in merge request"):
 97		return EventMentionedInMergeRequest
 98
 99	default:
100		return EventUnknown
101	}
102
103}
104
105func (n NoteEvent) Title() string {
106	if n.Kind() == EventTitleChanged {
107		return getNewTitle(n.Body)
108	}
109	return text.CleanupOneLine(n.Body)
110}
111
112var _ Event = &LabelEvent{}
113
114type LabelEvent struct{ gitlab.LabelEvent }
115
116func (l LabelEvent) ID() string           { return fmt.Sprintf("%d", l.LabelEvent.ID) }
117func (l LabelEvent) UserID() int          { return l.User.ID }
118func (l LabelEvent) CreatedAt() time.Time { return *l.LabelEvent.CreatedAt }
119func (l LabelEvent) Kind() EventKind {
120	switch l.Action {
121	case "add":
122		return EventAddLabel
123	case "remove":
124		return EventRemoveLabel
125	default:
126		return EventUnknown
127	}
128}
129
130var _ Event = &StateEvent{}
131
132type StateEvent struct{ gitlab.StateEvent }
133
134func (s StateEvent) ID() string           { return fmt.Sprintf("%d", s.StateEvent.ID) }
135func (s StateEvent) UserID() int          { return s.User.ID }
136func (s StateEvent) CreatedAt() time.Time { return *s.StateEvent.CreatedAt }
137func (s StateEvent) Kind() EventKind {
138	switch s.State {
139	case "closed":
140		return EventClosed
141	case "opened", "reopened":
142		return EventReopened
143	default:
144		return EventUnknown
145	}
146}
147
148var _ Event = &ErrorEvent{}
149
150type ErrorEvent struct {
151	Err  error
152	Time time.Time
153}
154
155func (e ErrorEvent) ID() string           { return "" }
156func (e ErrorEvent) UserID() int          { return -1 }
157func (e ErrorEvent) CreatedAt() time.Time { return e.Time }
158func (e ErrorEvent) Kind() EventKind      { return EventError }
159
160// SortedEvents fan-in some Event-channels into one, sorted by creation date, using CreatedAt-method.
161// This function assume that each channel is pre-ordered.
162func SortedEvents(inputs ...<-chan Event) chan Event {
163	out := make(chan Event)
164
165	go func() {
166		defer close(out)
167
168		heads := make([]Event, len(inputs))
169
170		// pre-fill the head view
171		for i, input := range inputs {
172			if event, ok := <-input; ok {
173				heads[i] = event
174			}
175		}
176
177		for {
178			var earliestEvent Event
179			var originChannel int
180
181			// pick the earliest event of the heads
182			for i, head := range heads {
183				if head != nil && (earliestEvent == nil || head.CreatedAt().Before(earliestEvent.CreatedAt())) {
184					earliestEvent = head
185					originChannel = i
186				}
187			}
188
189			if earliestEvent == nil {
190				// no event anymore, we are done
191				return
192			}
193
194			// we have an event: consume it and replace it if possible
195			heads[originChannel] = nil
196			if event, ok := <-inputs[originChannel]; ok {
197				heads[originChannel] = event
198			}
199			out <- earliestEvent
200		}
201	}()
202
203	return out
204}
205
206// getNewTitle parses body diff given by gitlab api and return it final form
207// examples: "changed title from **fourth issue** to **fourth issue{+ changed+}**"
208//           "changed title from **fourth issue{- changed-}** to **fourth issue**"
209// because Gitlab
210func getNewTitle(diff string) string {
211	newTitle := strings.Split(diff, "** to **")[1]
212	newTitle = strings.Replace(newTitle, "{+", "", -1)
213	newTitle = strings.Replace(newTitle, "+}", "", -1)
214	return strings.TrimSuffix(newTitle, "**")
215}