Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions apptrust/commands/version/create_app_version_cmd_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package version

import (
"encoding/json"
"errors"
"testing"

Expand Down Expand Up @@ -575,6 +576,59 @@ func TestParseArtifacts(t *testing.T) {
}
}

func TestParseAQL(t *testing.T) {
tests := []struct {
name string
spec *versionSpec
expected string
}{
{
name: "items_find object",
spec: &versionSpec{
AQL: &aqlSpec{ItemsFind: json.RawMessage(`{"repo":"my-repo"}`)},
},
expected: `items.find({"repo":"my-repo"})`,
},
{
name: "nested items_find object",
spec: &versionSpec{
AQL: &aqlSpec{ItemsFind: json.RawMessage(`{"repo":"my-repo","$or":[{"type":"file"}]}`)},
},
expected: `items.find({"repo":"my-repo","$or":[{"type":"file"}]})`,
},
{
name: "aql absent",
spec: &versionSpec{},
expected: "",
},
{
name: "aql present with empty items_find",
spec: &versionSpec{
AQL: &aqlSpec{ItemsFind: json.RawMessage(``)},
},
expected: "",
},
{
name: "aql present with null items_find",
spec: &versionSpec{
AQL: &aqlSpec{ItemsFind: json.RawMessage(`null`)},
},
expected: "",
},
{
name: "spec nil aql",
spec: &versionSpec{AQL: nil},
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, parseAQL(tt.spec))
})
}
}

func TestCreateAppVersionCommand_SpecFileSuite(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -724,6 +778,38 @@ func TestCreateAppVersionCommand_SpecFileSuite(t *testing.T) {
Version: "4.5.6",
},
},
AQL: `items.find({"repo":"my-repo","type":"file"})`,
},
},
},
{
name: "aql spec file",
specPath: "./testfiles/aql-spec.json",
args: []string{"app-aql", "1.0.0"},
expectsPayload: &model.CreateAppVersionRequest{
ApplicationKey: "app-aql",
Version: "1.0.0",
Draft: false,
Sources: &model.CreateVersionSources{
AQL: `items.find({"repo":"my-repo"})`,
},
},
},
{
name: "aql with filters spec file",
Comment thread
yurinovo18 marked this conversation as resolved.
specPath: "./testfiles/aql-with-filters-spec.json",
args: []string{"app-aql-filters", "1.0.0"},
expectsPayload: &model.CreateAppVersionRequest{
ApplicationKey: "app-aql-filters",
Version: "1.0.0",
Draft: false,
Sources: &model.CreateVersionSources{
AQL: `items.find({"repo":"my-repo"})`,
},
Filters: &model.CreateVersionFilters{
Included: []*model.CreateVersionSourceFilter{
{PackageType: "docker", PackageName: "frontend-*"},
},
},
},
},
Expand Down Expand Up @@ -822,6 +908,7 @@ func TestCreateAppVersionCommand_SpecFileSuite(t *testing.T) {
Version: "4.5.6",
},
},
AQL: `items.find({"repo":"my-repo","type":"file"})`,
},
},
},
Expand Down
5 changes: 4 additions & 1 deletion apptrust/commands/version/testfiles/all-sources-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,8 @@
"application_key": "dependency-app-2",
"version": "4.5.6"
}
]
],
"aql": {
"items.find": {"repo":"my-repo","type":"file"}
}
}
5 changes: 5 additions & 0 deletions apptrust/commands/version/testfiles/aql-spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"aql": {
"items.find": {"repo":"my-repo"}
}
}
13 changes: 13 additions & 0 deletions apptrust/commands/version/testfiles/aql-with-filters-spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"aql": {
"items.find": {"repo":"my-repo"}
},
"filters": {
"included": [
{
"package_type": "docker",
"package_name": "frontend-*"
}
]
}
}
15 changes: 15 additions & 0 deletions apptrust/commands/version/update_app_version_sources_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,21 @@ func TestUpdateAppVersionSourcesCommand_SourceFlagsSuite(t *testing.T) {
expectsDryRun: false,
expectsFailFast: true,
},
{
name: "update with aql spec file",
ctxSetup: func(ctx *components.Context) {
ctx.Arguments = []string{"app-key", "1.0.0"}
ctx.AddStringFlag(commands.SpecFlag, "./testfiles/aql-spec.json")
},
expectsPayload: &model.UpdateVersionSourcesRequest{
AddSources: &model.CreateVersionSources{
AQL: `items.find({"repo":"my-repo"})`,
},
},
expectsSync: true,
expectsDryRun: false,
expectsFailFast: true,
},
{
name: "update with spec file and spec-vars",
ctxSetup: func(ctx *components.Context) {
Expand Down
24 changes: 22 additions & 2 deletions apptrust/commands/version/version_source_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package version

import (
"encoding/json"
"fmt"
"strconv"
"strings"

Expand All @@ -21,6 +22,11 @@ type versionSpec struct {
ReleaseBundles []model.CreateVersionReleaseBundle `json:"release_bundles,omitempty"`
Versions []model.CreateVersionReference `json:"versions,omitempty"`
Filters *model.CreateVersionFilters `json:"filters,omitempty"`
AQL *aqlSpec `json:"aql,omitempty"`
}

type aqlSpec struct {
ItemsFind json.RawMessage `json:"items.find,omitempty"`
}

// validateNoSpecAndFlagsTogether returns error if both --spec and any other source flag or filter flag are set.
Expand Down Expand Up @@ -154,9 +160,11 @@ func loadSourcesFromSpec(ctx *components.Context) (*model.CreateVersionSources,
return nil, nil, err
}

aql := parseAQL(spec)

// Validation: if all sources are empty, return error
if (len(spec.Packages) == 0) && (len(spec.Builds) == 0) && (len(spec.ReleaseBundles) == 0) && (len(spec.Versions) == 0) && (len(spec.Artifacts) == 0) {
return nil, nil, errorutils.CheckErrorf("Spec file is empty: must provide at least one source (artifacts, packages, builds, release_bundles, or versions)")
if len(spec.Packages) == 0 && len(spec.Builds) == 0 && len(spec.ReleaseBundles) == 0 && len(spec.Versions) == 0 && len(spec.Artifacts) == 0 && aql == "" {
return nil, nil, errorutils.CheckErrorf("Spec file is empty: must provide at least one source (artifacts, packages, builds, release_bundles, versions, or aql)")
}

sources := &model.CreateVersionSources{
Expand All @@ -165,11 +173,23 @@ func loadSourcesFromSpec(ctx *components.Context) (*model.CreateVersionSources,
Builds: spec.Builds,
ReleaseBundles: spec.ReleaseBundles,
Versions: spec.Versions,
AQL: aql,
}

return sources, spec.Filters, nil
}

func parseAQL(spec *versionSpec) string {
if spec == nil || spec.AQL == nil {
return ""
}
itemsFind := string(spec.AQL.ItemsFind)
if itemsFind == "" || itemsFind == "null" {
return ""
}
return fmt.Sprintf("items.find(%s)", itemsFind)
}

func parseBuilds(buildsStr string) ([]model.CreateVersionBuild, error) {
const (
nameField = "name"
Expand Down
1 change: 1 addition & 0 deletions apptrust/model/create_app_version_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type CreateVersionSources struct {
Builds []CreateVersionBuild `json:"builds,omitempty"`
ReleaseBundles []CreateVersionReleaseBundle `json:"release_bundles,omitempty"`
Versions []CreateVersionReference `json:"versions,omitempty"`
AQL string `json:"aql,omitempty"`
}

type CreateVersionSourceFilter struct {
Expand Down
3 changes: 2 additions & 1 deletion e2e/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ func TestVersionUpdate_OutputFormat(t *testing.T) {

func TestVersionUpdateSources_OutputFormat(t *testing.T) {
testPackage := utils.GetTestPackage(t)
artifactPath := utils.GetTestArtifact(t)
artifactRepo, artifactFile := utils.GetTestArtifact(t)
artifactPath := artifactRepo + "/" + artifactFile

prepareDraftVersion := func(t *testing.T, suffix string) (appKey, version string, cleanup func()) {
appKey = utils.GenerateUniqueKey("version-upd-src-fmt-" + suffix)
Expand Down
18 changes: 10 additions & 8 deletions e2e/utils/e2e_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ var (

AppTrustCli *coreTests.JfrogCli

testProjectKey string
testPackageRes *TestPackageResources
testArtifactPath string
testProjectKey string
testPackageRes *TestPackageResources
testArtifactRepoKey string
testArtifactFileName string
)

func LoadCredentials() string {
Expand Down Expand Up @@ -81,12 +82,13 @@ func GetTestPackage(t *testing.T) *TestPackageResources {
return testPackageRes
}

func GetTestArtifact(t *testing.T) string {
if testArtifactPath == "" {
repoKey := createGenericRepo(t)
testArtifactPath = UploadTestArtifact(t, repoKey, "test-artifact.txt")
func GetTestArtifact(t *testing.T) (repoKey, fileName string) {
if testArtifactRepoKey == "" {
testArtifactRepoKey = createGenericRepo(t)
testArtifactFileName = "test-artifact.txt"
UploadTestArtifact(t, testArtifactRepoKey, testArtifactFileName)
}
return testArtifactPath
return testArtifactRepoKey, testArtifactFileName
}

func GenerateUniqueKey(prefix string) string {
Expand Down
85 changes: 84 additions & 1 deletion e2e/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -149,6 +151,86 @@ func TestCreateVersion_ReleaseBundle(t *testing.T) {
assertVersionContent(t, testPackage, versionContent, statusCode, appKey, version)
}

func TestCreateVersion_AQL(t *testing.T) {
appKey := utils.GenerateUniqueKey("app-version-create-aql")
utils.CreateBasicApplication(t, appKey)
defer utils.DeleteApplication(t, appKey)

repoKey, fileName := utils.GetTestArtifact(t)
artifactPath := repoKey + "/" + fileName
version := "1.0.13"

specPath := writeAQLSpec(t, fmt.Sprintf(`{"repo":"%s","name":"%s"}`, repoKey, fileName), "")

err := utils.AppTrustCli.Exec("version-create", appKey, version, "--spec="+specPath)
require.NoError(t, err)
defer utils.DeleteApplicationVersion(t, appKey, version)

versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, statusCode)
require.NotNil(t, versionContent)
assert.Equal(t, appKey, versionContent.ApplicationKey)
assert.Equal(t, version, versionContent.Version)
assert.Equal(t, utils.StatusCompleted, versionContent.Status)
assert.True(t, containsArtifactPath(versionContent, artifactPath),
"expected artifact %q resolved by AQL to appear in releasables", artifactPath)
}

func TestCreateVersion_AQL_WithExcludeFilter(t *testing.T) {
appKey := utils.GenerateUniqueKey("app-version-create-aql-filters")
utils.CreateBasicApplication(t, appKey)
defer utils.DeleteApplication(t, appKey)

repoKey := utils.CreateGenericRepoWithEnv(t, utils.GenerateUniqueKey("aql-filter"), nil)
includedPath := utils.UploadTestArtifact(t, repoKey, "included-artifact.txt")
excludedPath := utils.UploadTestArtifact(t, repoKey, "excluded-artifact.txt")
version := "1.0.14"

itemsFindJSON := fmt.Sprintf(`{"repo":"%s"}`, repoKey)
filtersJSON := fmt.Sprintf(`"filters":{"excluded":[{"path":"%s"}]}`, excludedPath)
specPath := writeAQLSpec(t, itemsFindJSON, filtersJSON)

err := utils.AppTrustCli.Exec("version-create", appKey, version, "--spec="+specPath)
require.NoError(t, err)
defer utils.DeleteApplicationVersion(t, appKey, version)

versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, statusCode)
require.NotNil(t, versionContent)
assert.Equal(t, appKey, versionContent.ApplicationKey)
assert.Equal(t, version, versionContent.Version)
assert.Equal(t, utils.StatusCompleted, versionContent.Status)
assert.True(t, containsArtifactPath(versionContent, includedPath),
"expected included artifact %q to remain after filter", includedPath)
assert.False(t, containsArtifactPath(versionContent, excludedPath),
"expected excluded artifact %q to be filtered out", excludedPath)
}

func writeAQLSpec(t *testing.T, itemsFindJSON, extraTopLevelJSON string) string {
t.Helper()
body := fmt.Sprintf(`{"aql":{"items.find":%s}`, itemsFindJSON)
if extraTopLevelJSON != "" {
body += "," + extraTopLevelJSON
}
body += "}"
path := filepath.Join(t.TempDir(), "aql-spec.json")
require.NoError(t, os.WriteFile(path, []byte(body), 0o600))
return path
}

func containsArtifactPath(vc *utils.VersionContentResponse, target string) bool {
for _, r := range vc.Releasables {
for _, a := range r.Artifacts {
if strings.Contains(target, a.Path) || strings.Contains(a.Path, target) {
return true
}
}
}
return false
}

func TestCreateVersion_Build(t *testing.T) {
// Prepare
appKey := utils.GenerateUniqueKey("app-version-create-build")
Expand Down Expand Up @@ -378,7 +460,8 @@ func TestUpdateDraftVersionSources(t *testing.T) {
err := utils.AppTrustCli.Exec("version-create", appKey, version, packageFlag, "--draft")
require.NoError(t, err)
defer utils.DeleteApplicationVersion(t, appKey, version)
artifactPath := utils.GetTestArtifact(t)
artifactRepo, artifactFile := utils.GetTestArtifact(t)
artifactPath := artifactRepo + "/" + artifactFile
artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath)

err = utils.AppTrustCli.Exec("version-update-sources", appKey, version, artifactFlag)
Expand Down
Loading