diff --git a/artifactory/services/utils/aqlquerybuilder.go b/artifactory/services/utils/aqlquerybuilder.go index caaec0c9c..4d4a0fa24 100644 --- a/artifactory/services/utils/aqlquerybuilder.go +++ b/artifactory/services/utils/aqlquerybuilder.go @@ -2,14 +2,27 @@ package utils import ( "fmt" - "golang.org/x/exp/slices" "strconv" "strings" + "golang.org/x/exp/slices" + "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" ) +var specialAqlCharacters = map[rune]string{ + '/': "%2F", + '\\': "%5C", + '|': "%7C", + '*': "%2A", + '?': "%3F", + '\'': "%22", + ':': "%3A", + ';': "%3B", + '%': "%25", +} + // Returns an AQL body string to search file in Artifactory by pattern, according the specified arguments requirements. func CreateAqlBodyForSpecWithPattern(params *CommonParams) (string, error) { searchPattern := prepareSourceSearchPattern(params.Pattern, params.Target) @@ -149,6 +162,38 @@ func CreateAqlQueryForPypi(repo, file string) string { return fmt.Sprintf(itemsPart, repo, file, buildIncludeQueryPart([]string{"name", "repo", "path", "actual_md5", "actual_sha1", "sha256"})) } +// noinspection GoUnusedExportedFunction +func CreateAqlQueryForBuildInfoJson(project, buildName, buildNumber, timestamp string) string { + if project == "" { + project = "artifactory" + } else { + project = encodeForBuildInfoRepository(project) + } + itemsPart := + `items.find({ + "repo": "%s", + "path": { + "$match": "%s" + }, + "name": { + "$match": "%s-%s.json" + } + })%s` + return fmt.Sprintf(itemsPart, project+"-build-info", encodeForBuildInfoRepository(buildName), encodeForBuildInfoRepository(buildNumber), timestamp, buildIncludeQueryPart([]string{"name", "repo", "path", "actual_sha1", "actual_md5"})) +} + +func encodeForBuildInfoRepository(value string) string { + results := "" + for _, char := range value { + if encoding, exist := specialAqlCharacters[char]; exist { + results += encoding + } else { + results += string(char) + } + } + return results +} + func CreateAqlQueryForLatestCreated(repo, path string) string { itemsPart := `items.find({` + diff --git a/artifactory/services/utils/aqlquerybuilder_test.go b/artifactory/services/utils/aqlquerybuilder_test.go index 8429125ca..1231b0133 100644 --- a/artifactory/services/utils/aqlquerybuilder_test.go +++ b/artifactory/services/utils/aqlquerybuilder_test.go @@ -220,3 +220,31 @@ func TestBuildKeyValQueryPart(t *testing.T) { }) } } + +var encodeForBuildInfoRepositoryProvider = []struct { + value string + expectedEncoding string +}{ + // Shouldn't encode + {"", ""}, + {"a", "a"}, + {"a b", "a b"}, + {"a.b", "a.b"}, + {"a&b", "a&b"}, + + // Should encode + {"a/b", "a%2Fb"}, + {"a\\b", "a%5Cb"}, + {"a:b", "a%3Ab"}, + {"a|b", "a%7Cb"}, + {"a*b", "a%2Ab"}, + {"a?b", "a%3Fb"}, +} + +func TestEncodeForBuildInfoRepository(t *testing.T) { + for _, testCase := range encodeForBuildInfoRepositoryProvider { + t.Run(testCase.value, func(t *testing.T) { + assert.Equal(t, testCase.expectedEncoding, encodeForBuildInfoRepository(testCase.value)) + }) + } +}