Skip to content

Commit 0dc80f1

Browse files
committed
Update the version-promote command to align with the new API
1 parent 917b4c7 commit 0dc80f1

File tree

6 files changed

+212
-37
lines changed

6 files changed

+212
-37
lines changed

apptrust/commands/flags.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const (
4242
UserOwnersFlag = "user-owners"
4343
GroupOwnersFlag = "group-owners"
4444
SigningKeyFlag = "signing-key"
45+
SyncFlag = "sync"
46+
PromotionTypeFlag = "promotion-type"
47+
DryRunFlag = "dry-run"
48+
ExcludeReposFlag = "exclude-repos"
49+
IncludeReposFlag = "include-repos"
4550
)
4651

4752
// Flag keys mapped to their corresponding components.Flag definition.
@@ -69,6 +74,11 @@ var flagsMap = map[string]components.Flag{
6974
UserOwnersFlag: components.NewStringFlag(UserOwnersFlag, "Comma-separated list of user owners.", func(f *components.StringFlag) { f.Mandatory = false }),
7075
GroupOwnersFlag: components.NewStringFlag(GroupOwnersFlag, "Comma-separated list of group owners.", func(f *components.StringFlag) { f.Mandatory = false }),
7176
SigningKeyFlag: components.NewStringFlag(SigningKeyFlag, "The GPG/RSA key-pair name given in Artifactory.", func(f *components.StringFlag) { f.Mandatory = false }),
77+
SyncFlag: components.NewBoolFlag(SyncFlag, "Whether to synchronize the operation.", components.WithBoolDefaultValueTrue()),
78+
PromotionTypeFlag: components.NewStringFlag(PromotionTypeFlag, "The promotion type. The following values are supported: "+coreutils.ListToText(model.PromotionTypeValues), func(f *components.StringFlag) { f.Mandatory = false; f.DefaultValue = model.PromotionTypeCopy }),
79+
DryRunFlag: components.NewBoolFlag(DryRunFlag, "Perform a simulation of the operation.", components.WithBoolDefaultValueFalse()),
80+
ExcludeReposFlag: components.NewStringFlag(ExcludeReposFlag, "Semicolon-separated list of repositories to exclude.", func(f *components.StringFlag) { f.Mandatory = false }),
81+
IncludeReposFlag: components.NewStringFlag(IncludeReposFlag, "Semicolon-separated list of repositories to include.", func(f *components.StringFlag) { f.Mandatory = false }),
7282
}
7383

7484
var commandFlags = map[string][]string{
@@ -90,8 +100,11 @@ var commandFlags = map[string][]string{
90100
user,
91101
accessToken,
92102
serverId,
93-
ApplicationKeyFlag,
94-
StageVarsFlag,
103+
SyncFlag,
104+
PromotionTypeFlag,
105+
DryRunFlag,
106+
ExcludeReposFlag,
107+
IncludeReposFlag,
95108
},
96109
DeleteAppVersion: {
97110
url,

apptrust/commands/version/promote_app_version_cmd.go

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import (
2020
type promoteAppVersionCommand struct {
2121
versionService versions.VersionService
2222
serverDetails *coreConfig.ServerDetails
23+
applicationKey string
24+
version string
2325
requestPayload *model.PromoteAppVersionRequest
26+
sync bool
2427
}
2528

2629
func (pv *promoteAppVersionCommand) Run() error {
@@ -29,7 +32,7 @@ func (pv *promoteAppVersionCommand) Run() error {
2932
return err
3033
}
3134

32-
return pv.versionService.PromoteAppVersion(ctx, pv.requestPayload)
35+
return pv.versionService.PromoteAppVersion(ctx, pv.applicationKey, pv.version, pv.requestPayload, pv.sync)
3336
}
3437

3538
func (pv *promoteAppVersionCommand) ServerDetails() (*coreConfig.ServerDetails, error) {
@@ -41,9 +44,17 @@ func (pv *promoteAppVersionCommand) CommandName() string {
4144
}
4245

4346
func (pv *promoteAppVersionCommand) prepareAndRunCommand(ctx *components.Context) error {
44-
if len(ctx.Arguments) != 1 {
47+
if len(ctx.Arguments) != 3 {
4548
return pluginsCommon.WrongNumberOfArgumentsHandler(ctx)
4649
}
50+
51+
// Extract from arguments
52+
pv.applicationKey = ctx.Arguments[0]
53+
pv.version = ctx.Arguments[1]
54+
55+
// Extract sync flag value
56+
pv.sync = ctx.GetBoolTFlagValue(commands.SyncFlag)
57+
4758
serverDetails, err := utils.ServerDetailsByFlags(ctx)
4859
if err != nil {
4960
return err
@@ -57,10 +68,37 @@ func (pv *promoteAppVersionCommand) prepareAndRunCommand(ctx *components.Context
5768
}
5869

5970
func (pv *promoteAppVersionCommand) buildRequestPayload(ctx *components.Context) (*model.PromoteAppVersionRequest, error) {
71+
stage := ctx.Arguments[2]
72+
73+
var includedRepos []string
74+
var excludedRepos []string
75+
76+
if includeReposStr := ctx.GetStringFlagValue(commands.IncludeReposFlag); includeReposStr != "" {
77+
includedRepos = utils.ParseSliceFlag(includeReposStr)
78+
}
79+
80+
if excludeReposStr := ctx.GetStringFlagValue(commands.ExcludeReposFlag); excludeReposStr != "" {
81+
excludedRepos = utils.ParseSliceFlag(excludeReposStr)
82+
}
83+
84+
// Validate promotion type flag
85+
promotionType := ctx.GetStringFlagValue(commands.PromotionTypeFlag)
86+
validatedPromotionType, err := utils.ValidateEnumFlag(commands.PromotionTypeFlag, promotionType, model.PromotionTypeCopy, model.PromotionTypeValues)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
// If dry-run is true, override with dry_run
92+
dryRun := ctx.GetBoolFlagValue(commands.DryRunFlag)
93+
if dryRun {
94+
validatedPromotionType = model.PromotionTypeDryRun
95+
}
96+
6097
return &model.PromoteAppVersionRequest{
61-
ApplicationKey: ctx.GetStringFlagValue(commands.ApplicationKeyFlag),
62-
Version: ctx.Arguments[0],
63-
Environment: ctx.GetStringFlagValue(commands.StageVarsFlag),
98+
Stage: stage,
99+
PromotionType: validatedPromotionType,
100+
IncludedRepositoryKeys: includedRepos,
101+
ExcludedRepositoryKeys: excludedRepos,
64102
}, nil
65103
}
66104

@@ -73,8 +111,18 @@ func GetPromoteAppVersionCommand(appContext app.Context) components.Command {
73111
Aliases: []string{"vp"},
74112
Arguments: []components.Argument{
75113
{
76-
Name: "version-name",
77-
Description: "The name of the version",
114+
Name: "application-key",
115+
Description: "The application key",
116+
Optional: false,
117+
},
118+
{
119+
Name: "version",
120+
Description: "The version to promote",
121+
Optional: false,
122+
},
123+
{
124+
Name: "target-stage",
125+
Description: "The target stage to which the application version should be promoted",
78126
Optional: false,
79127
},
80128
},

apptrust/commands/version/promote_app_version_cmd_test.go

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,100 @@
11
package version
22

33
import (
4+
"errors"
45
"testing"
56

67
"github.com/jfrog/jfrog-cli-application/apptrust/commands"
78
mockversions "github.com/jfrog/jfrog-cli-application/apptrust/service/versions/mocks"
89
"go.uber.org/mock/gomock"
910

1011
"github.com/jfrog/jfrog-cli-application/apptrust/model"
11-
coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
12+
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
1213
"github.com/stretchr/testify/assert"
1314
)
1415

15-
func TestRun(t *testing.T) {
16+
func TestPromoteAppVersionCommand_Run(t *testing.T) {
17+
tests := []struct {
18+
name string
19+
sync bool
20+
}{
21+
{
22+
name: "sync flag true",
23+
sync: true,
24+
},
25+
{
26+
name: "sync flag false",
27+
sync: false,
28+
},
29+
}
30+
31+
for _, tt := range tests {
32+
t.Run(tt.name, func(t *testing.T) {
33+
ctrl := gomock.NewController(t)
34+
defer ctrl.Finish()
35+
36+
serverDetails := &config.ServerDetails{Url: "https://example.com"}
37+
applicationKey := "app-key"
38+
version := "1.0.0"
39+
requestPayload := &model.PromoteAppVersionRequest{
40+
Stage: "prod",
41+
}
42+
43+
mockVersionService := mockversions.NewMockVersionService(ctrl)
44+
mockVersionService.EXPECT().PromoteAppVersion(gomock.Any(), applicationKey, version, requestPayload, tt.sync).
45+
Return(nil).Times(1)
46+
47+
cmd := &promoteAppVersionCommand{
48+
versionService: mockVersionService,
49+
serverDetails: serverDetails,
50+
applicationKey: applicationKey,
51+
version: version,
52+
requestPayload: requestPayload,
53+
sync: tt.sync,
54+
}
55+
56+
err := cmd.Run()
57+
assert.NoError(t, err)
58+
})
59+
}
60+
}
61+
62+
func TestPromoteAppVersionCommand_Run_Error(t *testing.T) {
1663
ctrl := gomock.NewController(t)
1764
defer ctrl.Finish()
1865

19-
serverDetails := &coreconfig.ServerDetails{}
66+
serverDetails := &config.ServerDetails{Url: "https://example.com"}
67+
applicationKey := "app-key"
68+
version := "1.0.0"
2069
requestPayload := &model.PromoteAppVersionRequest{
21-
ApplicationKey: "app",
22-
Version: "1.0.0",
23-
Environment: "env",
70+
Stage: "prod",
2471
}
72+
sync := true
73+
expectedError := errors.New("service error occurred")
2574

2675
mockVersionService := mockversions.NewMockVersionService(ctrl)
27-
mockVersionService.EXPECT().PromoteAppVersion(gomock.Any(), requestPayload).
28-
Return(nil).Times(1)
76+
mockVersionService.EXPECT().PromoteAppVersion(gomock.Any(), applicationKey, version, requestPayload, sync).
77+
Return(expectedError).Times(1)
2978

3079
cmd := &promoteAppVersionCommand{
3180
versionService: mockVersionService,
3281
serverDetails: serverDetails,
82+
applicationKey: applicationKey,
83+
version: version,
3384
requestPayload: requestPayload,
85+
sync: sync,
3486
}
3587

3688
err := cmd.Run()
37-
assert.NoError(t, err)
89+
assert.Error(t, err)
90+
assert.Contains(t, err.Error(), "service error occurred")
3891
}
3992

40-
func TestServerDetails(t *testing.T) {
93+
func TestPromoteAppVersionCommand_ServerDetails(t *testing.T) {
4194
ctrl := gomock.NewController(t)
4295
defer ctrl.Finish()
4396

44-
serverDetails := &coreconfig.ServerDetails{}
97+
serverDetails := &config.ServerDetails{}
4598
cmd := &promoteAppVersionCommand{
4699
serverDetails: serverDetails,
47100
}
@@ -51,7 +104,7 @@ func TestServerDetails(t *testing.T) {
51104
assert.Equal(t, serverDetails, details)
52105
}
53106

54-
func TestCommandName(t *testing.T) {
107+
func TestPromoteAppVersionCommand_CommandName(t *testing.T) {
55108
ctrl := gomock.NewController(t)
56109
defer ctrl.Finish()
57110

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
11
package model
22

3+
const (
4+
PromotionTypeCopy = "copy"
5+
PromotionTypeMove = "move"
6+
7+
// This is not a valid value for the --promotion-type flag, but is passed to the API if the --dry-run flag is set.
8+
PromotionTypeDryRun = "dry_run"
9+
)
10+
11+
var (
12+
PromotionTypeValues = []string{
13+
PromotionTypeCopy,
14+
PromotionTypeMove,
15+
}
16+
)
17+
318
type PromoteAppVersionRequest struct {
4-
ApplicationKey string `json:"application_key"`
5-
Version string `json:"version"`
6-
Environment string `json:"environment"`
19+
Stage string `json:"stage"`
20+
PromotionType string `json:"promotion_type,omitempty"`
21+
IncludedRepositoryKeys []string `json:"included_repository_keys,omitempty"`
22+
ExcludedRepositoryKeys []string `json:"excluded_repository_keys,omitempty"`
723
}

apptrust/service/versions/version_service.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ package versions
44

55
import (
66
"fmt"
7-
87
"github.com/jfrog/jfrog-cli-application/apptrust/service"
8+
"strconv"
99

1010
"github.com/jfrog/jfrog-cli-application/apptrust/model"
1111
)
1212

1313
type VersionService interface {
1414
CreateAppVersion(ctx service.Context, request *model.CreateAppVersionRequest) error
15-
PromoteAppVersion(ctx service.Context, payload *model.PromoteAppVersionRequest) error
15+
PromoteAppVersion(ctx service.Context, applicationKey string, version string, payload *model.PromoteAppVersionRequest, sync bool) error
1616
DeleteAppVersion(ctx service.Context, applicationKey string, version string) error
1717
}
1818

@@ -36,8 +36,9 @@ func (vs *versionService) CreateAppVersion(ctx service.Context, request *model.C
3636
return nil
3737
}
3838

39-
func (vs *versionService) PromoteAppVersion(ctx service.Context, payload *model.PromoteAppVersionRequest) error {
40-
response, responseBody, err := ctx.GetHttpClient().Post("/v1/applications/version/promote", payload)
39+
func (vs *versionService) PromoteAppVersion(ctx service.Context, applicationKey, version string, request *model.PromoteAppVersionRequest, sync bool) error {
40+
endpoint := fmt.Sprintf("/v1/applications/%s/versions/%s/promote?async=%s", applicationKey, version, strconv.FormatBool(!sync))
41+
response, responseBody, err := ctx.GetHttpClient().Post(endpoint, request)
4142
if err != nil {
4243
return err
4344
}
@@ -50,7 +51,7 @@ func (vs *versionService) PromoteAppVersion(ctx service.Context, payload *model.
5051
return nil
5152
}
5253

53-
func (vs *versionService) DeleteAppVersion(ctx service.Context, applicationKey string, version string) error {
54+
func (vs *versionService) DeleteAppVersion(ctx service.Context, applicationKey, version string) error {
5455
url := fmt.Sprintf("/v1/applications/%s/versions/%s", applicationKey, version)
5556
response, responseBody, err := ctx.GetHttpClient().Delete(url, nil)
5657
if err != nil {

0 commit comments

Comments
 (0)