Skip to content

Commit bb11799

Browse files
BlasttSehl-jf
authored andcommitted
WKS-1322 - Add version flags + usage of version 2 of API for the deploy command
Add check from server options to validate version length. Usage of V2 in test for create/update endpoints. Add test for options endpoint
1 parent 40bd99e commit bb11799

File tree

12 files changed

+260
-32
lines changed

12 files changed

+260
-32
lines changed

commands/common/cmd_worker_api.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
// FetchWorkerDetails Fetch a worker by its name. Returns nil if the worker does not exist (statusCode=404). Any other statusCode other than 200 will result as an error.
1313
func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken string, workerKey string, projectKey string) (*model.WorkerDetails, error) {
1414
details := new(model.WorkerDetails)
15-
1615
err := CallWorkerApi(c, ApiCallParams{
1716
Method: http.MethodGet,
1817
ServerUrl: serverUrl,
@@ -22,9 +21,9 @@ func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken s
2221
Path: []string{"workers", workerKey},
2322
OnContent: func(content []byte) error {
2423
if len(content) == 0 {
25-
log.Debug("No worker details returned from the server")
2624
return nil
2725
}
26+
log.Info(fmt.Sprintf("Worker %s details returned from the server", details.Key))
2827
return json.Unmarshal(content, details)
2928
},
3029
})
@@ -33,9 +32,9 @@ func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken s
3332
}
3433

3534
if details.Key == "" {
35+
log.Info(fmt.Sprintf("Worker %s does not exist", workerKey))
3636
return nil, nil
3737
}
38-
3938
return details, nil
4039
}
4140

@@ -64,3 +63,27 @@ func FetchActions(c model.IntFlagProvider, serverUrl string, accessToken string,
6463

6564
return metadata, nil
6665
}
66+
67+
func FetchOptions(c model.IntFlagProvider, serverUrl string, accessToken string) (*OptionsMetadata, error) {
68+
metadata := new(OptionsMetadata)
69+
70+
err := CallWorkerApi(c, ApiCallParams{
71+
Method: http.MethodGet,
72+
ServerUrl: serverUrl,
73+
ServerToken: accessToken,
74+
OkStatuses: []int{http.StatusOK},
75+
ApiVersion: ApiVersionV1,
76+
Path: []string{"options"},
77+
OnContent: func(content []byte) error {
78+
if len(content) == 0 {
79+
log.Debug("No options returned from the server")
80+
return nil
81+
}
82+
return json.Unmarshal(content, &metadata)
83+
},
84+
})
85+
if err != nil {
86+
return nil, fmt.Errorf("cannot fetch options: %w", err)
87+
}
88+
return metadata, nil
89+
}

commands/common/cmd_worker_api_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,11 @@ func TestFetchActions(t *testing.T) {
105105
})
106106
}
107107
}
108+
109+
func TestFetchOptions(t *testing.T) {
110+
samples := LoadSampleOptions(t)
111+
s, token := NewMockWorkerServer(t, NewServerStub(t).WithOptionsEndpoint().WithT(t))
112+
got, err := FetchOptions(IntFlagMap{}, s.BaseUrl(), token)
113+
require.NoError(t, err)
114+
assert.Equal(t, got, samples)
115+
}

commands/common/options.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package common
2+
3+
type EditionOptions struct {
4+
MaxCodeChars int `json:"maxCodeChars"`
5+
MaxVersionNumberChars int `json:"maxVersionNumberChars"`
6+
MaxVersionCommitShaChars int `json:"maxVersionCommitShaChars"`
7+
MaxVersionDescriptionChars int `json:"maxVersionDescriptionChars"`
8+
}
9+
10+
type OptionsMetadata struct {
11+
Edition EditionOptions `json:"edition"`
12+
MinArtifactoryVersionForProjectSupport string `json:"minArtifactoryVersionForProjectSupport"`
13+
IsTutorialAvailable bool `json:"isTutorialAvailable"`
14+
IsFeedbackEnabled bool `json:"isFeedbackEnabled"`
15+
IsHistoryEnabled bool `json:"isHistoryEnabled"`
16+
}

commands/common/test_commons.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ type Test interface {
3030

3131
const SecretPassword = "P@ssw0rd!"
3232

33-
//go:embed testdata/actions/*
34-
var sampleActions embed.FS
33+
//go:embed testdata/*
34+
var sampleFiles embed.FS
3535

3636
func SetCliIn(reader io.Reader) {
3737
cliIn = reader
@@ -159,11 +159,11 @@ func MustEncryptSecret(t require.TestingT, secretValue string, password ...strin
159159
func LoadSampleActions(t require.TestingT) ActionsMetadata {
160160
var metadata ActionsMetadata
161161

162-
actionsFiles, err := sampleActions.ReadDir("testdata/actions")
162+
actionsFiles, err := sampleFiles.ReadDir("testdata/actions")
163163
require.NoError(t, err)
164164

165165
for _, file := range actionsFiles {
166-
content, err := sampleActions.ReadFile("testdata/actions/" + file.Name())
166+
content, err := sampleFiles.ReadFile("testdata/actions/" + file.Name())
167167
require.NoError(t, err)
168168

169169
action := &model.ActionMetadata{}
@@ -187,6 +187,18 @@ func LoadSampleActionEvents(t require.TestingT) []string {
187187
return events
188188
}
189189

190+
func LoadSampleOptions(t require.TestingT) *OptionsMetadata {
191+
// var metadata OptionsMetadata
192+
193+
content, err := sampleFiles.ReadFile("testdata/options.json")
194+
require.NoError(t, err)
195+
options := &OptionsMetadata{}
196+
err = json.Unmarshal(content, options)
197+
require.NoError(t, err)
198+
199+
return options
200+
}
201+
190202
func TestSetEnv(t Test, key, value string) {
191203
err := os.Setenv(key, value)
192204
require.NoError(t, err)

commands/common/test_worker_server.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ const (
2626

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

29+
type workerDeployPayload struct {
30+
Key string `json:"key"`
31+
Description string `json:"description"`
32+
Enabled bool `json:"enabled"`
33+
Debug bool `json:"debug"`
34+
SourceCode string `json:"sourceCode"`
35+
Action model.Action `json:"action"`
36+
FilterCriteria *model.FilterCriteria `json:"filterCriteria,omitempty"`
37+
Secrets []*model.Secret `json:"secrets"`
38+
ProjectKey string `json:"projectKey"`
39+
Version *model.Version `json:"version,omitempty"`
40+
}
41+
2942
func ValidateJson(expected any) BodyValidator {
3043
return ValidateJsonFunc(expected, func(in any) any {
3144
return in
@@ -150,7 +163,7 @@ func (s *ServerStub) WithCreateEndpoint(validateBody BodyValidator) *ServerStub
150163
s.endpoints = append(s.endpoints,
151164
mockhttp.NewServerEndpoint().
152165
When(
153-
mockhttp.Request().POST("/worker/api/v1/workers"),
166+
mockhttp.Request().POST("/worker/api/v2/workers"),
154167
).
155168
HandleWith(s.handleSave(http.StatusCreated, validateBody)),
156169
)
@@ -176,7 +189,7 @@ func (s *ServerStub) WithUpdateEndpoint(validateBody BodyValidator) *ServerStub
176189
s.endpoints = append(s.endpoints,
177190
mockhttp.NewServerEndpoint().
178191
When(
179-
mockhttp.Request().PUT("/worker/api/v1/workers"),
192+
mockhttp.Request().PUT("/worker/api/v2/workers"),
180193
).
181194
HandleWith(s.handleSave(http.StatusNoContent, validateBody)),
182195
)
@@ -269,6 +282,17 @@ func (s *ServerStub) WithGetExecutionHistoryEndpoint() *ServerStub {
269282
return s
270283
}
271284

285+
func (s *ServerStub) WithOptionsEndpoint() *ServerStub {
286+
s.endpoints = append(s.endpoints,
287+
mockhttp.NewServerEndpoint().
288+
When(
289+
mockhttp.Request().GET("/worker/api/v1/options"),
290+
).
291+
HandleWith(s.handleGetOptions),
292+
)
293+
return s
294+
}
295+
272296
func (s *ServerStub) handleGetAll(res http.ResponseWriter, req *http.Request) {
273297
s.applyDelay()
274298

@@ -419,10 +443,12 @@ func (s *ServerStub) handleSave(status int, validateBody BodyValidator) http.Han
419443
validateBody(s.test, content)
420444
}
421445

422-
workerDetails := &model.WorkerDetails{}
423-
err = json.Unmarshal(content, workerDetails)
446+
worker := workerDeployPayload{}
447+
448+
err = json.Unmarshal(content, &worker)
424449
require.NoError(s.test, err)
425450

451+
workerDetails := mapWorkerSentToWorkerDetails(worker)
426452
s.workers[workerDetails.Key] = workerDetails
427453

428454
res.WriteHeader(status)
@@ -456,6 +482,20 @@ func (s *ServerStub) handleGetAllMetadata(metadata ActionsMetadata) http.Handler
456482
}
457483
}
458484

485+
func (s *ServerStub) handleGetOptions(res http.ResponseWriter, req *http.Request) {
486+
s.applyDelay()
487+
488+
if !s.validateToken(res, req) {
489+
return
490+
}
491+
492+
res.WriteHeader(http.StatusOK)
493+
494+
options := LoadSampleOptions(s.test)
495+
_, err := res.Write([]byte(MustJsonMarshal(s.test, options)))
496+
require.NoError(s.test, err)
497+
}
498+
459499
func (s *ServerStub) handle(status int, validateBody BodyValidator, responseBody any) http.HandlerFunc {
460500
return func(res http.ResponseWriter, req *http.Request) {
461501
s.applyDelay()
@@ -543,3 +583,17 @@ func (s *ServerStub) applyDelay() {
543583
time.Sleep(s.waitFor)
544584
}
545585
}
586+
587+
func mapWorkerSentToWorkerDetails(workerSent workerDeployPayload) *model.WorkerDetails {
588+
return &model.WorkerDetails{
589+
Key: workerSent.Key,
590+
Description: workerSent.Description,
591+
Enabled: workerSent.Enabled,
592+
Debug: workerSent.Debug,
593+
SourceCode: workerSent.SourceCode,
594+
Action: workerSent.Action.Name, // Map Action.Name
595+
FilterCriteria: workerSent.FilterCriteria,
596+
Secrets: workerSent.Secrets,
597+
ProjectKey: workerSent.ProjectKey,
598+
}
599+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"edition": {
3+
"maxCodeChars": 100000,
4+
"maxVersionNumberChars": 64,
5+
"maxVersionCommitShaChars": 128,
6+
"maxVersionDescriptionChars": 256
7+
},
8+
"minArtifactoryVersionForProjectSupport": "7.95.0",
9+
"isTutorialAvailable": false,
10+
"isFeedbackEnabled": false,
11+
"isHistoryEnabled": true
12+
}

commands/common/version.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/jfrog/jfrog-cli-platform-services/model"
7+
)
8+
9+
func ValidateVersion(version *model.Version, options *OptionsMetadata) error {
10+
if len(version.Number) > options.Edition.MaxVersionNumberChars {
11+
return fmt.Errorf("version number exceeds maximum length of %d characters", options.Edition.MaxVersionNumberChars)
12+
}
13+
if len(version.CommitSha) > options.Edition.MaxVersionCommitShaChars {
14+
return fmt.Errorf("commit sha exceeds maximum length of %d characters", options.Edition.MaxVersionCommitShaChars)
15+
}
16+
if len(version.Description) > options.Edition.MaxVersionDescriptionChars {
17+
return fmt.Errorf("description exceeds maximum length of %d characters", options.Edition.MaxVersionDescriptionChars)
18+
}
19+
return nil
20+
}

commands/deploy_cmd.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ type deployRequest struct {
2020
Enabled bool `json:"enabled"`
2121
Debug bool `json:"debug"`
2222
SourceCode string `json:"sourceCode"`
23-
Action string `json:"action"`
23+
Action model.Action `json:"action"`
2424
FilterCriteria *model.FilterCriteria `json:"filterCriteria,omitempty"`
2525
Secrets []*model.Secret `json:"secrets"`
2626
ProjectKey string `json:"projectKey"`
27+
Version *model.Version `json:"version,omitempty"`
2728
}
2829

2930
func GetDeployCommand() components.Command {
@@ -35,6 +36,9 @@ func GetDeployCommand() components.Command {
3536
plugins_common.GetServerIdFlag(),
3637
model.GetTimeoutFlag(),
3738
model.GetNoSecretsFlag(),
39+
model.GetChangesVersionFlag(),
40+
model.GetChangesDescriptionFlag(),
41+
model.GetChangesCommitShaFlag(),
3842
},
3943
Action: func(c *components.Context) error {
4044
server, err := model.GetServerDetails(c)
@@ -71,18 +75,32 @@ func GetDeployCommand() components.Command {
7175
}
7276
}
7377

74-
return runDeployCommand(c, manifest, actionMeta, server.GetUrl(), server.GetAccessToken())
78+
version := &model.Version{
79+
Number: c.GetStringFlagValue(model.FlagChangesVersion),
80+
Description: c.GetStringFlagValue(model.FlagChangesDescription),
81+
CommitSha: c.GetStringFlagValue(model.FlagChangesCommitSha),
82+
}
83+
if !version.IsEmpty() {
84+
options, err := common.FetchOptions(c, server.GetUrl(), server.GetAccessToken())
85+
if err != nil {
86+
return err
87+
}
88+
if err = common.ValidateVersion(version, options); err != nil {
89+
return err
90+
}
91+
}
92+
return runDeployCommand(c, manifest, actionMeta, version, server.GetUrl(), server.GetAccessToken())
7593
},
7694
}
7795
}
7896

79-
func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, serverUrl string, token string) error {
97+
func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, version *model.Version, serverUrl string, token string) error {
8098
existingWorker, err := common.FetchWorkerDetails(ctx, serverUrl, token, manifest.Name, manifest.ProjectKey)
8199
if err != nil {
82100
return err
83101
}
84102

85-
body, err := prepareDeployRequest(ctx, manifest, actionMeta, existingWorker)
103+
body, err := prepareDeployRequest(ctx, manifest, actionMeta, version, existingWorker)
86104
if err != nil {
87105
return err
88106
}
@@ -101,6 +119,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
101119
Body: bodyBytes,
102120
OkStatuses: []int{http.StatusCreated},
103121
Path: []string{"workers"},
122+
ApiVersion: common.ApiVersionV2,
104123
})
105124
if err == nil {
106125
log.Info(fmt.Sprintf("Worker '%s' deployed", manifest.Name))
@@ -116,6 +135,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
116135
Body: bodyBytes,
117136
OkStatuses: []int{http.StatusNoContent},
118137
Path: []string{"workers"},
138+
ApiVersion: common.ApiVersionV2,
119139
})
120140
if err == nil {
121141
log.Info(fmt.Sprintf("Worker '%s' updated", manifest.Name))
@@ -124,7 +144,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
124144
return err
125145
}
126146

127-
func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, existingWorker *model.WorkerDetails) (*deployRequest, error) {
147+
func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, version *model.Version, existingWorker *model.WorkerDetails) (*deployRequest, error) {
128148
sourceCode, err := common.ReadSourceCode(manifest)
129149
if err != nil {
130150
return nil, err
@@ -136,21 +156,20 @@ func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, act
136156
if !ctx.GetBoolFlagValue(model.FlagNoSecrets) {
137157
secrets = common.PrepareSecretsUpdate(manifest, existingWorker)
138158
}
139-
140159
payload := &deployRequest{
141160
Key: manifest.Name,
142-
Action: manifest.Action,
161+
Action: actionMeta.Action,
143162
Description: manifest.Description,
144163
Enabled: manifest.Enabled,
145164
Debug: manifest.Debug,
146165
SourceCode: sourceCode,
147166
Secrets: secrets,
148167
ProjectKey: manifest.ProjectKey,
168+
Version: version,
149169
}
150170

151171
if actionMeta.MandatoryFilter {
152172
payload.FilterCriteria = manifest.FilterCriteria
153173
}
154-
155174
return payload, nil
156175
}

0 commit comments

Comments
 (0)