diff --git a/apptrust/commands/package/bind_package_cmd.go b/apptrust/commands/package/bind_package_cmd.go index cbd5047..75ff2e8 100644 --- a/apptrust/commands/package/bind_package_cmd.go +++ b/apptrust/commands/package/bind_package_cmd.go @@ -17,6 +17,7 @@ import ( type bindPackageCommand struct { packageService packages.PackageService serverDetails *coreConfig.ServerDetails + applicationKey string requestPayload *model.BindPackageRequest } @@ -25,7 +26,7 @@ func (bp *bindPackageCommand) Run() error { if err != nil { return err } - return bp.packageService.BindPackage(ctx, bp.requestPayload) + return bp.packageService.BindPackage(ctx, bp.applicationKey, bp.requestPayload) } func (bp *bindPackageCommand) ServerDetails() (*coreConfig.ServerDetails, error) { @@ -46,14 +47,24 @@ func (bp *bindPackageCommand) prepareAndRunCommand(ctx *components.Context) erro if err != nil { return err } - bp.requestPayload, err = BuildPackageRequestPayload(ctx) - if err != nil { - return err - } + bp.extractFromArgs(ctx) return commonCLiCommands.Exec(bp) } +func (bp *bindPackageCommand) extractFromArgs(ctx *components.Context) { + bp.applicationKey = ctx.Arguments[0] + packageType := ctx.Arguments[1] + packageName := ctx.Arguments[2] + version := ctx.Arguments[3] + + bp.requestPayload = &model.BindPackageRequest{ + Type: packageType, + Name: packageName, + Version: version, + } +} + func GetBindPackageCommand(appContext app.Context) components.Command { cmd := &bindPackageCommand{packageService: appContext.GetPackageService()} return components.Command{ @@ -75,8 +86,8 @@ func GetBindPackageCommand(appContext app.Context) components.Command { 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').", + Name: "package-version", + Description: "Package version.", }, }, Flags: commands.GetCommandFlags(commands.PackageBind), diff --git a/apptrust/commands/package/bind_package_cmd_test.go b/apptrust/commands/package/bind_package_cmd_test.go index f1a1865..3cbaa02 100644 --- a/apptrust/commands/package/bind_package_cmd_test.go +++ b/apptrust/commands/package/bind_package_cmd_test.go @@ -16,20 +16,21 @@ func TestBindPackageCommand_Run(t *testing.T) { defer ctrl.Finish() serverDetails := &config.ServerDetails{Url: "https://example.com"} + applicationKey := "app-key" requestPayload := &model.BindPackageRequest{ - ApplicationKey: "app-key", - Type: "npm", - Name: "test-package", - Versions: []string{"1.0.0"}, + Type: "npm", + Name: "test-package", + Version: "1.0.0", } mockPackageService := mockpackages.NewMockPackageService(ctrl) - mockPackageService.EXPECT().BindPackage(gomock.Any(), requestPayload). + mockPackageService.EXPECT().BindPackage(gomock.Any(), applicationKey, requestPayload). Return(nil).Times(1) cmd := &bindPackageCommand{ packageService: mockPackageService, serverDetails: serverDetails, + applicationKey: applicationKey, requestPayload: requestPayload, } @@ -42,20 +43,21 @@ func TestBindPackageCommand_Run_Error(t *testing.T) { defer ctrl.Finish() serverDetails := &config.ServerDetails{Url: "https://example.com"} + applicationKey := "app-key" requestPayload := &model.BindPackageRequest{ - ApplicationKey: "app-key", - Type: "npm", - Name: "test-package", - Versions: []string{"1.0.0"}, + Type: "npm", + Name: "test-package", + Version: "1.0.0", } mockPackageService := mockpackages.NewMockPackageService(ctrl) - mockPackageService.EXPECT().BindPackage(gomock.Any(), requestPayload). + mockPackageService.EXPECT().BindPackage(gomock.Any(), applicationKey, requestPayload). Return(errors.New("bind error")).Times(1) cmd := &bindPackageCommand{ packageService: mockPackageService, serverDetails: serverDetails, + applicationKey: applicationKey, requestPayload: requestPayload, } diff --git a/apptrust/commands/package/unbind_package_cmd.go b/apptrust/commands/package/unbind_package_cmd.go index c89c210..9022975 100644 --- a/apptrust/commands/package/unbind_package_cmd.go +++ b/apptrust/commands/package/unbind_package_cmd.go @@ -5,7 +5,6 @@ import ( "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" @@ -17,7 +16,10 @@ import ( type unbindPackageCommand struct { packageService packages.PackageService serverDetails *coreConfig.ServerDetails - requestPayload *model.BindPackageRequest + applicationKey string + packageType string + packageName string + packageVersion string } func (up *unbindPackageCommand) Run() error { @@ -25,7 +27,7 @@ func (up *unbindPackageCommand) Run() error { if err != nil { return err } - return up.packageService.UnbindPackage(ctx, up.requestPayload) + return up.packageService.UnbindPackage(ctx, up.applicationKey, up.packageType, up.packageName, up.packageVersion) } func (up *unbindPackageCommand) ServerDetails() (*coreConfig.ServerDetails, error) { @@ -37,7 +39,7 @@ func (up *unbindPackageCommand) CommandName() string { } func (up *unbindPackageCommand) prepareAndRunCommand(ctx *components.Context) error { - if len(ctx.Arguments) < 3 || len(ctx.Arguments) > 4 { + if len(ctx.Arguments) != 4 { return pluginsCommon.WrongNumberOfArgumentsHandler(ctx) } @@ -46,10 +48,12 @@ func (up *unbindPackageCommand) prepareAndRunCommand(ctx *components.Context) er if err != nil { return err } - up.requestPayload, err = BuildPackageRequestPayload(ctx) - if err != nil { - return err - } + + // Extract from arguments + up.applicationKey = ctx.Arguments[0] + up.packageType = ctx.Arguments[1] + up.packageName = ctx.Arguments[2] + up.packageVersion = ctx.Arguments[3] return commonCLiCommands.Exec(up) } @@ -75,8 +79,8 @@ func GetUnbindPackageCommand(appContext app.Context) components.Command { 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.", + Name: "package-version", + Description: "Package version.", }, }, Flags: commands.GetCommandFlags(commands.PackageUnbind), diff --git a/apptrust/commands/package/unbind_package_cmd_test.go b/apptrust/commands/package/unbind_package_cmd_test.go index c61b45a..0f320e9 100644 --- a/apptrust/commands/package/unbind_package_cmd_test.go +++ b/apptrust/commands/package/unbind_package_cmd_test.go @@ -4,7 +4,6 @@ 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" @@ -16,21 +15,22 @@ func TestUnbindPackageCommand_Run(t *testing.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"}, - } + applicationKey := "app-key" + pkgType := "npm" + pkgName := "test-package" + pkgVersion := "1.0.0" mockPackageService := mockpackages.NewMockPackageService(ctrl) - mockPackageService.EXPECT().UnbindPackage(gomock.Any(), requestPayload). + mockPackageService.EXPECT().UnbindPackage(gomock.Any(), applicationKey, pkgType, pkgName, pkgVersion). Return(nil).Times(1) cmd := &unbindPackageCommand{ packageService: mockPackageService, serverDetails: serverDetails, - requestPayload: requestPayload, + applicationKey: applicationKey, + packageType: pkgType, + packageName: pkgName, + packageVersion: pkgVersion, } err := cmd.Run() @@ -42,21 +42,22 @@ func TestUnbindPackageCommand_Run_Error(t *testing.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"}, - } + applicationKey := "app-key" + pkgType := "npm" + pkgName := "test-package" + pkgVersion := "1.0.0" mockPackageService := mockpackages.NewMockPackageService(ctrl) - mockPackageService.EXPECT().UnbindPackage(gomock.Any(), requestPayload). + mockPackageService.EXPECT().UnbindPackage(gomock.Any(), applicationKey, pkgType, pkgName, pkgVersion). Return(errors.New("unbind error")).Times(1) cmd := &unbindPackageCommand{ packageService: mockPackageService, serverDetails: serverDetails, - requestPayload: requestPayload, + applicationKey: applicationKey, + packageType: pkgType, + packageName: pkgName, + packageVersion: pkgVersion, } err := cmd.Run() diff --git a/apptrust/commands/package/utils.go b/apptrust/commands/package/utils.go deleted file mode 100644 index 1b5089c..0000000 --- a/apptrust/commands/package/utils.go +++ /dev/null @@ -1,44 +0,0 @@ -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 deleted file mode 100644 index 3c78935..0000000 --- a/apptrust/commands/package/utils_test.go +++ /dev/null @@ -1,61 +0,0 @@ -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/http/http_client.go b/apptrust/http/http_client.go index 2922bcc..ae24573 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{}, params map[string]string) (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, requestBody interface{}) (resp *http.Response, body []byte, err error) + Delete(path string) (resp *http.Response, body []byte, err error) } type apptrustHttpClient struct { @@ -139,22 +139,14 @@ func (c *apptrustHttpClient) toJsonBytes(payload interface{}) ([]byte, error) { return jsonBytes, nil } -func (c *apptrustHttpClient) Delete(path string, requestBody interface{}) (resp *http.Response, body []byte, err error) { +func (c *apptrustHttpClient) Delete(path string) (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, requestContent, c.getJsonHttpClientDetails()) + return c.client.SendDelete(url, nil, c.getJsonHttpClientDetails()) } func (c *apptrustHttpClient) getJsonHttpClientDetails() *httputils.HttpClientDetails { diff --git a/apptrust/model/bind_package_request.go b/apptrust/model/bind_package_request.go index fc245c8..9c67f78 100644 --- a/apptrust/model/bind_package_request.go +++ b/apptrust/model/bind_package_request.go @@ -1,8 +1,7 @@ package model type BindPackageRequest struct { - ApplicationKey string `json:"application_key"` - Type string `json:"type"` - Name string `json:"name"` - Versions []string `json:"versions,omitempty"` + Type string `json:"package_type"` + Name string `json:"package_name"` + Version string `json:"package_version"` } diff --git a/apptrust/service/applications/application_service.go b/apptrust/service/applications/application_service.go index 475c53c..fbf887c 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, nil) + response, responseBody, err := ctx.GetHttpClient().Delete(endpoint) if err != nil { return err } diff --git a/apptrust/service/packages/package_service.go b/apptrust/service/packages/package_service.go index f548bd2..5cf1765 100644 --- a/apptrust/service/packages/package_service.go +++ b/apptrust/service/packages/package_service.go @@ -5,14 +5,15 @@ package packages import ( "fmt" "net/http" + "net/url" "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 + BindPackage(ctx service.Context, applicationKey string, request *model.BindPackageRequest) error + UnbindPackage(ctx service.Context, applicationKey, pkgType, pkgName, pkgVersion string) error } type packageService struct{} @@ -21,8 +22,8 @@ func NewPackageService() PackageService { return &packageService{} } -func (ps *packageService) BindPackage(ctx service.Context, request *model.BindPackageRequest) error { - endpoint := "/v1/package" +func (ps *packageService) BindPackage(ctx service.Context, applicationKey string, request *model.BindPackageRequest) error { + endpoint := fmt.Sprintf("/v1/applications/%s/packages", applicationKey) response, responseBody, err := ctx.GetHttpClient().Post(endpoint, request, nil) if err != nil { return err @@ -36,9 +37,9 @@ func (ps *packageService) BindPackage(ctx service.Context, request *model.BindPa return nil } -func (ps *packageService) UnbindPackage(ctx service.Context, request *model.BindPackageRequest) error { - endpoint := "/v1/package" - response, responseBody, err := ctx.GetHttpClient().Delete(endpoint, request) +func (ps *packageService) UnbindPackage(ctx service.Context, applicationKey, pkgType, pkgName, pkgVersion string) error { + endpoint := fmt.Sprintf("/v1/applications/%s/packages/%s/%s/%s", applicationKey, pkgType, url.PathEscape(pkgName), pkgVersion) + response, responseBody, err := ctx.GetHttpClient().Delete(endpoint) if err != nil { return err } diff --git a/apptrust/service/packages/package_service_test.go b/apptrust/service/packages/package_service_test.go index 7e2da9a..82e67f0 100644 --- a/apptrust/service/packages/package_service_test.go +++ b/apptrust/service/packages/package_service_test.go @@ -2,7 +2,9 @@ package packages import ( "errors" + "fmt" "net/http" + "net/url" "testing" mockhttp "github.com/jfrog/jfrog-cli-application/apptrust/http/mocks" @@ -18,6 +20,7 @@ func TestBindPackage(t *testing.T) { defer ctrl.Finish() service := NewPackageService() + applicationKey := "test-app" tests := []struct { name string @@ -29,10 +32,9 @@ func TestBindPackage(t *testing.T) { { name: "success", request: &model.BindPackageRequest{ - ApplicationKey: "test-app", - Type: "npm", - Name: "test-package", - Versions: []string{"1.0.0"}, + Type: "npm", + Name: "test-package", + Version: "1.0.0", }, mockResponse: &http.Response{StatusCode: 201}, mockError: nil, @@ -41,10 +43,9 @@ func TestBindPackage(t *testing.T) { { name: "failed with non-200 status code", request: &model.BindPackageRequest{ - ApplicationKey: "test-app", - Type: "npm", - Name: "test-package", - Versions: []string{"1.0.0"}, + Type: "npm", + Name: "test-package", + Version: "1.0.0", }, mockResponse: &http.Response{StatusCode: 400}, mockError: nil, @@ -53,10 +54,9 @@ func TestBindPackage(t *testing.T) { { name: "http client error", request: &model.BindPackageRequest{ - ApplicationKey: "test-app", - Type: "npm", - Name: "test-package", - Versions: []string{"1.0.0"}, + Type: "npm", + Name: "test-package", + Version: "1.0.0", }, mockResponse: nil, mockError: errors.New("http client error"), @@ -67,13 +67,13 @@ func TestBindPackage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockHttpClient := mockhttp.NewMockApptrustHttpClient(ctrl) - mockHttpClient.EXPECT().Post("/v1/package", tt.request, nil). + mockHttpClient.EXPECT().Post(fmt.Sprintf("/v1/applications/%s/packages", applicationKey), tt.request, nil). 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) + err := service.BindPackage(mockCtx, applicationKey, tt.request) if tt.expectedError == "" { assert.NoError(t, err) } else { @@ -87,64 +87,67 @@ func TestBindPackage(t *testing.T) { func TestUnbindPackage(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + applicationKey := "test-app" service := NewPackageService() tests := []struct { name string - request *model.BindPackageRequest + pkgType string + pkgName string + pkgVersion string 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"}, - }, + name: "success", + pkgType: "npm", + pkgName: "test-package", + pkgVersion: "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"}, - }, + name: "failed with non-200 status code", + pkgType: "npm", + pkgName: "test-package", + pkgVersion: "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"}, - }, + name: "http client error", + pkgType: "npm", + pkgName: "test-package", + pkgVersion: "1.0.0", mockResponse: nil, mockError: errors.New("http client error"), expectedError: "http client error", }, + { + name: "special characters in package name", + pkgType: "npm", + pkgName: "@test/package", + pkgVersion: "1.0.0", + mockResponse: &http.Response{StatusCode: 204}, + mockError: nil, + expectedError: "", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockHttpClient := mockhttp.NewMockApptrustHttpClient(ctrl) - mockHttpClient.EXPECT().Delete("/v1/package", tt.request). + mockHttpClient.EXPECT().Delete(fmt.Sprintf("/v1/applications/%s/packages/%s/%s/%s", applicationKey, tt.pkgType, url.PathEscape(tt.pkgName), tt.pkgVersion)). 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) + err := service.UnbindPackage(mockCtx, applicationKey, tt.pkgType, tt.pkgName, tt.pkgVersion) if tt.expectedError == "" { assert.NoError(t, err) } else { diff --git a/apptrust/service/versions/version_service.go b/apptrust/service/versions/version_service.go index 25141df..0eb27b5 100644 --- a/apptrust/service/versions/version_service.go +++ b/apptrust/service/versions/version_service.go @@ -71,7 +71,7 @@ func (vs *versionService) ReleaseAppVersion(ctx service.Context, applicationKey, func (vs *versionService) DeleteAppVersion(ctx service.Context, applicationKey, version string) error { url := fmt.Sprintf("/v1/applications/%s/versions/%s", applicationKey, version) - response, responseBody, err := ctx.GetHttpClient().Delete(url, nil) + response, responseBody, err := ctx.GetHttpClient().Delete(url) if err != nil { return err }