From 95ae1e90c57710a2439387010ee74690336d4f27 Mon Sep 17 00:00:00 2001 From: Daniel Amsellem Date: Thu, 16 Apr 2020 12:16:13 +0300 Subject: [PATCH 1/5] Added support for format flag and started to implement formatting in projects --- internal/commands/project.go | 43 +++++++++++++++++++++---------- internal/commands/project_test.go | 13 ++++++++++ internal/commands/root.go | 32 +++++++++++++++++++---- internal/commands/scan.go | 14 +++++----- 4 files changed, 75 insertions(+), 27 deletions(-) diff --git a/internal/commands/project.go b/internal/commands/project.go index 5138d58fa..2f1e2f2ba 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -71,27 +71,24 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd var input []byte var err error - var verbose bool var projInputFile string var projInput string - - verbose, _ = cmd.Flags().GetBool(verboseFlag) projInput, _ = cmd.Flags().GetString(inputFlag) projInputFile, _ = cmd.Flags().GetString(inputFileFlag) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFlag, projInput)) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFileFlag, projInputFile)) + PrintIfVerbose(fmt.Sprintf("%s: %s", inputFlag, projInput)) + PrintIfVerbose(fmt.Sprintf("%s: %s", inputFileFlag, projInputFile)) if projInputFile != "" { // Reading project from input file - PrintIfVerbose(verbose, fmt.Sprintf("Reading project input from file %s", projInputFile)) + PrintIfVerbose(fmt.Sprintf("Reading project input from file %s", projInputFile)) input, err = ioutil.ReadFile(projInputFile) if err != nil { return errors.Wrapf(err, "%s: Failed to open input file", failedCreatingProj) } } else if projInput != "" { // Reading from standard input - PrintIfVerbose(verbose, "Reading project input from console") + PrintIfVerbose("Reading project input from console") input = bytes.NewBufferString(projInput).Bytes() } else { // No input was given @@ -108,7 +105,7 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd var payload []byte payload, _ = json.Marshal(projModel) - PrintIfVerbose(verbose, fmt.Sprintf("Payload to projects service: %s\n", string(payload))) + PrintIfVerbose(fmt.Sprintf("Payload to projects service: %s\n", string(payload))) projResponseModel, errorModel, err = projectsWrapper.Create(&projModel) if err != nil { @@ -119,19 +116,37 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s\n", failedCreatingProj, errorModel.Code, errorModel.Message) } else if projResponseModel != nil { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(projResponseModel) + err = outputProject(cmd, projResponseModel) if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj) + return err } - cmdOut := cmd.OutOrStdout() - fmt.Fprintln(os.Stdout, "Project created successfully") - fmt.Fprintln(cmdOut, string(responseModelJSON)) } return nil } } +func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseModel) error { + if err := ValidateFormat(); err != nil { + return err + } + + if IsJSONFormat() { + responseModelJSON, err := json.Marshal(model) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj) + } + fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON)) + } else if IsPrettyFormat() { + fmt.Println("-----------New project created-----------------") + fmt.Println("Project ID:", model.ID) + fmt.Println("Created at:", model.Created) + fmt.Println("Updated at:", model.Updated) + fmt.Println("Tags:", model.Tags) + fmt.Println("-----------------------------------------------") + } + return nil +} + func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var allProjectsModel *projectsRESTApi.SlicedProjectsResponseModel diff --git a/internal/commands/project_test.go b/internal/commands/project_test.go index 00fafcb86..b8bb1a6b7 100644 --- a/internal/commands/project_test.go +++ b/internal/commands/project_test.go @@ -42,6 +42,19 @@ func TestRunCreateProjectCommandWithInput(t *testing.T) { assert.NilError(t, err) } +func TestRunCreateProjectCommandWithInvalidFormat(t *testing.T) { + cmd := createASTTestCommand() + err := executeTestCommand(cmd, "--format", "non-sense", "-v", "project", "create", "--input", "{\"id\": \"test_project\"}") + assert.Assert(t, err != nil) + assert.Assert(t, err.Error() == "Invalid format non-sense") +} + +func TestRunCreateProjectCommandWithInputPretty(t *testing.T) { + cmd := createASTTestCommand() + err := executeTestCommand(cmd, "--format", "pretty", "-v", "project", "create", "--input", "{\"id\": \"test_project\"}") + assert.NilError(t, err) +} + func TestRunCreateProjectCommandWithInputBadFormat(t *testing.T) { cmd := createASTTestCommand() err := executeTestCommand(cmd, "-v", "project", "create", "--input", "[]") diff --git a/internal/commands/root.go b/internal/commands/root.go index c7653e7a2..b6d9713ad 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/pkg/errors" + "github.com/checkmarxDev/ast-cli/internal/wrappers" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -36,6 +38,10 @@ const ( astAuthenticationURIFlagUsage = "The authentication URI for AST" insecureFlag = "insecure" insecureFlagUsage = "Ignore TLS certificate validations" + formatFlag = "format" + formatFlagUsage = "Format for the output. One of [json, pretty]. Default is JSON" + formatJSON = "json" + formatPretty = "pretty" ) var ( @@ -59,6 +65,7 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper, rootCmd.PersistentFlags().String(accessKeySecretFlag, "", accessKeySecretFlagUsage) rootCmd.PersistentFlags().String(astAuthenticationURIFlag, "", astAuthenticationURIFlagUsage) rootCmd.PersistentFlags().Bool(insecureFlag, false, insecureFlagUsage) + rootCmd.PersistentFlags().String(formatFlag, formatJSON, formatFlagUsage) // Bind the viper key ast_access_key_id to flag --key of the root command and // to the environment variable AST_ACCESS_KEY_ID so that it will be taken from environment variables first @@ -67,7 +74,9 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper, _ = viper.BindPFlag(AccessKeySecretConfigKey, rootCmd.PersistentFlags().Lookup(accessKeySecretFlag)) _ = viper.BindPFlag(AstAuthenticationURIConfigKey, rootCmd.PersistentFlags().Lookup(astAuthenticationURIFlag)) // Key here is the actual flag since it doesn't use an environment variable + _ = viper.BindPFlag(verboseFlag, rootCmd.PersistentFlags().Lookup(verboseFlag)) _ = viper.BindPFlag(insecureFlag, rootCmd.PersistentFlags().Lookup(insecureFlag)) + _ = viper.BindPFlag(formatFlag, rootCmd.PersistentFlags().Lookup(formatFlag)) scanCmd := NewScanCommand(scansWrapper, uploadsWrapper) projectCmd := NewProjectCommand(projectsWrapper) @@ -80,17 +89,30 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper, return rootCmd } -func PrintIfVerbose(verbose bool, msg string) { - if verbose { +func PrintIfVerbose(msg string) { + if viper.GetBool(verboseFlag) { fmt.Println(msg) } } func getLimitAndOffset(cmd *cobra.Command) (limit, offset uint64) { - verbose, _ := cmd.Flags().GetBool(verboseFlag) limit, _ = cmd.Flags().GetUint64(limitFlag) offset, _ = cmd.Flags().GetUint64(offsetFlag) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %d", limitFlag, limit)) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %d", offsetFlag, offset)) + PrintIfVerbose(fmt.Sprintf("%s: %d", limitFlag, limit)) + PrintIfVerbose(fmt.Sprintf("%s: %d", offsetFlag, offset)) return } + +func IsJSONFormat() bool { + return strings.EqualFold(viper.GetString(formatFlag), formatJSON) +} +func IsPrettyFormat() bool { + return strings.EqualFold(viper.GetString(formatFlag), formatPretty) +} + +func ValidateFormat() error { + if !(IsPrettyFormat() || IsJSONFormat()) { + return errors.Errorf("Invalid format %s", viper.GetString(formatFlag)) + } + return nil +} diff --git a/internal/commands/scan.go b/internal/commands/scan.go index e57a30304..8d9524d78 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -77,30 +77,28 @@ func runCreateScanCommand(scansWrapper wrappers.ScansWrapper, var input []byte var err error - var verbose bool var scanInputFile string var scanInput string var sourcesFile string - verbose, _ = cmd.Flags().GetBool(verboseFlag) scanInput, _ = cmd.Flags().GetString(inputFlag) scanInputFile, _ = cmd.Flags().GetString(inputFileFlag) sourcesFile, _ = cmd.Flags().GetString(sourcesFlag) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFlag, scanInput)) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFileFlag, scanInputFile)) - PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", sourcesFlag, sourcesFile)) + PrintIfVerbose(fmt.Sprintf("%s: %s", inputFlag, scanInput)) + PrintIfVerbose(fmt.Sprintf("%s: %s", inputFileFlag, scanInputFile)) + PrintIfVerbose(fmt.Sprintf("%s: %s", sourcesFlag, sourcesFile)) if scanInputFile != "" { // Reading from input file - PrintIfVerbose(verbose, fmt.Sprintf("Reading input from file %s", scanInputFile)) + PrintIfVerbose(fmt.Sprintf("Reading input from file %s", scanInputFile)) input, err = ioutil.ReadFile(scanInputFile) if err != nil { return errors.Wrapf(err, "%s: Failed to open input file", failedCreating) } } else if scanInput != "" { // Reading from standard input - PrintIfVerbose(verbose, "Reading input from console") + PrintIfVerbose("Reading input from console") input = bytes.NewBufferString(scanInput).Bytes() } else { // No input was given @@ -136,7 +134,7 @@ func runCreateScanCommand(scansWrapper wrappers.ScansWrapper, } var payload []byte payload, _ = json.Marshal(scanModel) - PrintIfVerbose(verbose, fmt.Sprintf("Payload to scans service: %s\n", string(payload))) + PrintIfVerbose(fmt.Sprintf("Payload to scans service: %s\n", string(payload))) scanResponseModel, errorModel, err = scansWrapper.Create(&scanModel) if err != nil { From 8648f5c19772bd138617b0e92e48669336b03d01 Mon Sep 17 00:00:00 2001 From: Daniel Amsellem Date: Thu, 16 Apr 2020 12:51:06 +0300 Subject: [PATCH 2/5] List projects with pretty formatting --- internal/commands/project.go | 90 ++++++++++++++++++-------------- test/integration/project_test.go | 9 ++++ 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/internal/commands/project.go b/internal/commands/project.go index 2f1e2f2ba..502bff67e 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -125,28 +125,6 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd } } -func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseModel) error { - if err := ValidateFormat(); err != nil { - return err - } - - if IsJSONFormat() { - responseModelJSON, err := json.Marshal(model) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj) - } - fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON)) - } else if IsPrettyFormat() { - fmt.Println("-----------New project created-----------------") - fmt.Println("Project ID:", model.ID) - fmt.Println("Created at:", model.Created) - fmt.Println("Updated at:", model.Updated) - fmt.Println("Tags:", model.Tags) - fmt.Println("-----------------------------------------------") - } - return nil -} - func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var allProjectsModel *projectsRESTApi.SlicedProjectsResponseModel @@ -162,30 +140,36 @@ func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd * if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s\n", failedGettingAll, errorModel.Code, errorModel.Message) } else if allProjectsModel != nil && allProjectsModel.Projects != nil { - cmdOut := cmd.OutOrStdout() - if cmdOut != os.Stdout { - var allProjectsJSON []byte - allProjectsJSON, err = json.Marshal(allProjectsModel) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) - } - fmt.Fprintln(cmdOut, string(allProjectsJSON)) - } - for _, project := range allProjectsModel.Projects { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(project) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) - } - fmt.Fprintln(os.Stdout, "----------------------------") - fmt.Fprintln(os.Stdout, string(responseModelJSON)) + err = outputProjects(cmd, allProjectsModel) + if err != nil { + return err } - fmt.Fprintln(os.Stdout, "----------------------------") } return nil } } +func outputProjects(cmd *cobra.Command, model *projectsRESTApi.SlicedProjectsResponseModel) error { + if IsJSONFormat() { + var allProjectsJSON []byte + allProjectsJSON, err := json.Marshal(model) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) + } + fmt.Fprintln(cmd.OutOrStdout(), string(allProjectsJSON)) + } else if IsPrettyFormat() { + for _, project := range model.Projects { + outputSingleProject(&projectsRESTApi.ProjectResponseModel{ + ID: project.ID, + Created: project.Created, + Updated: project.Updated, + Tags: project.Tags, + }) + } + } + return nil +} + func runGetProjectByIDCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var projectResponseModel *projectsRESTApi.ProjectResponseModel @@ -262,3 +246,29 @@ func runGetProjectsTagsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cm return nil } } + +func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseModel) error { + if err := ValidateFormat(); err != nil { + return err + } + + if IsJSONFormat() { + responseModelJSON, err := json.Marshal(model) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj) + } + fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON)) + } else if IsPrettyFormat() { + outputSingleProject(model) + } + return nil +} + +func outputSingleProject(model *projectsRESTApi.ProjectResponseModel) { + fmt.Println("----------------------------") + fmt.Println("Project ID:", model.ID) + fmt.Println("Created at:", model.Created) + fmt.Println("Updated at:", model.Updated) + fmt.Println("Tags:", model.Tags) + fmt.Println("----------------------------") +} diff --git a/test/integration/project_test.go b/test/integration/project_test.go index 356e8bb92..54db237b2 100644 --- a/test/integration/project_test.go +++ b/test/integration/project_test.go @@ -20,6 +20,7 @@ func TestProjectsE2E(t *testing.T) { projectID := createProjectFromInput(t, RandomizeString(5), []string{}) deleteProject(t, projectID) getAllProjects(t, projectFromFile) + getAllProjectsPretty(t, projectFromFile) getProjectByID(t, projectFromFile) _ = createProjectFromInput(t, RandomizeString(5), []string{"A", "B", "D"}) getProjectTags(t) @@ -78,6 +79,14 @@ func getProjectByID(t *testing.T, projectID string) { assert.Assert(t, project.Tags[2] == "C") } +func getAllProjectsPretty(t *testing.T, projectID string) { + getAllCommand := createASTIntegrationTestCommand(t) + var limit uint64 = 40 + var offset uint64 = 0 + err := execute(getAllCommand, "-v", "--format", "pretty", "project", "list", "--limit", strconv.FormatUint(limit, 10), "--offset", strconv.FormatUint(offset, 10)) + assert.NilError(t, err, "Getting all projects should pass") +} + func getAllProjects(t *testing.T, projectID string) { b := bytes.NewBufferString("") getAllCommand := createASTIntegrationTestCommand(t) From 74e91b2c065a37f94ad5f78d654298cce401f916 Mon Sep 17 00:00:00 2001 From: Daniel Amsellem Date: Thu, 16 Apr 2020 12:58:02 +0300 Subject: [PATCH 3/5] Get project with pretty formatting --- internal/commands/project.go | 50 +++++++++++++++----------------- test/integration/project_test.go | 7 +++++ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/internal/commands/project.go b/internal/commands/project.go index 502bff67e..40580400c 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -149,27 +149,6 @@ func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd * } } -func outputProjects(cmd *cobra.Command, model *projectsRESTApi.SlicedProjectsResponseModel) error { - if IsJSONFormat() { - var allProjectsJSON []byte - allProjectsJSON, err := json.Marshal(model) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) - } - fmt.Fprintln(cmd.OutOrStdout(), string(allProjectsJSON)) - } else if IsPrettyFormat() { - for _, project := range model.Projects { - outputSingleProject(&projectsRESTApi.ProjectResponseModel{ - ID: project.ID, - Created: project.Created, - Updated: project.Updated, - Tags: project.Tags, - }) - } - } - return nil -} - func runGetProjectByIDCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var projectResponseModel *projectsRESTApi.ProjectResponseModel @@ -187,14 +166,10 @@ func runGetProjectByIDCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s", failedGettingProj, errorModel.Code, errorModel.Message) } else if projectResponseModel != nil { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(projectResponseModel) + err = outputProject(cmd, projectResponseModel) if err != nil { - return errors.Wrapf(err, "%s: failed to serialize scan response ", failedGetting) + return err } - cmdOut := cmd.OutOrStdout() - fmt.Fprintf(os.Stdout, "-----Project ID %s-----\n", projectResponseModel.ID) - fmt.Fprintln(cmdOut, string(responseModelJSON)) } return nil } @@ -247,6 +222,27 @@ func runGetProjectsTagsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cm } } +func outputProjects(cmd *cobra.Command, model *projectsRESTApi.SlicedProjectsResponseModel) error { + if IsJSONFormat() { + var allProjectsJSON []byte + allProjectsJSON, err := json.Marshal(model) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) + } + fmt.Fprintln(cmd.OutOrStdout(), string(allProjectsJSON)) + } else if IsPrettyFormat() { + for _, project := range model.Projects { + outputSingleProject(&projectsRESTApi.ProjectResponseModel{ + ID: project.ID, + Created: project.Created, + Updated: project.Updated, + Tags: project.Tags, + }) + } + } + return nil +} + func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseModel) error { if err := ValidateFormat(); err != nil { return err diff --git a/test/integration/project_test.go b/test/integration/project_test.go index 54db237b2..551a520b2 100644 --- a/test/integration/project_test.go +++ b/test/integration/project_test.go @@ -22,6 +22,7 @@ func TestProjectsE2E(t *testing.T) { getAllProjects(t, projectFromFile) getAllProjectsPretty(t, projectFromFile) getProjectByID(t, projectFromFile) + getProjectByIDPretty(t, projectFromFile) _ = createProjectFromInput(t, RandomizeString(5), []string{"A", "B", "D"}) getProjectTags(t) } @@ -58,6 +59,12 @@ func executeCreateProject(t *testing.T, err error, b *bytes.Buffer) string { return createdProject.ID } +func getProjectByIDPretty(t *testing.T, projectID string) { + getProjectCommand := createASTIntegrationTestCommand(t) + err := execute(getProjectCommand, "-v", "--format", "pretty", "project", "show", projectID) + assert.NilError(t, err, "Getting a project should pass") +} + func getProjectByID(t *testing.T, projectID string) { b := bytes.NewBufferString("") getProjectCommand := createASTIntegrationTestCommand(t) From 57eb49646f94fe5dadf792a331d41d722dcd432b Mon Sep 17 00:00:00 2001 From: Daniel Amsellem Date: Thu, 16 Apr 2020 14:04:10 +0300 Subject: [PATCH 4/5] Get project tags and README --- README.md | 21 +++++++++++++++++++-- internal/commands/project.go | 5 +---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index df967d031..d3bf49431 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,22 @@ [![CircleCI](https://circleci.com/gh/CheckmarxDev/ast-cli/tree/master.svg?style=svg&circle-token=32eeef7505db60c11294e63db64e70882bde83b0)](https://circleci.com/gh/CheckmarxDev/ast-cli/tree/master) # ast-cli A CLI project wrapping the AST APIs - -ast (name-of-binary) [commands] + +## Building from source code +### Windows +When building an executable for Windows and providing a name, be sure to explicitly specify the .exe suffix when setting the executable’s name. +Inside the command prompt run: +**env GOOS=windows GOARCH=amd64 go build -o ./bin/ast.exe ./cmd** + +## Running the CLI + +### Windows +"bin/ast.exe" [commands] + +## Authentication +In order to authenticate with AST there are 3 environment variables that needs to be set: +**AST_AUTHENTICATION_URI**: The authentication URI used by AST +**AST_ACCESS_KEY_ID**: The access key ID +**AST_ACCESS_KEY_SECRET**: The access key secret + +Both access key ID and access key secret can be overriden by the flags **--key** and **--secret** respectively diff --git a/internal/commands/project.go b/internal/commands/project.go index 40580400c..19bde15d0 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "os" "github.com/pkg/errors" @@ -214,9 +213,7 @@ func runGetProjectsTagsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cm if err != nil { return errors.Wrapf(err, "%s: failed to serialize project tags response ", failedGettingTags) } - cmdOut := cmd.OutOrStdout() - fmt.Fprintln(os.Stdout, "-----Tags-----") - fmt.Fprintln(cmdOut, string(tagsJSON)) + fmt.Fprintln(cmd.OutOrStdout(), string(tagsJSON)) } return nil } From 1727740080737b41c374ff7316154f1b01f74bf9 Mon Sep 17 00:00:00 2001 From: Daniel Amsellem Date: Thu, 16 Apr 2020 16:42:56 +0300 Subject: [PATCH 5/5] Scan pretty print and tests --- internal/commands/project.go | 5 +- internal/commands/project_test.go | 2 +- internal/commands/scan.go | 94 ++++++++++++++++++------------- internal/commands/scan_test.go | 11 +++- test/integration/scan_test.go | 21 ++++++- 5 files changed, 88 insertions(+), 45 deletions(-) diff --git a/internal/commands/project.go b/internal/commands/project.go index 19bde15d0..a88434c4a 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -117,7 +117,7 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd } else if projResponseModel != nil { err = outputProject(cmd, projResponseModel) if err != nil { - return err + return errors.Wrapf(err, "%s", failedCreatingProj) } } return nil @@ -248,7 +248,7 @@ func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseMod if IsJSONFormat() { responseModelJSON, err := json.Marshal(model) if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj) + return errors.Wrapf(err, "Failed to serialize project response") } fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON)) } else if IsPrettyFormat() { @@ -263,5 +263,4 @@ func outputSingleProject(model *projectsRESTApi.ProjectResponseModel) { fmt.Println("Created at:", model.Created) fmt.Println("Updated at:", model.Updated) fmt.Println("Tags:", model.Tags) - fmt.Println("----------------------------") } diff --git a/internal/commands/project_test.go b/internal/commands/project_test.go index b8bb1a6b7..95fff3c92 100644 --- a/internal/commands/project_test.go +++ b/internal/commands/project_test.go @@ -46,7 +46,7 @@ func TestRunCreateProjectCommandWithInvalidFormat(t *testing.T) { cmd := createASTTestCommand() err := executeTestCommand(cmd, "--format", "non-sense", "-v", "project", "create", "--input", "{\"id\": \"test_project\"}") assert.Assert(t, err != nil) - assert.Assert(t, err.Error() == "Invalid format non-sense") + assert.Assert(t, err.Error() == "Failed creating a project: Invalid format non-sense") } func TestRunCreateProjectCommandWithInputPretty(t *testing.T) { diff --git a/internal/commands/scan.go b/internal/commands/scan.go index 8d9524d78..3def2a374 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "os" "github.com/pkg/errors" @@ -17,10 +16,10 @@ import ( const ( failedCreating = "Failed creating a scan" - failedGetting = "Failed getting a scan" + failedGetting = "Failed showing a scan" failedGettingTags = "Failed getting tags" failedDeleting = "Failed deleting a scan" - failedGettingAll = "Failed getting all" + failedGettingAll = "Failed listing" ) func NewScanCommand(scansWrapper wrappers.ScansWrapper, uploadsWrapper wrappers.UploadsWrapper) *cobra.Command { @@ -44,7 +43,7 @@ func NewScanCommand(scansWrapper wrappers.ScansWrapper, uploadsWrapper wrappers. listScansCmd := &cobra.Command{ Use: "list", Short: "List all scans in the system", - RunE: runGetAllScansCommand(scansWrapper), + RunE: runListScansCommand(scansWrapper), } listScansCmd.PersistentFlags().Uint64P(limitFlag, limitFlagSh, 0, limitUsage) listScansCmd.PersistentFlags().Uint64P(offsetFlag, offsetFlagSh, 0, offsetUsage) @@ -145,20 +144,16 @@ func runCreateScanCommand(scansWrapper wrappers.ScansWrapper, if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s\n", failedCreating, errorModel.Code, errorModel.Message) } else if scanResponseModel != nil { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(scanResponseModel) + err = outputScan(cmd, scanResponseModel) if err != nil { - return errors.Wrapf(err, "%s: failed to serialize scan response ", failedCreating) + return errors.Wrapf(err, "%s\n", failedCreating) } - cmdOut := cmd.OutOrStdout() - fmt.Fprintln(os.Stdout, "Scan created successfully") - fmt.Fprintln(cmdOut, string(responseModelJSON)) } return nil } } -func runGetAllScansCommand(scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { +func runListScansCommand(scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var allScansModel *scansRESTApi.SlicedScansResponseModel var errorModel *scansRESTApi.ErrorModel @@ -173,25 +168,10 @@ func runGetAllScansCommand(scansWrapper wrappers.ScansWrapper) func(cmd *cobra.C if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s\n", failedGettingAll, errorModel.Code, errorModel.Message) } else if allScansModel != nil && allScansModel.Scans != nil { - cmdOut := cmd.OutOrStdout() - if cmdOut != os.Stdout { - var allScansJSON []byte - allScansJSON, err = json.Marshal(allScansModel) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize scan response ", failedGettingAll) - } - fmt.Fprintln(cmdOut, string(allScansJSON)) - } - for _, scan := range allScansModel.Scans { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(scan) - if err != nil { - return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll) - } - fmt.Fprintln(os.Stdout, "----------------------------") - fmt.Fprintln(os.Stdout, string(responseModelJSON)) + err = outputScans(cmd, allScansModel) + if err != nil { + return err } - fmt.Fprintln(os.Stdout, "----------------------------") } return nil } @@ -214,14 +194,10 @@ func runGetScanByIDCommand(scansWrapper wrappers.ScansWrapper) func(cmd *cobra.C if errorModel != nil { return errors.Errorf("%s: CODE: %d, %s", failedGetting, errorModel.Code, errorModel.Message) } else if scanResponseModel != nil { - var responseModelJSON []byte - responseModelJSON, err = json.Marshal(scanResponseModel) + err = outputScan(cmd, scanResponseModel) if err != nil { - return errors.Wrapf(err, "%s: failed to serialize scan response ", failedGetting) + return err } - cmdOut := cmd.OutOrStdout() - fmt.Fprintf(os.Stdout, "-----Scan ID %s-----\n", scanResponseModel.ID) - fmt.Fprintln(cmdOut, string(responseModelJSON)) } return nil } @@ -265,10 +241,52 @@ func runGetTagsCommand(scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Comma if err != nil { return errors.Wrapf(err, "%s: failed to serialize scan tags response ", failedGettingTags) } - cmdOut := cmd.OutOrStdout() - fmt.Fprintln(os.Stdout, "-----Tags-----") - fmt.Fprintln(cmdOut, string(tagsJSON)) + fmt.Fprintln(cmd.OutOrStdout(), string(tagsJSON)) } return nil } } + +func outputScans(cmd *cobra.Command, allScansModel *scansRESTApi.SlicedScansResponseModel) error { + if IsJSONFormat() { + allScansJSON, err := json.Marshal(allScansModel) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize scan response ", failedGettingAll) + } + fmt.Fprintln(cmd.OutOrStdout(), string(allScansJSON)) + } else if IsPrettyFormat() { + for _, scan := range allScansModel.Scans { + prettySingleScan(&scansRESTApi.ScanResponseModel{ + ID: scan.ID, + Created: scan.Created, + Updated: scan.Updated, + Tags: scan.Tags, + }) + } + } + return nil +} + +func outputScan(cmd *cobra.Command, model *scansRESTApi.ScanResponseModel) error { + if err := ValidateFormat(); err != nil { + return err + } + + if IsJSONFormat() { + responseModelJSON, err := json.Marshal(model) + if err != nil { + return errors.Wrapf(err, "Failed to serialize scan response") + } + fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON)) + } else if IsPrettyFormat() { + prettySingleScan(model) + } + return nil +} +func prettySingleScan(model *scansRESTApi.ScanResponseModel) { + fmt.Println("----------------------------") + fmt.Println("Scan ID:", model.ID) + fmt.Println("Created at:", model.Created) + fmt.Println("Updated at:", model.Updated) + fmt.Println("Tags:", model.Tags) +} diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index 9a59c2c14..c2a56a8b3 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -49,6 +49,15 @@ func TestRunCreateScanCommandWithInput(t *testing.T) { assert.NilError(t, err) } +func TestRunCreateScanCommandWithInputPretty(t *testing.T) { + cmd := createASTTestCommand() + err := executeTestCommand(cmd, "-v", "--format", "pretty", "scan", + "create", "--input", "{\"project\":{\"id\":\"test\",\"type\":\"upload\",\"handler\":"+ + "{\"url\":\"MOSHIKO\"},\"tags\":[]},\"config\":"+ + "[{\"type\":\"sast\",\"value\":{\"presetName\":\"Default\"}}],\"tags\":[]}", "--sources", "./payloads/sources.zip") + assert.NilError(t, err) +} + func TestRunCreateScanCommandWithInputBadFormat(t *testing.T) { cmd := createASTTestCommand() err := executeTestCommand(cmd, "-v", "scan", "create", "--input", "[]", "--sources", "./payloads/sources.zip") @@ -59,7 +68,7 @@ func TestRunGetScanByIdCommandNoScanID(t *testing.T) { cmd := createASTTestCommand() err := executeTestCommand(cmd, "-v", "scan", "show") assert.Assert(t, err != nil) - assert.Assert(t, err.Error() == "Failed getting a scan: Please provide a scan ID") + assert.Assert(t, err.Error() == "Failed showing a scan: Please provide a scan ID") } func TestRunGetScanByIdCommandFlagNonExist(t *testing.T) { diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index bd180b07a..414731e13 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -48,7 +48,8 @@ func TestScansE2E(t *testing.T) { incScanResults := getResultsNumberForScan(t, incScanID) assert.Assert(t, incScanResults < scanResults, "Wrong number of inc scan results") - getAllScans(t) + listScansPretty(t) + listScans(t) getScansTags(t) } @@ -75,7 +76,7 @@ func deleteScan(t *testing.T) { } -func getAllScans(t *testing.T) { +func listScans(t *testing.T) { b := bytes.NewBufferString("") getAllCommand := createASTIntegrationTestCommand(t) getAllCommand.SetOut(b) @@ -98,6 +99,16 @@ func getAllScans(t *testing.T) { assert.Assert(t, len(allScans.Scans) == 2, "Total should be 2") } +func listScansPretty(t *testing.T) { + getAllCommand := createASTIntegrationTestCommand(t) + var limit uint64 = 40 + var offset uint64 = 0 + l := strconv.FormatUint(limit, 10) + o := strconv.FormatUint(offset, 10) + err := execute(getAllCommand, "-v", "--format", "pretty", "scan", "list", "--limit", l, "--offset", o) + assert.NilError(t, err, "Getting all scans should pass") +} + func getScanByID(t *testing.T, scanID string) *scansRESTApi.ScanResponseModel { getBuffer := bytes.NewBufferString("") getCommand := createASTIntegrationTestCommand(t) @@ -114,6 +125,11 @@ func getScanByID(t *testing.T, scanID string) *scansRESTApi.ScanResponseModel { assert.Assert(t, cmp.Equal(getScan.ID, scanID)) return &getScan } +func getScanByIDPretty(t *testing.T, scanID string) { + getCommand := createASTIntegrationTestCommand(t) + err := execute(getCommand, "-v", "--format", "pretty", "scan", "show", scanID) + assert.NilError(t, err) +} func getScansTags(t *testing.T) { b := bytes.NewBufferString("") @@ -156,6 +172,7 @@ func pollScanUntilStatus(t *testing.T, scanID string, ch chan<- bool, requiredSt for { log.Printf("Polling scan %s\n", scanID) scan := getScanByID(t, scanID) + getScanByIDPretty(t, scanID) if string(scan.Status) == string(requiredStatus) { ch <- true return