Skip to content

Commit

Permalink
✨ Add pagination to "GET /v2/ocr/specs/:ID/runs" endpoint (smartcontr…
Browse files Browse the repository at this point in the history
  • Loading branch information
DeividasK authored Nov 11, 2020
1 parent dba35cf commit f18ccb5
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 23 deletions.
38 changes: 38 additions & 0 deletions core/store/orm/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/smartcontractkit/chainlink/core/auth"
"github.com/smartcontractkit/chainlink/core/gracefulpanic"
"github.com/smartcontractkit/chainlink/core/logger"
"github.com/smartcontractkit/chainlink/core/services/pipeline"
"github.com/smartcontractkit/chainlink/core/store/dbutil"
"github.com/smartcontractkit/chainlink/core/store/models"
"github.com/smartcontractkit/chainlink/core/store/models/vrfkey"
Expand Down Expand Up @@ -1051,6 +1052,43 @@ func (orm *ORM) FindOffChainReportingJob(id int32) (models.JobSpecV2, error) {
return job, err
}

// OffChainReportingJobRuns returns OCR job runs
func (orm *ORM) OffChainReportingJobRuns(jobID int32, offset, size int) ([]pipeline.Run, int, error) {
orm.MustEnsureAdvisoryLock()

var pipelineRuns []pipeline.Run
var count int

err := orm.DB.
Model(pipeline.Run{}).
Joins("INNER JOIN jobs ON pipeline_runs.pipeline_spec_id = jobs.pipeline_spec_id").
Where("jobs.id = ?", jobID).
Count(&count).
Error

if err != nil {
return pipelineRuns, 0, err
}

err = orm.DB.
Preload("PipelineSpec").
Preload("PipelineTaskRuns", func(db *gorm.DB) *gorm.DB {
return db.
Where(`pipeline_task_runs.type != 'result'`).
Order("created_at ASC, id ASC")
}).
Preload("PipelineTaskRuns.PipelineTaskSpec").
Joins("INNER JOIN jobs ON pipeline_runs.pipeline_spec_id = jobs.pipeline_spec_id").
Where("jobs.id = ?", jobID).
Limit(size).
Offset(offset).
Order("created_at ASC, id ASC").
Find(&pipelineRuns).
Error

return pipelineRuns, count, err
}

// TxFrom returns all transactions from a particular address.
func (orm *ORM) TxFrom(from common.Address) ([]models.Tx, error) {
orm.MustEnsureAdvisoryLock()
Expand Down
12 changes: 3 additions & 9 deletions core/web/ocr_job_runs_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,22 @@ type OCRJobRunsController struct {
// Index returns all pipeline runs for an OCR job.
// Example:
// "GET <application>/ocr/specs/:ID/runs"
func (ocrjrc *OCRJobRunsController) Index(c *gin.Context) {
func (ocrjrc *OCRJobRunsController) Index(c *gin.Context, size, page, offset int) {
jobSpec := models.JobSpecV2{}
err := jobSpec.SetID(c.Param("ID"))
if err != nil {
jsonAPIError(c, http.StatusUnprocessableEntity, err)
return
}

var pipelineRuns []pipeline.Run
err = preloadPipelineRunDependencies(ocrjrc.App.GetStore().DB).
Joins("INNER JOIN jobs ON pipeline_runs.pipeline_spec_id = jobs.pipeline_spec_id").
Where("jobs.offchainreporting_oracle_spec_id IS NOT NULL").
Where("jobs.id = ?", jobSpec.ID).
Order("created_at ASC, id ASC").
Find(&pipelineRuns).Error
pipelineRuns, count, err := ocrjrc.App.GetStore().OffChainReportingJobRuns(jobSpec.ID, offset, size)

if err != nil {
jsonAPIError(c, http.StatusInternalServerError, err)
return
}

jsonAPIResponse(c, pipelineRuns, "offChainReportingJobRun")
paginatedResponse(c, "offChainReportingJobRun", size, page, pipelineRuns, count, err)
}

// Show returns a specified pipeline run.
Expand Down
57 changes: 44 additions & 13 deletions core/web/ocr_job_runs_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ func TestOCRJobRunsController_Create_HappyPath(t *testing.T) {
}

func TestOCRJobRunsController_Index_HappyPath(t *testing.T) {
client, jobID, runID, cleanup, cleanupHTTP := setupOCRJobRunsControllerTests(t)
client, jobID, runIDs, cleanup := setupOCRJobRunsControllerTests(t)
defer cleanup()
defer cleanupHTTP()

response, cleanup := client.Get("/v2/ocr/specs/" + fmt.Sprintf("%v", jobID) + "/runs")
defer cleanup()
Expand All @@ -54,18 +53,41 @@ func TestOCRJobRunsController_Index_HappyPath(t *testing.T) {
err := web.ParseJSONAPIResponse(responseBytes, &parsedResponse)
assert.NoError(t, err)

assert.Equal(t, parsedResponse[0].ID, runID)
require.Len(t, parsedResponse, 2)
assert.Equal(t, parsedResponse[0].ID, runIDs[0])
assert.NotNil(t, parsedResponse[0].CreatedAt)
assert.NotNil(t, parsedResponse[0].FinishedAt)
require.Len(t, parsedResponse[0].PipelineTaskRuns, 4)
}

func TestOCRJobRunsController_Index_Pagination(t *testing.T) {
client, jobID, runIDs, cleanup := setupOCRJobRunsControllerTests(t)
defer cleanup()

response, cleanup := client.Get("/v2/ocr/specs/" + fmt.Sprintf("%v", jobID) + "/runs?page=2&size=1")
defer cleanup()
cltest.AssertServerResponse(t, response, http.StatusOK)

var parsedResponse []pipeline.Run
responseBytes := cltest.ParseResponseBody(t, response)
assert.Contains(t, string(responseBytes), `"meta":null,"errors":[null],"outputs":["3"]`)
assert.Contains(t, string(responseBytes), `"meta":{"count":2}`)

err := web.ParseJSONAPIResponse(responseBytes, &parsedResponse)
assert.NoError(t, err)

require.Len(t, parsedResponse, 1)
assert.Equal(t, parsedResponse[0].ID, runIDs[1])
assert.NotNil(t, parsedResponse[0].CreatedAt)
assert.NotNil(t, parsedResponse[0].FinishedAt)
require.Len(t, parsedResponse[0].PipelineTaskRuns, 4)
}

func TestOCRJobRunsController_Show_HappyPath(t *testing.T) {
client, jobID, runID, cleanup, cleanupHTTP := setupOCRJobRunsControllerTests(t)
client, jobID, runIDs, cleanup := setupOCRJobRunsControllerTests(t)
defer cleanup()
defer cleanupHTTP()

response, cleanup := client.Get("/v2/ocr/specs/" + fmt.Sprintf("%v", jobID) + "/runs/" + fmt.Sprintf("%v", runID))
response, cleanup := client.Get("/v2/ocr/specs/" + fmt.Sprintf("%v", jobID) + "/runs/" + fmt.Sprintf("%v", runIDs[0]))
defer cleanup()
cltest.AssertServerResponse(t, response, http.StatusOK)

Expand All @@ -76,7 +98,7 @@ func TestOCRJobRunsController_Show_HappyPath(t *testing.T) {
err := web.ParseJSONAPIResponse(responseBytes, &parsedResponse)
assert.NoError(t, err)

assert.Equal(t, parsedResponse.ID, runID)
assert.Equal(t, parsedResponse.ID, runIDs[0])
assert.NotNil(t, parsedResponse.CreatedAt)
assert.NotNil(t, parsedResponse.FinishedAt)
require.Len(t, parsedResponse.PipelineTaskRuns, 4)
Expand All @@ -94,13 +116,12 @@ func TestOCRJobRunsController_ShowRun_InvalidID(t *testing.T) {
cltest.AssertServerResponse(t, response, http.StatusUnprocessableEntity)
}

func setupOCRJobRunsControllerTests(t *testing.T) (cltest.HTTPClientCleaner, int32, int64, func(), func()) {
func setupOCRJobRunsControllerTests(t *testing.T) (cltest.HTTPClientCleaner, int32, []int64, func()) {
t.Parallel()
app, cleanup := cltest.NewApplication(t, cltest.LenientEthMock)
require.NoError(t, app.Start())
client := app.NewHTTPClient()
mockHTTP, cleanupHTTP := cltest.NewHTTPMockServer(t, http.StatusOK, "GET", `{"USD": 1}`)
httpURL := mockHTTP.URL

var ocrJobSpec offchainreporting.OracleSpec
toml.Decode(fmt.Sprintf(`
Expand All @@ -123,13 +144,23 @@ func setupOCRJobRunsControllerTests(t *testing.T) (cltest.HTTPClientCleaner, int
answer [type=median index=0];
"""
`, cltest.NewAddress().Hex(), cltest.DefaultP2PPeerID, cltest.DefaultOCRKeyBundleID, cltest.DefaultKey, httpURL), &ocrJobSpec)
`, cltest.NewAddress().Hex(), cltest.DefaultP2PPeerID, cltest.DefaultOCRKeyBundleID, cltest.DefaultKey, mockHTTP.URL), &ocrJobSpec)

jobID, err := app.AddJobV2(context.Background(), ocrJobSpec)
require.NoError(t, err)
runID, err := app.RunJobV2(context.Background(), jobID, nil)

firstRunID, err := app.RunJobV2(context.Background(), jobID, nil)
require.NoError(t, err)
secondRunID, err := app.RunJobV2(context.Background(), jobID, nil)
require.NoError(t, err)

err = app.AwaitRun(context.Background(), firstRunID)
require.NoError(t, err)
err = app.AwaitRun(context.Background(), runID)
err = app.AwaitRun(context.Background(), secondRunID)
require.NoError(t, err)

return client, jobID, runID, cleanup, cleanupHTTP
return client, jobID, []int64{firstRunID, secondRunID}, func() {
cleanup()
cleanupHTTP()
}
}
2 changes: 1 addition & 1 deletion core/web/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) {
ocr.DELETE("/specs/:ID", ocrjsc.Delete)

ocrjrc := OCRJobRunsController{app}
ocr.GET("/specs/:ID/runs", ocrjrc.Index)
ocr.GET("/specs/:ID/runs", paginatedRequest(ocrjrc.Index))
ocr.GET("/specs/:ID/runs/:runID", ocrjrc.Show)
ocr.POST("/specs/:ID/runs", ocrjrc.Create)
}
Expand Down

0 comments on commit f18ccb5

Please sign in to comment.