package gerrit import ( "bytes" "encoding/json" "io/ioutil" "net/url" "time" ) // PatchSet contains detailed information about a specific patch set. // // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#patchSet type PatchSet struct { Number Number `json:"number"` Revision string `json:"revision"` Parents []string `json:"parents"` Ref string `json:"ref"` Uploader AccountInfo `json:"uploader"` Author AccountInfo `json:"author"` CreatedOn int `json:"createdOn"` IsDraft bool `json:"isDraft"` Kind string `json:"kind"` } // RefUpdate contains data about a reference update. // // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/json.html#refUpdate type RefUpdate struct { OldRev string `json:"oldRev"` NewRev string `json:"newRev"` RefName string `json:"refName"` Project string `json:"project"` } // EventInfo contains information about an event emitted by Gerrit. This // structure can be used either when parsing streamed events or when reading // the output of the events-log plugin. // // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/cmd-stream-events.html#events type EventInfo struct { Type string `json:"type"` Change ChangeInfo `json:"change,omitempty"` ChangeKey ChangeInfo `json:"changeKey,omitempty"` PatchSet PatchSet `json:"patchSet,omitempty"` EventCreatedOn int `json:"eventCreatedOn,omitempty"` Reason string `json:"reason,omitempty"` Abandoner AccountInfo `json:"abandoner,omitempty"` Restorer AccountInfo `json:"restorer,omitempty"` Submitter AccountInfo `json:"submitter,omitempty"` Author AccountInfo `json:"author,omitempty"` Uploader AccountInfo `json:"uploader,omitempty"` Approvals []AccountInfo `json:"approvals,omitempty"` Comment string `json:"comment,omitempty"` Editor AccountInfo `json:"editor,omitempty"` Added []string `json:"added,omitempty"` Removed []string `json:"removed,omitempty"` Hashtags []string `json:"hashtags,omitempty"` RefUpdate RefUpdate `json:"refUpdate,omitempty"` Project ProjectInfo `json:"project,omitempty"` Reviewer AccountInfo `json:"reviewer,omitempty"` OldTopic string `json:"oldTopic,omitempty"` Changer AccountInfo `json:"changer,omitempty"` } // EventsLogService contains functions for querying the API provided // by the optional events-log plugin. type EventsLogService struct { client *Client } // EventsLogOptions contains options for querying events from the events-logs // plugin. type EventsLogOptions struct { From time.Time To time.Time // IgnoreUnmarshalErrors will cause GetEvents to ignore any errors // that come up when calling json.Unmarshal. This can be useful in // cases where the events-log plugin was not kept up to date with // the Gerrit version for some reason. In these cases the events-log // plugin will return data structs that don't match the EventInfo // struct which in turn causes issues for json.Unmarshal. IgnoreUnmarshalErrors bool } // getURL returns the url that should be used in the request. This will vary // depending on the options provided to GetEvents. func (events *EventsLogService) getURL(options *EventsLogOptions) (string, error) { parsed, err := url.Parse("/plugins/events-log/events/") if err != nil { return "", err } query := parsed.Query() if !options.From.IsZero() { query.Set("t1", options.From.Format("2006-01-02 15:04:05")) } if !options.To.IsZero() { query.Set("t2", options.To.Format("2006-01-02 15:04:05")) } encoded := query.Encode() if len(encoded) > 0 { parsed.RawQuery = encoded } return parsed.String(), nil } // GetEvents returns a list of events for the given input options. Use of this // function requires an authenticated user and for the events-log plugin to be // installed. This function returns the unmarshalled EventInfo structs, response, // failed lines and errors. Marshaling errors will cause this function to return // before processing is complete unless you set EventsLogOptions.IgnoreUnmarshalErrors // to true. This can be useful in cases where the events-log plugin got out of sync // with the Gerrit version which in turn produced events which can't be transformed // unmarshalled into EventInfo. // // Gerrit API docs: https:///plugins/events-log/Documentation/rest-api-events.html func (events *EventsLogService) GetEvents(options *EventsLogOptions) ([]EventInfo, *Response, [][]byte, error) { info := []EventInfo{} failures := [][]byte{} requestURL, err := events.getURL(options) if err != nil { return info, nil, failures, err } request, err := events.client.NewRequest("GET", requestURL, nil) if err != nil { return info, nil, failures, err } // Perform the request but do not pass in a structure to unpack // the response into. The format of the response is one EventInfo // object per line so we need to manually handle the response here. response, err := events.client.Do(request, nil) if err != nil { return info, response, failures, err } body, err := ioutil.ReadAll(response.Body) defer response.Body.Close() // nolint: errcheck if err != nil { return info, response, failures, err } for _, line := range bytes.Split(body, []byte("\n")) { if len(line) > 0 { event := EventInfo{} if err := json.Unmarshal(line, &event); err != nil { // nolint: vetshadow failures = append(failures, line) if !options.IgnoreUnmarshalErrors { return info, response, failures, err } continue } info = append(info, event) } } return info, response, failures, err }