Repository: datreeio/datree
Pull request #15 (2021-04-19 12:26:49)

feat: support yaml and json output
* Add flag `-o, --output` to `datree test` to allow output in different formats 
* currently suppot `json` and `yaml`


Patch:
diff --git a/bl/evaluator.go b/bl/evaluator.go
index 26babd6..5bdaf10 100644
--- a/bl/evaluator.go
+++ b/bl/evaluator.go
@@ -1,6 +1,7 @@
 package bl
 
 import (
+	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -8,6 +9,7 @@ import (
 	"github.com/datreeio/datree/pkg/cliClient"
 	"github.com/datreeio/datree/pkg/printer"
 	"github.com/datreeio/datree/pkg/propertiesExtractor"
+	"gopkg.in/yaml.v3"
 )
 
 type Printer interface {
@@ -63,7 +65,29 @@ func (e *Evaluator) Evaluate(pattern string, cliId string, evaluationConc int) (
 	return results, fileErrors, nil
 }
 
-func (e *Evaluator) PrintResults(results *EvaluationResults, cliId string) error {
+func (e *Evaluator) PrintResults(results *EvaluationResults, cliId string, output string) error {
+	if output == "json" {
+		jsonOutput, err := json.Marshal(results)
+		if err != nil {
+			fmt.Println(err)
+			return err
+		}
+
+		fmt.Println(string(jsonOutput))
+		return nil
+	}
+
+	if output == "yaml" {
+		yamlOutput, err := yaml.Marshal(results)
+		if err != nil {
+			fmt.Println(err)
+			return err
+		}
+
+		fmt.Println(string(yamlOutput))
+		return nil
+	}
+
 	warnings, err := e.parseEvaluationResultsToWarnings(results)
 	if err != nil {
 		fmt.Println(err)
diff --git a/go.mod b/go.mod
index 3506b48..60fccb7 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,6 @@ require (
 	github.com/google/uuid v1.2.0 // indirect
 	github.com/kyokomi/emoji v2.2.4+incompatible
 	github.com/lithammer/shortuuid v3.0.0+incompatible
-	github.com/magiconair/properties v1.8.1
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/spf13/cobra v1.1.3
 	github.com/spf13/viper v1.7.1


Test patch:
diff --git a/bl/evaluator_test.go b/bl/evaluator_test.go
index 5bd6d35..f4c30c5 100644
--- a/bl/evaluator_test.go
+++ b/bl/evaluator_test.go
@@ -252,7 +252,7 @@ func TestPrintResults(t *testing.T) {
 		}{},
 	}
 
-	evaluator.PrintResults(&results, "cli_id")
+	evaluator.PrintResults(&results, "cli_id", "")
 
 	expectedPrinterWarnings := []printer.Warning{}
 	printerSpy.AssertCalled(t, "PrintWarnings", expectedPrinterWarnings)
diff --git a/cmd/test/main.go b/cmd/test/main.go
index e613f5c..c49ad24 100644
--- a/cmd/test/main.go
+++ b/cmd/test/main.go
@@ -17,7 +17,7 @@ type LocalConfigManager interface {
 }
 
 type Evaluator interface {
-	PrintResults(results *bl.EvaluationResults, cliId string) error
+	PrintResults(results *bl.EvaluationResults, cliId string, output string) error
 	PrintFileParsingErrors(errors []propertiesExtractor.FileError)
 	Evaluate(pattern string, cliId string, evaluationConc int) (*bl.EvaluationResults, []propertiesExtractor.FileError, error)
 }
@@ -27,21 +27,35 @@ type TestCommandContext struct {
 	Evaluator   Evaluator
 }
 
+type TestCommandFlags struct {
+	Output string
+}
+
 func CreateTestCommand(ctx *TestCommandContext) *cobra.Command {
-	return &cobra.Command{
+	testCommand := &cobra.Command{
 		Use:   "test",
 		Short: "Execute static analysis for pattern",
 		Long:  `Execute static analysis for pattern. Input should be glob`,
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return test(ctx, args[0])
+			outputFlag, err := cmd.Flags().GetString("output")
+			if err != nil {
+				fmt.Println(err)
+				return err
+			}
+
+			testCommandFlags := TestCommandFlags{Output: outputFlag}
+			return test(ctx, args[0], testCommandFlags)
 		},
 		Args:          cobra.ExactValidArgs(1),
 		SilenceUsage:  true,
 		SilenceErrors: true,
 	}
+
+	testCommand.Flags().StringP("output", "o", "", "Define output format")
+	return testCommand
 }
 
-func test(ctx *TestCommandContext, pattern string) error {
+func test(ctx *TestCommandContext, pattern string, flags TestCommandFlags) error {
 	absolutePath, err := filepath.Abs(pattern)
 	if err != nil {
 		fmt.Println(err)
@@ -77,5 +91,5 @@ func test(ctx *TestCommandContext, pattern string) error {
 
 	}
 
-	return ctx.Evaluator.PrintResults(evaluationResponse, config.CliId)
+	return ctx.Evaluator.PrintResults(evaluationResponse, config.CliId, flags.Output)
 }
diff --git a/cmd/test/main_test.go b/cmd/test/main_test.go
index e91ed3e..7cde8aa 100644
--- a/cmd/test/main_test.go
+++ b/cmd/test/main_test.go
@@ -23,8 +23,8 @@ type mockEvaluator struct {
 	mock.Mock
 }
 
-func (m *mockEvaluator) PrintResults(results *bl.EvaluationResults, cliId string) error {
-	m.Called(results, cliId)
+func (m *mockEvaluator) PrintResults(results *bl.EvaluationResults, cliId string, output string) error {
+	m.Called(results, cliId, output)
 	return nil
 }
 
@@ -47,7 +47,7 @@ func TestTestCommand(t *testing.T) {
 	}
 	evaluator.On("Evaluate", mock.Anything, mock.Anything, mock.Anything).Return(mockedEvaluateResponse, []propertiesExtractor.FileError{}, nil)
 	evaluator.On("PrintFileParsingErrors", mock.Anything).Return()
-	evaluator.On("PrintResults", mock.Anything, mock.Anything).Return()
+	evaluator.On("PrintResults", mock.Anything, mock.Anything, mock.Anything).Return()
 
 	localConfigManager := &mockLocalConfigManager{}
 	localConfigManager.On("GetConfiguration").Return(localConfig.LocalConfiguration{CliId: "134kh"}, nil)
@@ -57,11 +57,37 @@ func TestTestCommand(t *testing.T) {
 		LocalConfig: localConfigManager,
 	}
 
-	test(ctx, "8/*")
+	test_testCommand_no_flags(t, localConfigManager, evaluator, mockedEvaluateResponse, ctx)
+	test_testCommand_json_output(t, localConfigManager, evaluator, mockedEvaluateResponse, ctx)
+	test_testCommand_yaml_output(t, localConfigManager, evaluator, mockedEvaluateResponse, ctx)
+}
+
+func test_testCommand_no_flags(t *testing.T, localConfigManager *mockLocalConfigManager, evaluator *mockEvaluator, mockedEvaluateResponse *bl.EvaluationResults, ctx *TestCommandContext) {
+	test(ctx, "8/*", TestCommandFlags{})
+	localConfigManager.AssertCalled(t, "GetConfiguration")
+
+	expectedPath, _ := filepath.Abs("8/*")
+	evaluator.AssertCalled(t, "Evaluate", expectedPath, "134kh", 50)
+	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
+	evaluator.AssertCalled(t, "PrintResults", mockedEvaluateResponse, "134kh", "")
+}
+
+func test_testCommand_json_output(t *testing.T, localConfigManager *mockLocalConfigManager, evaluator *mockEvaluator, mockedEvaluateResponse *bl.EvaluationResults, ctx *TestCommandContext) {
+	test(ctx, "8/*", TestCommandFlags{Output: "json"})
+	localConfigManager.AssertCalled(t, "GetConfiguration")
+
+	expectedPath, _ := filepath.Abs("8/*")
+	evaluator.AssertCalled(t, "Evaluate", expectedPath, "134kh", 50)
+	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
+	evaluator.AssertCalled(t, "PrintResults", mockedEvaluateResponse, "134kh", "json")
+}
+
+func test_testCommand_yaml_output(t *testing.T, localConfigManager *mockLocalConfigManager, evaluator *mockEvaluator, mockedEvaluateResponse *bl.EvaluationResults, ctx *TestCommandContext) {
+	test(ctx, "8/*", TestCommandFlags{Output: "yaml"})
 	localConfigManager.AssertCalled(t, "GetConfiguration")
 
 	expectedPath, _ := filepath.Abs("8/*")
 	evaluator.AssertCalled(t, "Evaluate", expectedPath, "134kh", 50)
 	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
-	evaluator.AssertCalled(t, "PrintResults", mockedEvaluateResponse, "134kh")
+	evaluator.AssertCalled(t, "PrintResults", mockedEvaluateResponse, "134kh", "yaml")
 }