diff --git a/internal/commands/project.go b/internal/commands/project.go index 5a8742ada..c9c7f88b8 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -29,6 +29,7 @@ const ( sshConfKey = "scan.handler.git.sshKey" mandatoryRepoURLError = "flag --repo-url is mandatory when --ssh-key is provided" invalidRepoURL = "provided repository url doesn't need a key. Make sure you are defining the right repository or remove the flag --ssh-key" + emptyTag = "NONE" ) var ( @@ -398,6 +399,8 @@ func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd * return errors.Wrapf(err, "%s", failedGettingAll) } + supportEmptyTags(params) + allProjectsModel, errorModel, err = projectsWrapper.Get(params) if err != nil { return errors.Wrapf(err, "%s\n", failedGettingAll) @@ -416,6 +419,47 @@ func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd * } } +func supportEmptyTags(params map[string]string) { + if hasNoneKeyAndValue(params) { + addEmptyTagsParam(params) + } +} + +func hasNoneKeyAndValue(params map[string]string) bool { + hasNoneKey := hasNoneValueInAttribute(params, commonParams.TagsKeyQueryParam) + hasNoneValue := hasNoneValueInAttribute(params, commonParams.TagsValueQueryParam) + return hasNoneKey && hasNoneValue +} + +func hasNoneValueInAttribute(params map[string]string, attribute string) bool { + values, exists := params[attribute] + return exists && strings.Contains(values, emptyTag) +} + +func addEmptyTagsParam(params map[string]string) { + removeNoneKeyAndValue(params) + params[commonParams.TagsEmptyQueryParam] = "true" +} + +func removeNoneKeyAndValue(params map[string]string) { + removeNoneAttribute(params, commonParams.TagsKeyQueryParam) + removeNoneAttribute(params, commonParams.TagsValueQueryParam) +} + +func removeNoneAttribute(params map[string]string, attribute string) { + values, exists := params[attribute] + if exists { + values = strings.ReplaceAll(values, ","+emptyTag, "") + values = strings.ReplaceAll(values, emptyTag+",", "") + values = strings.ReplaceAll(values, emptyTag, "") + if values == "" { + delete(params, attribute) + } else { + params[attribute] = values + } + } +} + func runGetProjectByIDCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var projectResponseModel *wrappers.ProjectResponseModel diff --git a/internal/commands/project_test.go b/internal/commands/project_test.go index 37d2332e3..25b0d8a9c 100644 --- a/internal/commands/project_test.go +++ b/internal/commands/project_test.go @@ -5,6 +5,8 @@ package commands import ( "testing" + asserts "github.com/stretchr/testify/assert" + errorConstants "github.com/checkmarx/ast-cli/internal/constants/errors" "github.com/checkmarx/ast-cli/internal/wrappers/mock" "github.com/checkmarx/ast-cli/internal/wrappers/utils" @@ -197,3 +199,73 @@ func TestGetProjectByName(t *testing.T) { assert.Equal(t, result.Name, projectName) assert.Equal(t, result.ID, "3") } + +func TestSupportEmptyTags_whenTagsFlagsNotExists_shouldNotChangeParams(t *testing.T) { + params := map[string]string{ + "limit": "10", + "ids": "1,2,3", + } + + supportEmptyTags(params) + + assert.Equal(t, params["limit"], "10") + assert.Equal(t, params["ids"], "1,2,3") + assert.Equal(t, len(params), 2) +} + +func TestSupportEmptyTags_whenTagsFlagsHasOnlyEmptyValues_shouldAddEmptyTagParamAndRemoveNoneTags(t *testing.T) { + params := map[string]string{ + "limit": "10", + "ids": "1,2,3", + "tags-keys": emptyTag, + "tags-values": emptyTag, + } + + supportEmptyTags(params) + + assert.Equal(t, params["limit"], "10") + assert.Equal(t, params["ids"], "1,2,3") + assert.Equal(t, params["empty-tags"], "true") + _, existsKey := params["tags-keys"] + _, existsValue := params["tags-values"] + asserts.False(t, existsKey, "tags-keys should not exist") + asserts.False(t, existsValue, "tags-values should not exist") + assert.Equal(t, len(params), 3) +} + +func TestSupportEmptyTags_whenTagsFlagsHaveAlsoEmptyValues_shouldAddEmptyTagParamAndRemoveNoneTags(t *testing.T) { + params := map[string]string{ + "limit": "10", + "ids": "1,2,3", + "tags-keys": "key1,key2," + emptyTag, + "tags-values": emptyTag + ",value1", + } + + supportEmptyTags(params) + + assert.Equal(t, params["limit"], "10") + assert.Equal(t, params["ids"], "1,2,3") + keys := params["tags-keys"] + values := params["tags-values"] + assert.Equal(t, keys, "key1,key2") + assert.Equal(t, values, "value1") + assert.Equal(t, params["empty-tags"], "true") + assert.Equal(t, len(params), 5) +} + +func TestSupportEmptyTags_whenOnlyKeysFlagHasEmptyValue_shouldNotChangeParams(t *testing.T) { + params := map[string]string{ + "limit": "10", + "ids": "1,2,3", + "tags-keys": "key1,key2," + emptyTag, + "tags-values": "value1", + } + + supportEmptyTags(params) + + assert.Equal(t, params["limit"], "10") + assert.Equal(t, params["ids"], "1,2,3") + assert.Equal(t, params["tags-keys"], "key1,key2,"+emptyTag) + assert.Equal(t, params["tags-values"], "value1") + assert.Equal(t, len(params), 4) +} diff --git a/internal/params/flags.go b/internal/params/flags.go index c1c00d6ab..ae40c6527 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -221,6 +221,7 @@ const ( QueryIDQueryParam = "query-id" TagsKeyQueryParam = "tags-keys" TagsValueQueryParam = "tags-values" + TagsEmptyQueryParam = "empty-tags" StatusesQueryParam = "statuses" StatusQueryParam = "status" BranchNameQueryParam = "branch-name" diff --git a/test/integration/project_test.go b/test/integration/project_test.go index c6f3eef9a..ec364cdf2 100644 --- a/test/integration/project_test.go +++ b/test/integration/project_test.go @@ -52,6 +52,17 @@ func TestProjectsE2E(t *testing.T) { assert.Equal(t, len(response), 0, "Total projects should be 0 as the project was deleted") } +func TestGetProjectByTagsFilter_whenProjectHasNoneTags_shouldReturnProjectWithNoTags(t *testing.T) { + projectID, _ := createProject(t, nil, nil) + defer deleteProject(t, projectID) + + projects := listProjectByTagsAndLimit(t, "NONE", "NONE", "5") + fmt.Println("Projects length: ", len(projects)) + for _, project := range projects { + assert.Equal(t, len(project.Tags), 0, "Project should have no tags") + } +} + // Assert project contains created tags and groups func assertTagsAndGroups(t *testing.T, project wrappers.ProjectResponseModel, groups []string) { @@ -221,6 +232,24 @@ func listProjectByID(t *testing.T, projectID string) []wrappers.ProjectResponseM return projects } +func listProjectByTagsAndLimit(t *testing.T, tagsKeys string, tagsValues string, limit string) []wrappers.ProjectResponseModel { + tagsFilter := fmt.Sprintf("%s=%s,%s=%s", params.TagsKeyQueryParam, tagsKeys, params.TagsValueQueryParam, tagsValues) + limitFilter := fmt.Sprintf("%s=%s", params.LimitQueryParam, limit) + fmt.Println("Listing project for filters: ", tagsFilter, ",", limitFilter) + filters := tagsFilter + "," + limitFilter + outputBuffer := executeCmdNilAssertion( + t, + "Getting the project should pass", + "project", "list", + flag(params.FormatFlag), printer.FormatJSON, flag(params.FilterFlag), filters, + ) + var projects []wrappers.ProjectResponseModel + _ = unmarshall(t, outputBuffer, &projects, "Reading all projects response JSON should pass") + fmt.Println("Listing project for tags projects length: ", len(projects)) + + return projects +} + func showProject(t *testing.T, projectID string) wrappers.ProjectResponseModel { assertRequiredParameter(t, "Failed getting a project: Please provide a project ID", "project", "show")