diff --git a/apptrust/app/context.go b/apptrust/app/context.go index 44e86c7..d3e4fb6 100644 --- a/apptrust/app/context.go +++ b/apptrust/app/context.go @@ -2,6 +2,7 @@ package app import ( "github.com/jfrog/jfrog-cli-application/apptrust/service/applications" + "github.com/jfrog/jfrog-cli-application/apptrust/service/packages" "github.com/jfrog/jfrog-cli-application/apptrust/service/systems" "github.com/jfrog/jfrog-cli-application/apptrust/service/versions" ) @@ -9,6 +10,7 @@ import ( type Context interface { GetApplicationService() applications.ApplicationService GetVersionService() versions.VersionService + GetPackageService() packages.PackageService GetSystemService() systems.SystemService GetConfig() interface{} } @@ -16,6 +18,7 @@ type Context interface { type context struct { applicationService applications.ApplicationService versionService versions.VersionService + packageService packages.PackageService systemService systems.SystemService } @@ -23,6 +26,7 @@ func NewAppContext() Context { return &context{ applicationService: applications.NewApplicationService(), versionService: versions.NewVersionService(), + packageService: packages.NewPackageService(), systemService: systems.NewSystemService(), } } @@ -35,6 +39,10 @@ func (c *context) GetVersionService() versions.VersionService { return c.versionService } +func (c *context) GetPackageService() packages.PackageService { + return c.packageService +} + func (c *context) GetSystemService() systems.SystemService { return c.systemService } diff --git a/apptrust/commands/flags.go b/apptrust/commands/flags.go index 6b96082..cfea069 100644 --- a/apptrust/commands/flags.go +++ b/apptrust/commands/flags.go @@ -12,6 +12,8 @@ const ( CreateAppVersion = "version-create" PromoteAppVersion = "version-promote" DeleteAppVersion = "version-delete" + PackageBind = "package-bind" + PackageUnbind = "package-unbind" CreateApp = "app-create" UpdateApp = "app-update" DeleteApp = "app-delete" @@ -98,6 +100,20 @@ var commandFlags = map[string][]string{ serverId, }, + PackageBind: { + url, + user, + accessToken, + serverId, + }, + + PackageUnbind: { + url, + user, + accessToken, + serverId, + }, + Ping: { url, user, @@ -139,6 +155,13 @@ var commandFlags = map[string][]string{ SpecFlag, SpecVarsFlag, }, + + DeleteApp: { + url, + user, + accessToken, + serverId, + }, } func GetCommandFlags(cmdKey string) []components.Flag { diff --git a/apptrust/commands/package/bind_package_cmd.go b/apptrust/commands/package/bind_package_cmd.go new file mode 100644 index 0000000..718eaea --- /dev/null +++ b/apptrust/commands/package/bind_package_cmd.go @@ -0,0 +1,85 @@ +package packagecmds + +import ( + "github.com/jfrog/jfrog-cli-application/apptrust/app" + "github.com/jfrog/jfrog-cli-application/apptrust/commands" + "github.com/jfrog/jfrog-cli-application/apptrust/commands/utils" + "github.com/jfrog/jfrog-cli-application/apptrust/common" + "github.com/jfrog/jfrog-cli-application/apptrust/model" + "github.com/jfrog/jfrog-cli-application/apptrust/service" + "github.com/jfrog/jfrog-cli-application/apptrust/service/packages" + commonCLiCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands" + pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" +) + +type bindPackageCommand struct { + packageService packages.PackageService + serverDetails *coreConfig.ServerDetails + requestPayload *model.BindPackageRequest +} + +func (bp *bindPackageCommand) Run() error { + ctx, err := service.NewContext(*bp.serverDetails) + if err != nil { + return err + } + return bp.packageService.BindPackage(ctx, bp.requestPayload) +} + +func (bp *bindPackageCommand) ServerDetails() (*coreConfig.ServerDetails, error) { + return bp.serverDetails, nil +} + +func (bp *bindPackageCommand) CommandName() string { + return commands.PackageBind +} + +func (bp *bindPackageCommand) prepareAndRunCommand(ctx *components.Context) error { + if len(ctx.Arguments) != 4 { + return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) + } + + var err error + bp.serverDetails, err = utils.ServerDetailsByFlags(ctx) + if err != nil { + return err + } + bp.requestPayload, err = BuildPackageRequestPayload(ctx) + if err != nil { + return err + } + + return commonCLiCommands.Exec(bp) +} + +func GetBindPackageCommand(appContext app.Context) components.Command { + cmd := &bindPackageCommand{packageService: appContext.GetPackageService()} + return components.Command{ + Name: commands.PackageBind, + Description: "Bind packages to an application", + Category: common.CategoryPackage, + Aliases: []string{"pb"}, + Arguments: []components.Argument{ + { + Name: "application-key", + Description: "The key of the application to bind the package to.", + }, + { + Name: "package-type", + Description: "Package type (e.g., npm, docker, maven, generic).", + }, + { + Name: "package-name", + Description: "Package name.", + }, + { + Name: "package-versions", + Description: "Comma-separated versions of the package to bind (e.g., '1.0.0,1.1.0,1.2.0').", + }, + }, + Flags: commands.GetCommandFlags(commands.PackageBind), + Action: cmd.prepareAndRunCommand, + } +} diff --git a/apptrust/commands/package/bind_package_cmd_test.go b/apptrust/commands/package/bind_package_cmd_test.go new file mode 100644 index 0000000..f1a1865 --- /dev/null +++ b/apptrust/commands/package/bind_package_cmd_test.go @@ -0,0 +1,65 @@ +package packagecmds + +import ( + "errors" + "testing" + + "github.com/jfrog/jfrog-cli-application/apptrust/model" + mockpackages "github.com/jfrog/jfrog-cli-application/apptrust/service/packages/mocks" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestBindPackageCommand_Run(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + serverDetails := &config.ServerDetails{Url: "https://example.com"} + requestPayload := &model.BindPackageRequest{ + ApplicationKey: "app-key", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + } + + mockPackageService := mockpackages.NewMockPackageService(ctrl) + mockPackageService.EXPECT().BindPackage(gomock.Any(), requestPayload). + Return(nil).Times(1) + + cmd := &bindPackageCommand{ + packageService: mockPackageService, + serverDetails: serverDetails, + requestPayload: requestPayload, + } + + err := cmd.Run() + assert.NoError(t, err) +} + +func TestBindPackageCommand_Run_Error(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + serverDetails := &config.ServerDetails{Url: "https://example.com"} + requestPayload := &model.BindPackageRequest{ + ApplicationKey: "app-key", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + } + + mockPackageService := mockpackages.NewMockPackageService(ctrl) + mockPackageService.EXPECT().BindPackage(gomock.Any(), requestPayload). + Return(errors.New("bind error")).Times(1) + + cmd := &bindPackageCommand{ + packageService: mockPackageService, + serverDetails: serverDetails, + requestPayload: requestPayload, + } + + err := cmd.Run() + assert.Error(t, err) + assert.Equal(t, "bind error", err.Error()) +} diff --git a/apptrust/commands/package/unbind_package_cmd.go b/apptrust/commands/package/unbind_package_cmd.go new file mode 100644 index 0000000..79d39ce --- /dev/null +++ b/apptrust/commands/package/unbind_package_cmd.go @@ -0,0 +1,85 @@ +package packagecmds + +import ( + "github.com/jfrog/jfrog-cli-application/apptrust/app" + "github.com/jfrog/jfrog-cli-application/apptrust/commands" + "github.com/jfrog/jfrog-cli-application/apptrust/commands/utils" + "github.com/jfrog/jfrog-cli-application/apptrust/common" + "github.com/jfrog/jfrog-cli-application/apptrust/model" + "github.com/jfrog/jfrog-cli-application/apptrust/service" + "github.com/jfrog/jfrog-cli-application/apptrust/service/packages" + commonCLiCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands" + pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" +) + +type unbindPackageCommand struct { + packageService packages.PackageService + serverDetails *coreConfig.ServerDetails + requestPayload *model.BindPackageRequest +} + +func (up *unbindPackageCommand) Run() error { + ctx, err := service.NewContext(*up.serverDetails) + if err != nil { + return err + } + return up.packageService.UnbindPackage(ctx, up.requestPayload) +} + +func (up *unbindPackageCommand) ServerDetails() (*coreConfig.ServerDetails, error) { + return up.serverDetails, nil +} + +func (up *unbindPackageCommand) CommandName() string { + return commands.PackageUnbind +} + +func (up *unbindPackageCommand) prepareAndRunCommand(ctx *components.Context) error { + if len(ctx.Arguments) < 3 || len(ctx.Arguments) > 4 { + return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) + } + + var err error + up.serverDetails, err = utils.ServerDetailsByFlags(ctx) + if err != nil { + return err + } + up.requestPayload, err = BuildPackageRequestPayload(ctx) + if err != nil { + return err + } + + return commonCLiCommands.Exec(up) +} + +func GetUnbindPackageCommand(appContext app.Context) components.Command { + cmd := &unbindPackageCommand{packageService: appContext.GetPackageService()} + return components.Command{ + Name: commands.PackageUnbind, + Description: "Unbind packages from an application", + Category: common.CategoryPackage, + Aliases: []string{"pu"}, + Arguments: []components.Argument{ + { + Name: "application-key", + Description: "The key of the application to unbind the package from.", + }, + { + Name: "package-type", + Description: "Package type (e.g., npm, docker, maven, generic).", + }, + { + Name: "package-name", + Description: "Package name.", + }, + { + Name: "package-versions", + Description: "Comma-separated versions of the package to unbind (e.g., '1.0.0,1.1.0,1.2.0'). If omitted, all versions will be unbound.", + }, + }, + Flags: commands.GetCommandFlags(commands.PackageUnbind), + Action: cmd.prepareAndRunCommand, + } +} diff --git a/apptrust/commands/package/unbind_package_cmd_test.go b/apptrust/commands/package/unbind_package_cmd_test.go new file mode 100644 index 0000000..c61b45a --- /dev/null +++ b/apptrust/commands/package/unbind_package_cmd_test.go @@ -0,0 +1,65 @@ +package packagecmds + +import ( + "errors" + "testing" + + "github.com/jfrog/jfrog-cli-application/apptrust/model" + mockpackages "github.com/jfrog/jfrog-cli-application/apptrust/service/packages/mocks" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestUnbindPackageCommand_Run(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + serverDetails := &config.ServerDetails{Url: "https://example.com"} + requestPayload := &model.BindPackageRequest{ + ApplicationKey: "app-key", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + } + + mockPackageService := mockpackages.NewMockPackageService(ctrl) + mockPackageService.EXPECT().UnbindPackage(gomock.Any(), requestPayload). + Return(nil).Times(1) + + cmd := &unbindPackageCommand{ + packageService: mockPackageService, + serverDetails: serverDetails, + requestPayload: requestPayload, + } + + err := cmd.Run() + assert.NoError(t, err) +} + +func TestUnbindPackageCommand_Run_Error(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + serverDetails := &config.ServerDetails{Url: "https://example.com"} + requestPayload := &model.BindPackageRequest{ + ApplicationKey: "app-key", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + } + + mockPackageService := mockpackages.NewMockPackageService(ctrl) + mockPackageService.EXPECT().UnbindPackage(gomock.Any(), requestPayload). + Return(errors.New("unbind error")).Times(1) + + cmd := &unbindPackageCommand{ + packageService: mockPackageService, + serverDetails: serverDetails, + requestPayload: requestPayload, + } + + err := cmd.Run() + assert.Error(t, err) + assert.Equal(t, "unbind error", err.Error()) +} diff --git a/apptrust/commands/package/utils.go b/apptrust/commands/package/utils.go new file mode 100644 index 0000000..1b5089c --- /dev/null +++ b/apptrust/commands/package/utils.go @@ -0,0 +1,44 @@ +package packagecmds + +import ( + "strings" + + "github.com/jfrog/jfrog-cli-application/apptrust/model" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" +) + +// BuildPackageRequestPayload creates a BindPackageRequest from command arguments. +// This function is shared between bind and unbind package commands. +// It expects the following arguments in order: []. +func BuildPackageRequestPayload(ctx *components.Context) (*model.BindPackageRequest, error) { + applicationKey := ctx.Arguments[0] + packageType := ctx.Arguments[1] + packageName := ctx.Arguments[2] + + var versions []string + if len(ctx.Arguments) > 3 { + // Parse comma-separated versions + versions = parseVersions(ctx.Arguments[3]) + } + + return &model.BindPackageRequest{ + ApplicationKey: applicationKey, + Type: packageType, + Name: packageName, + Versions: versions, + }, nil +} + +// parseVersions parses a comma-separated string of versions into a slice. +// It trims whitespaces from each version and filters out empty strings. +func parseVersions(versionsString string) []string { + parts := strings.Split(versionsString, ",") + var versions []string + for _, part := range parts { + trimmed := strings.TrimSpace(part) + if trimmed != "" { + versions = append(versions, trimmed) + } + } + return versions +} diff --git a/apptrust/commands/package/utils_test.go b/apptrust/commands/package/utils_test.go new file mode 100644 index 0000000..3c78935 --- /dev/null +++ b/apptrust/commands/package/utils_test.go @@ -0,0 +1,61 @@ +package packagecmds + +import ( + "testing" + + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" + "github.com/stretchr/testify/assert" +) + +func TestBuildPackageRequestPayload(t *testing.T) { + tests := []struct { + name string + arguments []string + expectedKey string + expectedType string + expectedName string + expectedVersions []string + }{ + { + name: "Valid package request with single version", + arguments: []string{"my-app", "npm", "my-package", "1.0.0"}, + expectedKey: "my-app", + expectedType: "npm", + expectedName: "my-package", + expectedVersions: []string{"1.0.0"}, + }, + { + name: "Valid package request with multiple versions", + arguments: []string{"my-app", "npm", "my-package", "1.0.0,1.1.0 , 1.2.0"}, + expectedKey: "my-app", + expectedType: "npm", + expectedName: "my-package", + expectedVersions: []string{"1.0.0", "1.1.0", "1.2.0"}, + }, + { + name: "Package request without versions", + arguments: []string{"my-app", "npm", "my-package"}, + expectedKey: "my-app", + expectedType: "npm", + expectedName: "my-package", + expectedVersions: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := &components.Context{ + Arguments: tt.arguments, + } + + result, err := BuildPackageRequestPayload(ctx) + + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, tt.expectedKey, result.ApplicationKey) + assert.Equal(t, tt.expectedType, result.Type) + assert.Equal(t, tt.expectedName, result.Name) + assert.Equal(t, tt.expectedVersions, result.Versions) + }) + } +} diff --git a/apptrust/commands/version/create_app_version_cmd.go b/apptrust/commands/version/create_app_version_cmd.go index fb25775..1437b89 100644 --- a/apptrust/commands/version/create_app_version_cmd.go +++ b/apptrust/commands/version/create_app_version_cmd.go @@ -110,9 +110,6 @@ func loadPackagesFromSpec(ctx *components.Context, packages *[]model.CreateVersi } func validateCreateAppVersionContext(ctx *components.Context) error { - if show, err := pluginsCommon.ShowCmdHelpIfNeeded(ctx, ctx.Arguments); show || err != nil { - return err - } if len(ctx.Arguments) != 1 { return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) } diff --git a/apptrust/commands/version/promote_app_version_cmd.go b/apptrust/commands/version/promote_app_version_cmd.go index 6123716..909d20d 100644 --- a/apptrust/commands/version/promote_app_version_cmd.go +++ b/apptrust/commands/version/promote_app_version_cmd.go @@ -41,8 +41,8 @@ func (pv *promoteAppVersionCommand) CommandName() string { } func (pv *promoteAppVersionCommand) prepareAndRunCommand(ctx *components.Context) error { - if err := validatePromoteAppVersionContext(ctx); err != nil { - return err + if len(ctx.Arguments) != 1 { + return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) } serverDetails, err := utils.ServerDetailsByFlags(ctx) if err != nil { @@ -56,16 +56,6 @@ func (pv *promoteAppVersionCommand) prepareAndRunCommand(ctx *components.Context return commonCLiCommands.Exec(pv) } -func validatePromoteAppVersionContext(ctx *components.Context) error { - if show, err := pluginsCommon.ShowCmdHelpIfNeeded(ctx, ctx.Arguments); show || err != nil { - return err - } - if len(ctx.Arguments) != 1 { - return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) - } - return nil -} - func (pv *promoteAppVersionCommand) buildRequestPayload(ctx *components.Context) (*model.PromoteAppVersionRequest, error) { return &model.PromoteAppVersionRequest{ ApplicationKey: ctx.GetStringFlagValue(commands.ApplicationKeyFlag), diff --git a/apptrust/common/categories.go b/apptrust/common/categories.go index 9a30756..1825963 100644 --- a/apptrust/common/categories.go +++ b/apptrust/common/categories.go @@ -2,6 +2,7 @@ package common const ( CategorySystem = "system" - CategoryVersion = "version" CategoryApplication = "application" + CategoryVersion = "version" + CategoryPackage = "package" ) diff --git a/apptrust/http/http_client.go b/apptrust/http/http_client.go index 2f36e63..1356244 100644 --- a/apptrust/http/http_client.go +++ b/apptrust/http/http_client.go @@ -26,7 +26,7 @@ type ApptrustHttpClient interface { Post(path string, requestBody interface{}) (resp *http.Response, body []byte, err error) Get(path string) (resp *http.Response, body []byte, err error) Patch(path string, requestBody interface{}) (resp *http.Response, body []byte, err error) - Delete(path string) (resp *http.Response, body []byte, err error) + Delete(path string, requestBody interface{}) (resp *http.Response, body []byte, err error) } type apptrustHttpClient struct { @@ -139,14 +139,22 @@ func (c *apptrustHttpClient) toJsonBytes(payload interface{}) ([]byte, error) { return jsonBytes, nil } -func (c *apptrustHttpClient) Delete(path string) (resp *http.Response, body []byte, err error) { +func (c *apptrustHttpClient) Delete(path string, requestBody interface{}) (resp *http.Response, body []byte, err error) { url, err := utils.BuildUrl(c.serverDetails.Url, apptrustApiPath+path, nil) if err != nil { return nil, nil, err } + var requestContent []byte + if requestBody != nil { + requestContent, err = c.toJsonBytes(requestBody) + if err != nil { + return nil, nil, err + } + } + log.Debug("Sending DELETE request to:", url) - return c.client.SendDelete(url, nil, c.getJsonHttpClientDetails()) + return c.client.SendDelete(url, requestContent, c.getJsonHttpClientDetails()) } func (c *apptrustHttpClient) getJsonHttpClientDetails() *httputils.HttpClientDetails { diff --git a/apptrust/model/bind_package_request.go b/apptrust/model/bind_package_request.go new file mode 100644 index 0000000..fc245c8 --- /dev/null +++ b/apptrust/model/bind_package_request.go @@ -0,0 +1,8 @@ +package model + +type BindPackageRequest struct { + ApplicationKey string `json:"application_key"` + Type string `json:"type"` + Name string `json:"name"` + Versions []string `json:"versions,omitempty"` +} diff --git a/apptrust/service/applications/application_service.go b/apptrust/service/applications/application_service.go index 9be37f6..94a3d77 100644 --- a/apptrust/service/applications/application_service.go +++ b/apptrust/service/applications/application_service.go @@ -57,7 +57,7 @@ func (as *applicationService) UpdateApplication(ctx service.Context, requestBody func (as *applicationService) DeleteApplication(ctx service.Context, applicationKey string) error { endpoint := fmt.Sprintf("/v1/applications/%s", applicationKey) - response, responseBody, err := ctx.GetHttpClient().Delete(endpoint) + response, responseBody, err := ctx.GetHttpClient().Delete(endpoint, nil) if err != nil { return err } diff --git a/apptrust/service/packages/package_service.go b/apptrust/service/packages/package_service.go new file mode 100644 index 0000000..85c80c7 --- /dev/null +++ b/apptrust/service/packages/package_service.go @@ -0,0 +1,52 @@ +package packages + +//go:generate ${PROJECT_DIR}/scripts/mockgen.sh ${GOFILE} + +import ( + "fmt" + "net/http" + + "github.com/jfrog/jfrog-cli-application/apptrust/model" + "github.com/jfrog/jfrog-cli-application/apptrust/service" +) + +type PackageService interface { + BindPackage(ctx service.Context, request *model.BindPackageRequest) error + UnbindPackage(ctx service.Context, request *model.BindPackageRequest) error +} + +type packageService struct{} + +func NewPackageService() PackageService { + return &packageService{} +} + +func (ps *packageService) BindPackage(ctx service.Context, request *model.BindPackageRequest) error { + endpoint := "/v1/package" + response, responseBody, err := ctx.GetHttpClient().Post(endpoint, request) + if err != nil { + return err + } + + if response.StatusCode != http.StatusCreated { + return fmt.Errorf("failed to bind package. Status code: %d.\n%s", + response.StatusCode, responseBody) + } + + return nil +} + +func (ps *packageService) UnbindPackage(ctx service.Context, request *model.BindPackageRequest) error { + endpoint := "/v1/package" + response, responseBody, err := ctx.GetHttpClient().Delete(endpoint, request) + if err != nil { + return err + } + + if response.StatusCode != http.StatusNoContent { + return fmt.Errorf("failed to unbind package. Status code: %d.\n%s", + response.StatusCode, responseBody) + } + + return nil +} diff --git a/apptrust/service/packages/package_service_test.go b/apptrust/service/packages/package_service_test.go new file mode 100644 index 0000000..76196a9 --- /dev/null +++ b/apptrust/service/packages/package_service_test.go @@ -0,0 +1,156 @@ +package packages + +import ( + "errors" + "net/http" + "testing" + + mockhttp "github.com/jfrog/jfrog-cli-application/apptrust/http/mocks" + mockservice "github.com/jfrog/jfrog-cli-application/apptrust/service/mocks" + "go.uber.org/mock/gomock" + + "github.com/jfrog/jfrog-cli-application/apptrust/model" + "github.com/stretchr/testify/assert" +) + +func TestBindPackage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + service := NewPackageService() + + tests := []struct { + name string + request *model.BindPackageRequest + mockResponse *http.Response + mockError error + expectedError string + }{ + { + name: "success", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: &http.Response{StatusCode: 201}, + mockError: nil, + expectedError: "", + }, + { + name: "failed with non-200 status code", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: &http.Response{StatusCode: 400}, + mockError: nil, + expectedError: "failed to bind package. Status code: 400", + }, + { + name: "http client error", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: nil, + mockError: errors.New("http client error"), + expectedError: "http client error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockHttpClient := mockhttp.NewMockApptrustHttpClient(ctrl) + mockHttpClient.EXPECT().Post("/v1/package", tt.request). + Return(tt.mockResponse, []byte(""), tt.mockError).Times(1) + + mockCtx := mockservice.NewMockContext(ctrl) + mockCtx.EXPECT().GetHttpClient().Return(mockHttpClient).Times(1) + + err := service.BindPackage(mockCtx, tt.request) + if tt.expectedError == "" { + assert.NoError(t, err) + } else { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + } + }) + } +} + +func TestUnbindPackage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + service := NewPackageService() + + tests := []struct { + name string + request *model.BindPackageRequest + mockResponse *http.Response + mockError error + expectedError string + }{ + { + name: "success", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: &http.Response{StatusCode: 204}, + mockError: nil, + expectedError: "", + }, + { + name: "failed with non-200 status code", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: &http.Response{StatusCode: 400}, + mockError: nil, + expectedError: "failed to unbind package. Status code: 400", + }, + { + name: "http client error", + request: &model.BindPackageRequest{ + ApplicationKey: "test-app", + Type: "npm", + Name: "test-package", + Versions: []string{"1.0.0"}, + }, + mockResponse: nil, + mockError: errors.New("http client error"), + expectedError: "http client error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockHttpClient := mockhttp.NewMockApptrustHttpClient(ctrl) + mockHttpClient.EXPECT().Delete("/v1/package", tt.request). + Return(tt.mockResponse, []byte(""), tt.mockError).Times(1) + + mockCtx := mockservice.NewMockContext(ctrl) + mockCtx.EXPECT().GetHttpClient().Return(mockHttpClient).Times(1) + + err := service.UnbindPackage(mockCtx, tt.request) + if tt.expectedError == "" { + assert.NoError(t, err) + } else { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + } + }) + } +} diff --git a/apptrust/service/versions/version_service.go b/apptrust/service/versions/version_service.go index 15c8bb6..6b38ed6 100644 --- a/apptrust/service/versions/version_service.go +++ b/apptrust/service/versions/version_service.go @@ -52,7 +52,7 @@ func (vs *versionService) PromoteAppVersion(ctx service.Context, payload *model. func (vs *versionService) DeleteAppVersion(ctx service.Context, applicationKey string, version string) error { url := fmt.Sprintf("/v1/applications/%s/versions/%s", applicationKey, version) - response, responseBody, err := ctx.GetHttpClient().Delete(url) + response, responseBody, err := ctx.GetHttpClient().Delete(url, nil) if err != nil { return err } diff --git a/cli/cli.go b/cli/cli.go index 0757305..25f734d 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -3,6 +3,7 @@ package cli import ( "github.com/jfrog/jfrog-cli-application/apptrust/app" "github.com/jfrog/jfrog-cli-application/apptrust/commands/application" + packagecmds "github.com/jfrog/jfrog-cli-application/apptrust/commands/package" "github.com/jfrog/jfrog-cli-application/apptrust/commands/system" "github.com/jfrog/jfrog-cli-application/apptrust/commands/version" "github.com/jfrog/jfrog-cli-core/v2/plugins/components" @@ -21,6 +22,9 @@ import ( // system.GetPingCommand(appContext), // version.GetCreateAppVersionCommand(appContext), // version.GetPromoteAppVersionCommand(appContext), +// version.GetDeleteAppVersionCommand(appContext), +// packagecmds.GetBindPackageCommand(appContext), +// packagecmds.GetUnbindPackageCommand(appContext), // application.GetCreateAppCommand(appContext), // application.GetUpdateAppCommand(appContext), // application.GetDeleteAppCommand(appContext), @@ -33,7 +37,7 @@ import ( func GetJfrogCliApptrustApp() components.App { appContext := app.NewAppContext() appEntity := components.CreateApp( - "apptrust", + "app", "1.0.5", "JFrog AppTrust CLI", []components.Command{ @@ -41,6 +45,8 @@ func GetJfrogCliApptrustApp() components.App { version.GetCreateAppVersionCommand(appContext), version.GetPromoteAppVersionCommand(appContext), version.GetDeleteAppVersionCommand(appContext), + packagecmds.GetBindPackageCommand(appContext), + packagecmds.GetUnbindPackageCommand(appContext), application.GetCreateAppCommand(appContext), application.GetUpdateAppCommand(appContext), application.GetDeleteAppCommand(appContext),