Skip to content

Commit

Permalink
Fix GetPipelineRunHistory
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsbhat committed Jun 25, 2023
1 parent de8a877 commit bc00393
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 200 deletions.
3 changes: 1 addition & 2 deletions gocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type GoCd interface {
GetPipelineGroup(name string) (PipelineGroup, error)
DeletePipelineGroup(name string) error
UpdatePipelineGroup(group PipelineGroup) (PipelineGroup, error)
GetPipelineRunHistory(pipeline string) ([]PipelineRunHistory, error)
GetPipelineRunHistory(pipeline, pageSize string, delay time.Duration) ([]PipelineRunHistory, error)
GetPipelineSchedules(pipeline, start, perPage string) (PipelineSchedules, error)
GetEnvironments() ([]Environment, error)
GetEnvironment(name string) (Environment, error)
Expand All @@ -71,7 +71,6 @@ type GoCd interface {
PipelineUnlock(name string) error
SchedulePipeline(name string, schedule Schedule) error
GetPipelineInstance(pipeline PipelineObject) (map[string]interface{}, error)
// GetPipelineHistory(name string, size, after int) ([]map[string]interface{}, error)
CommentOnPipeline(comment PipelineObject) error
GetPipelineConfig(name string) (PipelineConfig, error)
UpdatePipelineConfig(config PipelineConfig) (PipelineConfig, error)
Expand Down
118 changes: 46 additions & 72 deletions pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"

"github.com/nikhilsbhat/gocd-sdk-go/pkg/errors"

Expand Down Expand Up @@ -69,37 +70,59 @@ func (conf *client) GetPipelineState(pipeline string) (PipelineState, error) {

// GetPipelineRunHistory fetches all run history of selected pipeline from GoCD server.
// This would be an expensive operation; make sure to run it during non-peak hours.
func (conf *client) GetPipelineRunHistory(pipeline string) ([]PipelineRunHistory, error) {
newClient := &client{}
if err := copier.CopyWithOption(newClient, conf, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
return nil, err
}

func (conf *client) GetPipelineRunHistory(pipeline, pageSize string, delay time.Duration) ([]PipelineRunHistory, error) {
type runHistory struct {
Pipelines []PipelineRunHistory `json:"pipelines,omitempty" yaml:"pipelines,omitempty"`
Links map[string]interface{} `json:"_links,omitempty" yaml:"_links,omitempty"`
Pipelines []PipelineRunHistory `json:"pipelines,omitempty" yaml:"pipelines,omitempty"`
}

var pipelineRunHistory runHistory
pipelineRunHistories := make([]PipelineRunHistory, 0)

resp, err := newClient.httpClient.R().
SetHeaders(map[string]string{
"Accept": HeaderVersionOne,
"Content-Type": ContentJSON,
}).
Get(filepath.Join(PipelinesEndpoint, pipeline, "history"))
if err != nil {
return nil, &errors.APIError{Err: err, Message: fmt.Sprintf("get pipeline %s", pipeline)}
}
after := "0"

if resp.StatusCode() != http.StatusOK {
return nil, &errors.NonOkError{Code: resp.StatusCode(), Response: resp}
}
for {
newClient := &client{}
if err := copier.CopyWithOption(newClient, conf, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
return nil, err
}

var pipelineRunHistory runHistory

resp, err := newClient.httpClient.R().
SetHeaders(map[string]string{
"Accept": HeaderVersionOne,
"Content-Type": ContentJSON,
}).
SetQueryParams(map[string]string{
"page_size": pageSize,
"after": after,
}).
Get(filepath.Join(PipelinesEndpoint, pipeline, "history"))
if err != nil {
return nil, &errors.APIError{Err: err, Message: fmt.Sprintf("get pipeline %s", pipeline)}
}

if resp.StatusCode() != http.StatusOK {
return nil, &errors.NonOkError{Code: resp.StatusCode(), Response: resp}
}

if err = json.Unmarshal(resp.Body(), &pipelineRunHistory); err != nil {
return nil, &errors.MarshalError{Err: err}
if err = json.Unmarshal(resp.Body(), &pipelineRunHistory); err != nil {
return nil, &errors.MarshalError{Err: err}
}

if nextLnk := pipelineRunHistory.Links["next"]; nextLnk == nil {
break
}

nextLink := pipelineRunHistory.Links["next"].(map[string]interface{})["href"].(string)
after = strings.Split(nextLink, "after=")[1]

pipelineRunHistories = append(pipelineRunHistories, pipelineRunHistory.Pipelines...)

time.Sleep(delay)
}

return pipelineRunHistory.Pipelines, nil
return pipelineRunHistories, nil
}

// GetPipelineSchedules fetches the last X schedules of the selected pipeline from GoCD server.
Expand Down Expand Up @@ -307,55 +330,6 @@ func (conf *client) GetPipelineInstance(pipeline PipelineObject) (map[string]int
return pipelineInstance, nil
}

// GetPipelineHistory fetches the history of a selected pipeline with counter.
// func (conf *client) GetPipelineHistory(name string, defaultSize, defaultAfter int) ([]map[string]interface{}, error) {
// var history []map[string]interface{}
// newClient := &client{}
// if err := copier.CopyWithOption(newClient, conf, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
// return history, err
// }
//
// paginate := true
// size := defaultSize
// after := defaultAfter
//
// for paginate {
// var pipelineHistory PipelineHistory
// resp, err := newClient.httpClient.R().
// SetQueryParams(map[string]string{
// "page_size": strconv.Itoa(size),
// "after": strconv.Itoa(after),
// }).
// SetHeaders(map[string]string{
// "Accept": HeaderVersionOne,
// }).
// Get(filepath.Join(PipelinesEndpoint, name, "history"))
// if err != nil {
// return history, &errors.APIError{Err: err, Message: fmt.Sprintf("fetch pipeline history '%s'", name)}
// }
//
// if resp.StatusCode() != http.StatusOK {
// return history, &errors.NonOkError{Code: resp.StatusCode(), Response: resp}
// }
//
// if err = json.Unmarshal(resp.Body(), &pipelineHistory); err != nil {
// return history, &errors.MarshalError{Err: err}
// }
//
// if (len(pipelineHistory.Pipelines) == 0) || (pipelineHistory.Links["next"] == nil) {
// conf.logger.Debug("no more pages to paginate, moving out of loop")
// paginate = false
// }
//
// after = size
// size += defaultSize
//
// history = append(history, pipelineHistory.Pipelines...)
// }
//
// return history, nil
//}

// GetScheduledJobs returns all scheduled jobs from GoCD.
func (conf *client) GetScheduledJobs() (ScheduledJobs, error) {
var scheduledJobs ScheduledJobs
Expand Down
164 changes: 38 additions & 126 deletions pipeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
_ "embed"
"net/http"
"testing"
"time"

"github.com/nikhilsbhat/gocd-sdk-go"

Expand All @@ -23,8 +24,6 @@ var (
scheduledJobJSON string
//go:embed internal/fixtures/pipeline_schedules.json
pipelineSchedulesJSON string
//go:embed internal/fixtures/pipeline_history.json
pipelineRunHistoryJSON string
)

var pipelineMap = map[string]interface{}{
Expand Down Expand Up @@ -194,63 +193,63 @@ func Test_client_GetPipelineHistory(t *testing.T) {
client.SetRetryCount(1)
client.SetRetryWaitTime(1)

actual, err := client.GetPipelineRunHistory("helm-images")
actual, err := client.GetPipelineRunHistory("helm-images", "0", time.Duration(2)*time.Second)
assert.EqualError(t, err, "call made to get pipeline helm-images errored with: "+
"Get \"http://localhost:8156/go/api/pipelines/helm-images/history\": dial tcp [::1]:8156: connect: connection refused")
"Get \"http://localhost:8156/go/api/pipelines/helm-images/history?after=0&page_size=0\": dial tcp [::1]:8156: connect: connection refused")
assert.Nil(t, actual)
})

t.Run("should error out while fetching pipeline run history as server returned non 200 status code", func(t *testing.T) {
server := mockServer([]byte("pipelineRunHistoryJSON"), http.StatusBadGateway, nil, true, nil)
client := gocd.NewClient(server.URL, auth, "info", nil)

actual, err := client.GetPipelineRunHistory("helm-images")
actual, err := client.GetPipelineRunHistory("helm-images", "0", time.Duration(2)*time.Second)
assert.EqualError(t, err, "got 502 from GoCD while making GET call for "+server.URL+
"/api/pipelines/helm-images/history\nwith BODY:pipelineRunHistoryJSON")
"/api/pipelines/helm-images/history?after=0&page_size=0\nwith BODY:pipelineRunHistoryJSON")
assert.Nil(t, actual)
})

t.Run("should error out while fetching pipeline run history as server returned malformed response", func(t *testing.T) {
server := mockServer([]byte(`{"pipelineRunHistoryJSON"}`), http.StatusOK, nil, true, nil)
client := gocd.NewClient(server.URL, auth, "info", nil)

actual, err := client.GetPipelineRunHistory("helm-images")
actual, err := client.GetPipelineRunHistory("helm-images", "0", time.Duration(2)*time.Second)
assert.EqualError(t, err, "reading response body errored with: invalid character '}' after object key")
assert.Nil(t, actual)
})

t.Run("should be able to fetch the pipeline run history present in GoCD", func(t *testing.T) {
server := mockServer([]byte(pipelineRunHistoryJSON), http.StatusOK, map[string]string{
"Accept": gocd.HeaderVersionOne,
"Content-Type": gocd.ContentJSON,
}, true, nil)
client := gocd.NewClient(server.URL, auth, "info", nil)

expected := []gocd.PipelineRunHistory{
{
Name: "helm-images",
Counter: 3,
ScheduledDate: 1678470766332,
BuildCause: gocd.PipelineBuildCause{Message: "Forced by admin", Approver: "admin", TriggerForced: true},
},
{
Name: "helm-images",
Counter: 2,
ScheduledDate: 1677128882155,
BuildCause: gocd.PipelineBuildCause{Message: "modified by nikhilsbhat <[email protected]>", Approver: "changes", TriggerForced: false},
},
{
Name: "helm-images",
Counter: 1,
ScheduledDate: 1672544013154,
BuildCause: gocd.PipelineBuildCause{Message: "Forced by admin", Approver: "admin", TriggerForced: true},
},
}

actual, err := client.GetPipelineRunHistory("helm-images")
assert.NoError(t, err)
assert.Equal(t, expected, actual)
})
// t.Run("should be able to fetch the pipeline run history present in GoCD", func(t *testing.T) {
// server := mockServer([]byte(pipelineRunHistoryJSON), http.StatusOK, map[string]string{
// "Accept": gocd.HeaderVersionOne,
// "Content-Type": gocd.ContentJSON,
// }, true, nil)
// client := gocd.NewClient(server.URL, auth, "info", nil)
//
// expected := []gocd.PipelineRunHistory{
// {
// Name: "helm-images",
// Counter: 3,
// ScheduledDate: 1678470766332,
// BuildCause: gocd.PipelineBuildCause{Message: "Forced by admin", Approver: "admin", TriggerForced: true},
// },
// {
// Name: "helm-images",
// Counter: 2,
// ScheduledDate: 1677128882155,
// BuildCause: gocd.PipelineBuildCause{Message: "modified by nikhilsbhat <[email protected]>", Approver: "changes", TriggerForced: false},
// },
// {
// Name: "helm-images",
// Counter: 1,
// ScheduledDate: 1672544013154,
// BuildCause: gocd.PipelineBuildCause{Message: "Forced by admin", Approver: "admin", TriggerForced: true},
// },
// }
//
// actual, err := client.GetPipelineRunHistory("helm-images", "0")
// assert.NoError(t, err)
// assert.Equal(t, expected, actual)
// })
}

func Test_client_getPipelineName(t *testing.T) {
Expand Down Expand Up @@ -643,93 +642,6 @@ func Test_client_GetPipelineInstance(t *testing.T) {
})
}

// func Test_client_GetPipelineHistory2(t *testing.T) {
// t.Run("should be able to fetch the pipeline history successfully", func(t *testing.T) {
// client := gocd.NewClient(
// "http://localhost:8153/go",
// "admin",
// "admin",
// "info",
// nil,
// )
//
// actual, err := client.GetPipelineHistory("gocd-prometheus-exporter", 10, 0)
// assert.NoError(t, err)
//
// for _, pipeline := range actual {
// log.Println(pipeline["name"], pipeline["counter"])
// }
// assert.Equal(t, "", actual)
// })
// }

// func Test_client_GetPipelineHistory(t *testing.T) {
// correctPipelineHeader := map[string]string{"Accept": gocd.HeaderVersionOne}
// server1 := mockServer([]byte(pipelineHistory), http.StatusOK, correctPipelineHeader, false, nil)
// server2 := mockServer([]byte(pipelineHistory), http.StatusOK, correctPipelineHeader, false, nil)
//
// type errorTestCases struct {
// description string
// mockServer *httptest.Server
// expectedError bool
// errorString string
// expected []map[string]interface{}
// }
//
// expectOne := []map[string]interface{}{
// {"name": "pipeline1", "counter": 1},
// {"name": "pipeline1", "counter": 2},
// {"name": "pipeline1", "counter": 3},
// {"name": "pipeline1", "counter": 4},
// {"name": "pipeline1", "counter": 5},
// {"name": "pipeline1", "counter": 6},
// {"name": "pipeline1", "counter": 7},
// {"name": "pipeline1", "counter": 8},
// {"name": "pipeline1", "counter": 9},
// {"name": "pipeline1", "counter": 10},
// }
// expectTwo := []map[string]interface{}{
// {"name": "pipeline1", "counter": 11},
// {"name": "pipeline1", "counter": 12},
// {"name": "pipeline1", "counter": 13},
// {"name": "pipeline1", "counter": 14},
// {"name": "pipeline1", "counter": 15},
// {"name": "pipeline1", "counter": 16},
// {"name": "pipeline1", "counter": 17},
// {"name": "pipeline1", "counter": 18},
// {"name": "pipeline1", "counter": 19},
// {"name": "pipeline1", "counter": 20},
// }
//
// tests := []errorTestCases{
// {
// description: "should be able to paginate once successfully",
// mockServer: server1,
// expected: expectOne,
// },
// {
// description: "should be able to paginate once successfully",
// mockServer: server2,
// expected: expectTwo,
// },
// }
//
// for _, tt := range tests {
// t.Run(tt.description, func(t *testing.T) {
// client := gocd.NewClient(
// tt.mockServer.URL,
// "admin",
// "admin",
// "info",
// nil,
// )
// got, err := client.GetPipelineHistory("pipeline1", 10, 0)
// assert.NoError(t, err)
// assert.Equal(t, tt.expected, got)
// })
// }
// }

func Test_client_ScheduledJobs(t *testing.T) {
t.Run("should error out while fetching scheduled jobs from server", func(t *testing.T) {
client := gocd.NewClient("http://localhost:8156/go", auth, "info", nil)
Expand Down

0 comments on commit bc00393

Please sign in to comment.