Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose creation timestamp filters for pipelines/workflows #234

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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