Skip to content
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
1 change: 1 addition & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func getWorkerNamespace() components.Namespace {
commands.GetAddSecretCommand(),
commands.GetListEventsCommand(),
commands.GetEditScheduleCommand(),
commands.GetShowExecutionHistoryCommand(),
},
}
}
6 changes: 2 additions & 4 deletions commands/common/test_commons.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,10 @@ func AssertOutputJson[T any](wantResponse T) AssertOutputFunc {
return func(t *testing.T, output []byte, err error) {
require.NoError(t, err)

outputData := new(T)

err = json.Unmarshal(output, outputData)
wantJson, err := json.Marshal(wantResponse)
require.NoError(t, err)

assert.Equal(t, wantResponse, *outputData)
assert.JSONEq(t, string(wantJson), string(output))
}
}

Expand Down
137 changes: 117 additions & 20 deletions commands/common/test_worker_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"net/http"
"regexp"
"slices"
"strings"
"testing"
"time"
Expand All @@ -19,6 +20,10 @@ import (
"github.com/stretchr/testify/require"
)

const (
EndpointExecutionHistory = "/worker/api/v1/execution_history"
)

type BodyValidator func(t require.TestingT, content []byte)

func ValidateJson(expected any) BodyValidator {
Expand Down Expand Up @@ -62,22 +67,43 @@ func NewMockWorkerServer(t *testing.T, stubs ...*ServerStub) (*mockhttp.Server,
return server, token
}

type queryParamStub struct {
value string
paths []string
}

type ExecutionHistoryResultEntryStub struct {
Result string `json:"result"`
Logs string `json:"logs"`
}

type ExecutionHistoryEntryStub struct {
Start time.Time `json:"start"`
End time.Time `json:"end"`
TestRun bool `json:"testRun"`
Result ExecutionHistoryResultEntryStub `json:"entries"`
}

type ExecutionHistoryStub []*ExecutionHistoryEntryStub

func NewServerStub(t *testing.T) *ServerStub {
return &ServerStub{
test: t,
workers: map[string]*model.WorkerDetails{},
queryParams: map[string]string{},
test: t,
workers: map[string]*model.WorkerDetails{},
queryParams: map[string]queryParamStub{},
executionHistory: map[string]ExecutionHistoryStub{},
}
}

type ServerStub struct {
test *testing.T
waitFor time.Duration
token string
projectKey string
workers map[string]*model.WorkerDetails
endpoints []mockhttp.ServerEndpoint
queryParams map[string]string
test *testing.T
waitFor time.Duration
token string
projectKey *queryParamStub
workers map[string]*model.WorkerDetails
executionHistory map[string]ExecutionHistoryStub
endpoints []mockhttp.ServerEndpoint
queryParams map[string]queryParamStub
}

func (s *ServerStub) WithT(t *testing.T) *ServerStub {
Expand All @@ -95,13 +121,16 @@ func (s *ServerStub) WithToken(token string) *ServerStub {
return s
}

func (s *ServerStub) WithProjectKey(projectKey string) *ServerStub {
s.projectKey = projectKey
func (s *ServerStub) WithProjectKey(projectKey string, paths ...string) *ServerStub {
s.projectKey = &queryParamStub{
value: projectKey,
paths: paths,
}
return s
}

func (s *ServerStub) WithQueryParam(name, value string) *ServerStub {
s.queryParams[name] = value
func (s *ServerStub) WithQueryParam(name, value string, paths ...string) *ServerStub {
s.queryParams[name] = queryParamStub{value: value, paths: paths}
return s
}

Expand All @@ -112,6 +141,11 @@ func (s *ServerStub) WithWorkers(workers ...*model.WorkerDetails) *ServerStub {
return s
}

func (s *ServerStub) WithWorkerExecutionHistory(workerKey string, history ExecutionHistoryStub) *ServerStub {
s.executionHistory[workerKey] = history
return s
}

func (s *ServerStub) WithCreateEndpoint(validateBody BodyValidator) *ServerStub {
s.endpoints = append(s.endpoints,
mockhttp.NewServerEndpoint().
Expand Down Expand Up @@ -222,6 +256,19 @@ func (s *ServerStub) WithGetAllEndpoint() *ServerStub {
return s
}

func (s *ServerStub) WithGetExecutionHistoryEndpoint() *ServerStub {
s.endpoints = append(s.endpoints,
mockhttp.NewServerEndpoint().
When(
mockhttp.Request().
Method(http.MethodGet).
Path(EndpointExecutionHistory),
).
HandleWith(s.handleGetExecutionHistory),
)
return s
}

func (s *ServerStub) handleGetAll(res http.ResponseWriter, req *http.Request) {
s.applyDelay()

Expand Down Expand Up @@ -284,6 +331,48 @@ func (s *ServerStub) handleGetOne(res http.ResponseWriter, req *http.Request) {
require.NoError(s.test, err)
}

func (s *ServerStub) handleGetExecutionHistory(res http.ResponseWriter, req *http.Request) {
s.applyDelay()

if !s.validateToken(res, req) {
return
}

if !s.validateProjectKey(res, req) {
return
}

if !s.validateQueryParams(res, req) {
return
}

workerKey := req.URL.Query().Get("workerKey")

executionHistory, hasHistory := s.executionHistory[workerKey]
if !hasHistory {
executionHistory = ExecutionHistoryStub{}
}

showTestRun := req.URL.Query().Get("showTestRun") == "true"

newHistory := make(ExecutionHistoryStub, 0, len(executionHistory))
for _, entry := range executionHistory {
if entry.TestRun {
if showTestRun {
newHistory = append(newHistory, entry)
}
} else {
newHistory = append(newHistory, entry)
}
}
executionHistory = newHistory

res.Header().Set("Content-Type", "application/json")

_, err := res.Write([]byte(MustJsonMarshal(s.test, executionHistory)))
require.NoError(s.test, err)
}

func (s *ServerStub) handleDelete(res http.ResponseWriter, req *http.Request) {
s.applyDelay()

Expand Down Expand Up @@ -408,9 +497,9 @@ func (s *ServerStub) validateToken(res http.ResponseWriter, req *http.Request) b
}

func (s *ServerStub) validateProjectKey(res http.ResponseWriter, req *http.Request) bool {
if s.projectKey != "" {
if s.projectKey != nil && (len(s.projectKey.paths) == 0 || slices.Contains(s.projectKey.paths, req.URL.Path)) {
gotProjectKey := req.URL.Query().Get("projectKey")
if s.projectKey == gotProjectKey {
if s.projectKey.value == gotProjectKey {
return true
}
res.WriteHeader(http.StatusForbidden)
Expand All @@ -421,13 +510,21 @@ func (s *ServerStub) validateProjectKey(res http.ResponseWriter, req *http.Reque
}

func (s *ServerStub) validateQueryParams(res http.ResponseWriter, req *http.Request) bool {
for key, value := range s.queryParams {
for key, stub := range s.queryParams {
if len(stub.paths) > 0 && !slices.Contains(stub.paths, req.URL.Path) {
// We only check the query param if the path is in the list
continue
}

gotValue := req.URL.Query().Get(key)
if value == gotValue {
return true
if stub.value == gotValue {
continue
}

res.WriteHeader(http.StatusBadRequest)
assert.FailNow(s.test, fmt.Sprintf("Invalid query params %s want=%s, got=%s", key, value, gotValue))

assert.FailNow(s.test, fmt.Sprintf("Invalid query params %s want=%s, got=%s", key, stub, gotValue))

return false
}
return true
Expand Down
63 changes: 63 additions & 0 deletions commands/show_execution_history_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package commands

import (
"net/http"

"github.com/jfrog/jfrog-cli-platform-services/commands/common"

plugins_common "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"

"github.com/jfrog/jfrog-cli-platform-services/model"
)

func GetShowExecutionHistoryCommand() components.Command {
return components.Command{
Name: "execution-history",
Description: "Show a worker execution history.",
Aliases: []string{"exec-hist", "eh"},
Flags: []components.Flag{
plugins_common.GetServerIdFlag(),
model.GetTimeoutFlag(),
model.GetProjectKeyFlag(),
components.NewBoolFlag(
"with-test-runs",
"Whether to include test-runs entries.",
components.WithBoolDefaultValue(false),
),
},
Arguments: []components.Argument{
model.GetWorkerKeyArgument(),
},
Action: func(c *components.Context) error {
workerKey, projectKey, err := common.ExtractProjectAndKeyFromCommandContext(c, c.Arguments, 1, false)
if err != nil {
return err
}

server, err := model.GetServerDetails(c)
if err != nil {
return err
}

query := map[string]string{
"workerKey": workerKey,
}

if c.GetBoolFlagValue("with-test-runs") {
query["showTestRun"] = "true"
}

return common.CallWorkerApi(c, common.ApiCallParams{
Method: http.MethodGet,
ServerUrl: server.GetUrl(),
ServerToken: server.GetAccessToken(),
OkStatuses: []int{http.StatusOK},
ProjectKey: projectKey,
Query: query,
Path: []string{"execution_history"},
OnContent: common.PrintJson,
})
},
}
}
Loading
Loading