Skip to content

Commit

Permalink
feat: expose creation timestamp filters for pipelines/workflows (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucaspin authored Oct 8, 2024
1 parent 0c5b1bf commit 1f84651
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 8 deletions.
31 changes: 31 additions & 0 deletions api/client/pipelines_v1_alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"errors"
"fmt"
"net/url"

models "github.com/semaphoreci/cli/api/models"
"github.com/semaphoreci/cli/api/uuid"
Expand Down Expand Up @@ -92,6 +93,11 @@ func (c *PipelinesApiV1AlphaApi) ListPplByWfID(projectID, wfID string) ([]byte,
return body, nil
}

type ListOptions struct {
CreatedAfter int64
CreatedBefore int64
}

func (c *PipelinesApiV1AlphaApi) ListPpl(projectID string) ([]byte, error) {
detailed := fmt.Sprintf("%s?project_id=%s", c.ResourceNamePlural, projectID)
body, status, err := c.BaseClient.List(detailed)
Expand All @@ -106,3 +112,28 @@ func (c *PipelinesApiV1AlphaApi) ListPpl(projectID string) ([]byte, error) {

return body, nil
}

func (c *PipelinesApiV1AlphaApi) ListPplWithOptions(projectID string, options ListOptions) ([]byte, error) {
query := url.Values{}
query.Add("project_id", projectID)

if options.CreatedAfter > 0 {
query.Add("created_after", fmt.Sprintf("%d", options.CreatedAfter))
}

if options.CreatedBefore > 0 {
query.Add("created_before", fmt.Sprintf("%d", options.CreatedBefore))
}

body, status, err := c.BaseClient.ListWithParams(c.ResourceNamePlural, query)

if err != nil {
return nil, errors.New(fmt.Sprintf("connecting to Semaphore failed '%s'", err))
}

if status != 200 {
return nil, errors.New(fmt.Sprintf("http status %d with message \"%s\" received from upstream", status, body))
}

return body, nil
}
25 changes: 25 additions & 0 deletions api/client/workflows_v1_alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"errors"
"fmt"
"net/url"

models "github.com/semaphoreci/cli/api/models"
"github.com/semaphoreci/cli/api/uuid"
Expand Down Expand Up @@ -40,6 +41,30 @@ func (c *WorkflowApiV1AlphaApi) ListWorkflows(project_id string) (*models.Workfl
return models.NewWorkflowListV1AlphaFromJson(body)
}

func (c *WorkflowApiV1AlphaApi) ListWorkflowsWithOptions(projectID string, options ListOptions) (*models.WorkflowListV1Alpha, error) {
query := url.Values{}
query.Add("project_id", projectID)

if options.CreatedAfter > 0 {
query.Add("created_after", fmt.Sprintf("%d", options.CreatedAfter))
}

if options.CreatedBefore > 0 {
query.Add("created_before", fmt.Sprintf("%d", options.CreatedBefore))
}

body, status, err := c.BaseClient.ListWithParams(c.ResourceNamePlural, query)
if err != nil {
return nil, errors.New(fmt.Sprintf("connecting to Semaphore failed '%s'", err))
}

if status != 200 {
return nil, errors.New(fmt.Sprintf("http status %d with message \"%s\" received from upstream", status, body))
}

return models.NewWorkflowListV1AlphaFromJson(body)
}

func (c *WorkflowApiV1AlphaApi) CreateSnapshotWf(project_id, label, archivePath string) ([]byte, error) {
requestToken, err := uuid.NewUUID()

Expand Down
23 changes: 21 additions & 2 deletions cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"os"
"text/tabwriter"
"time"

client "github.com/semaphoreci/cli/api/client"
models "github.com/semaphoreci/cli/api/models"
Expand All @@ -16,6 +17,10 @@ import (
"github.com/spf13/cobra"
)

const (
DefaultListingAge = time.Hour * 24 * 90
)

var getCmd = &cobra.Command{
Use: "get [KIND]",
Short: "List resources.",
Expand Down Expand Up @@ -398,7 +403,7 @@ var GetPplCmd = &cobra.Command{
if len(args) == 0 {
projectID := getPrj(cmd)

pipelines.List(projectID)
pipelines.List(projectID, listOptions(cmd))
} else {
id := args[0]
pipelines.Describe(id, GetPplFollow)
Expand All @@ -417,7 +422,7 @@ var GetWfCmd = &cobra.Command{
projectID := getPrj(cmd)

if len(args) == 0 {
workflows.List(projectID)
workflows.List(projectID, listOptions(cmd))
} else {
wfID := args[0]
workflows.Describe(projectID, wfID)
Expand Down Expand Up @@ -504,6 +509,16 @@ func getPrj(cmd *cobra.Command) string {
return projectID
}

func listOptions(cmd *cobra.Command) client.ListOptions {
age, err := cmd.Flags().GetDuration("age")
utils.Check(err)

return client.ListOptions{
CreatedBefore: time.Now().Unix(),
CreatedAfter: time.Now().Add(-1 * age).Unix(),
}
}

func init() {
RootCmd.AddCommand(getCmd)

Expand Down Expand Up @@ -533,13 +548,17 @@ func init() {
"project name; if not specified will be inferred from git origin")
GetPplCmd.Flags().StringP("project-id", "i", "",
"project id; if not specified will be inferred from git origin")
GetPplCmd.Flags().DurationP("age", "", DefaultListingAge,
"list only pipelines created in the given duration; it accepts a Go duration. e.g. 24h, 30m, 60s")
getCmd.AddCommand(GetPplCmd)

getCmd.AddCommand(GetWfCmd)
GetWfCmd.Flags().StringP("project-name", "p", "",
"project name; if not specified will be inferred from git origin")
GetWfCmd.Flags().StringP("project-id", "i", "",
"project id; if not specified will be inferred from git origin")
GetWfCmd.Flags().DurationP("age", "", DefaultListingAge,
"list only workflows created in the given duration; it accepts a Go duration. e.g. 24h, 30m, 60s")

getCmd.AddCommand(GetDTCmd)
GetDTCmd.Flags().StringP("project-name", "p", "",
Expand Down
170 changes: 168 additions & 2 deletions cmd/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"net/url"
"testing"
"time"

httpmock "github.com/jarcoal/httpmock"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -360,6 +361,112 @@ func Test__GetAgent__Response200(t *testing.T) {
}
}

func Test__GetPipelines__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

received := false

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/projects/foo",
func(req *http.Request) (*http.Response, error) {
received = true

p := `{
"metadata": {
"id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe"
}
}`

return httpmock.NewStringResponse(200, p), nil
},
)

httpmock.RegisterResponder("GET", `=~^https:\/\/org\.semaphoretext\.xyz\/api\/v1alpha\/pipelines\?created_after=\d+&created_before=\d+&project_id=758cb945-7495-4e40-a9a1-4b3991c6a8fe`,
func(req *http.Request) (*http.Response, error) {
received = true

p := `[{
"pipeline": {
"ppl_id": "494b76aa-f3f0-4ecf-b5ef-c389591a01be",
"name": "snapshot test",
"state": "done",
"result": "passed",
"result_reason": "test",
"error_description": ""
}
}]`

return httpmock.NewStringResponse(200, p), nil
},
)

RootCmd.SetArgs([]string{"get", "pipelines", "--project-name", "foo"})
RootCmd.Execute()

if received == false {
t.Error("Expected the API to receive GET /pipelines")
}
}

func Test__GetPipelines__WithCreationTimestampFilters(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

received := false

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/projects/foo",
func(req *http.Request) (*http.Response, error) {
received = true

p := `{
"metadata": {
"id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe"
}
}`

return httpmock.NewStringResponse(200, p), nil
},
)

age := 720 * time.Hour
createdBefore := fmt.Sprintf("%d", time.Now().Unix())
createdAfter := fmt.Sprintf("%d", time.Now().Add(-1*age).Unix())
url := fmt.Sprintf("https://org.semaphoretext.xyz/api/v1alpha/pipelines?created_after=%s&created_before=%s&project_id=758cb945-7495-4e40-a9a1-4b3991c6a8fe", createdAfter, createdBefore)
httpmock.RegisterResponder("GET", url,
func(req *http.Request) (*http.Response, error) {
received = true

p := `[{
"pipeline": {
"ppl_id": "494b76aa-f3f0-4ecf-b5ef-c389591a01be",
"name": "snapshot test",
"state": "done",
"result": "passed",
"result_reason": "test",
"error_description": ""
}
}]`

return httpmock.NewStringResponse(200, p), nil
},
)

RootCmd.SetArgs([]string{
"get",
"pipelines",
"--project-name",
"foo",
"--age",
age.String(),
})

RootCmd.Execute()

if received == false {
t.Error("Expected the API to receive GET /pipelines")
}
}

func Test__GetPipeline__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
Expand Down Expand Up @@ -537,7 +644,7 @@ func Test__GetWorkflows__Response200(t *testing.T) {
},
)

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/plumber-workflows?project_id=758cb945-7495-4e40-a9a1-4b3991c6a8fe",
httpmock.RegisterResponder("GET", `=~^https:\/\/org\.semaphoretext\.xyz\/api\/v1alpha\/plumber-workflows\?created_after=\d+&created_before=\d+&project_id=758cb945-7495-4e40-a9a1-4b3991c6a8fe`,
func(req *http.Request) (*http.Response, error) {
received = true

Expand All @@ -560,6 +667,65 @@ func Test__GetWorkflows__Response200(t *testing.T) {
RootCmd.Execute()

if received == false {
t.Error("Expected the API to receive GET secrets/aaaaaaa")
t.Error("Expected the API to receive GET /plumber-workflows")
}
}

func Test__GetWorkflows__WithCreationTimestampFilters(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

received := false

httpmock.RegisterResponder("GET", "https://org.semaphoretext.xyz/api/v1alpha/projects/foo",
func(req *http.Request) (*http.Response, error) {
received = true

p := `{
"metadata": {
"id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe"
}
}`

return httpmock.NewStringResponse(200, p), nil
},
)

age := 720 * time.Hour
createdBefore := fmt.Sprintf("%d", time.Now().Unix())
createdAfter := fmt.Sprintf("%d", time.Now().Add(-1*age).Unix())
url := fmt.Sprintf("https://org.semaphoretext.xyz/api/v1alpha/plumber-workflows?created_after=%s&created_before=%s&project_id=758cb945-7495-4e40-a9a1-4b3991c6a8fe", createdAfter, createdBefore)
httpmock.RegisterResponder("GET", url,
func(req *http.Request) (*http.Response, error) {
received = true

p := `[{
"wf_id": "b129e277-4aa5-4308-8e31-ec825815e335",
"requester_id": "92f81b82-3584-4852-ab28-4866624bed1e",
"project_id": "758cb945-7495-4e40-a9a1-4b3991c6a8fe",
"initial_ppl_id": "92f81b82-3584-4852-ab28-4866624bed1e",
"created_at": {
"seconds": 1533833523,
"nanos": 537460000
}
}]`

return httpmock.NewStringResponse(200, p), nil
},
)

RootCmd.SetArgs([]string{
"get",
"workflows",
"--project-name",
"foo",
"--age",
age.String(),
})

RootCmd.Execute()

if received == false {
t.Error("Expected the API to receive GET /plumber-workflows")
}
}
4 changes: 2 additions & 2 deletions cmd/pipelines/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ func describe(c client.PipelinesApiV1AlphaApi, id string) ([]byte, bool) {
return pplY, pplJ.IsDone()
}

func List(projectID string) {
func List(projectID string, options client.ListOptions) {
fmt.Printf("%s\n", projectID)
c := client.NewPipelinesV1AlphaApi()
body, err := c.ListPpl(projectID)
body, err := c.ListPplWithOptions(projectID, options)
utils.Check(err)

prettyPrintPipelineList(body)
Expand Down
4 changes: 2 additions & 2 deletions cmd/workflows/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (
"github.com/semaphoreci/cli/cmd/utils"
)

func List(projectID string) {
func List(projectID string, options client.ListOptions) {
wfClient := client.NewWorkflowV1AlphaApi()
workflows, err := wfClient.ListWorkflows(projectID)
workflows, err := wfClient.ListWorkflowsWithOptions(projectID, options)
utils.Check(err)

prettyPrint(workflows)
Expand Down

0 comments on commit 1f84651

Please sign in to comment.