Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cli option to group reporter output. Closes #38 #92

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
fb451a1
Add GroupBy command
jcsmurph Nov 19, 2023
fdb4d5f
Add multiple groupby commands and validation
jcsmurph Nov 19, 2023
4e048fd
Add Tests
jcsmurph Nov 19, 2023
5683cb6
Reformat groupBy
jcsmurph Nov 20, 2023
a88b172
Add Test Files
jcsmurph Nov 20, 2023
7800a50
Update grouping order and update readme
jcsmurph Nov 20, 2023
853efbd
Fix groupby directory
jcsmurph Nov 20, 2023
bd74941
Update Tests
jcsmurph Nov 20, 2023
ca546e4
Fix Comment
jcsmurph Nov 20, 2023
e61be66
Update README.md
jcsmurph Nov 20, 2023
1140303
Update cli_test.go
jcsmurph Nov 20, 2023
b333ed2
Update validator.go
jcsmurph Nov 20, 2023
42e0c50
Update validator_test.go
jcsmurph Nov 20, 2023
1264e99
Update group_output.go
jcsmurph Nov 20, 2023
49de011
Update README.md
jcsmurph Nov 20, 2023
89718be
Update validator.go
jcsmurph Nov 20, 2023
367bd49
Update group_output.go
jcsmurph Nov 20, 2023
438275f
Update validator.go
jcsmurph Nov 20, 2023
741a1ca
Update cli_test.go
jcsmurph Nov 20, 2023
1f21c25
Update group_output.go
jcsmurph Nov 20, 2023
8dc47c6
Formatting fix
jcsmurph Nov 21, 2023
a90a888
Add Group output
jcsmurph Nov 21, 2023
a7b3335
Implement Group Output standard out
jcsmurph Nov 22, 2023
0aa37a3
Implement Single Groupby JSON
jcsmurph Nov 22, 2023
64acf60
Refactor GroupBy
jcsmurph Nov 22, 2023
2429073
Refactor GroupByTwo
jcsmurph Nov 22, 2023
3060971
Update to Stdout
jcsmurph Nov 22, 2023
16e341a
Implement JSON single group
jcsmurph Nov 23, 2023
01ddc54
Implement JSON double group and tests
jcsmurph Nov 23, 2023
d3391a1
Update go fmt
jcsmurph Nov 23, 2023
a190abc
Implement triple group output
jcsmurph Nov 23, 2023
f41a5e0
Add Output Tests
jcsmurph Nov 23, 2023
888f74a
Clean comments
jcsmurph Nov 23, 2023
6898103
Changed Summary Format
jcsmurph Nov 23, 2023
8452661
Update README.md
jcsmurph Nov 30, 2023
5bb439c
Update cmd/validator/validator.go
jcsmurph Nov 30, 2023
e893ee0
Add Header Comments
jcsmurph Dec 4, 2023
4fdb835
Fix Naming Conflict
jcsmurph Dec 4, 2023
a084cc7
Add Util function for Json Reporter
jcsmurph Dec 5, 2023
1a3db98
Rename Utility Function
jcsmurph Dec 5, 2023
eb2dfc1
Rename Utility Function Calls
jcsmurph Dec 5, 2023
bb5f191
Update JSON Print
jcsmurph Dec 5, 2023
e26c7cc
Update STD Reporter Test
jcsmurph Dec 11, 2023
fd2c8e8
Merge branch 'main' into Add-CLI-option-to-group-reporter-output-#38
jcsmurph Dec 11, 2023
3bf77db
Refactor Reporter
jcsmurph Dec 11, 2023
627b5c3
Update GroupBy Filetype
jcsmurph Dec 11, 2023
5262344
GoFmt update
jcsmurph Dec 12, 2023
df6ac8c
Update directory groupby
jcsmurph Dec 18, 2023
99fb5b8
Add GroupBy Directory test
jcsmurph Dec 18, 2023
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
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ There are several ways to install the config file validator tool

We offer alpine, ubuntu, and scratch containers

#### Apline
#### Apline
jcsmurph marked this conversation as resolved.
Show resolved Hide resolved

```
docker pull ghcr.io/boeing/config-file-validator:v1.5.0
Expand Down Expand Up @@ -103,10 +103,13 @@ optional flags:
Subdirectories to exclude when searching for configuration files
-exclude-file-types string
A comma separated list of file types to ignore
-groupby string
Group the output by filetype, pass/fail, or directory
-reporter string
Format of the printed report. Options are standard and json (default "standard")
-version
Version prints the release version of validator

jcsmurph marked this conversation as resolved.
Show resolved Hide resolved
```

### Examples
Expand All @@ -130,7 +133,7 @@ validator /path/to/search /another/path/to/search
Exclude subdirectories in the search path

```
validator --exclude-dirs=/path/to/search/tests /path/to/search
validator --exclude-dirs=/path/to/search/tests /path/to/search
```

![Exclude Dirs Run](./img/exclude_dirs.png)
Expand All @@ -145,7 +148,7 @@ validator --exclude-file-types=json /path/to/search
![Exclude File Types Run](./img/exclude_file_types.png)

#### Customize recursion depth
By default there is no recursion limit. If desired, the recursion depth can be set to an integer value. If depth is set to `0` recursion will be disabled and only the files in the search path will be validated.
By default there is no recursion limit. If desired, the recursion depth can be set to an integer value. If depth is set to `0` recursion will be disabled and only the files in the search path will be validated.

```
validator --depth=0 /path/to/search
Expand All @@ -162,6 +165,14 @@ validator --reporter=json /path/to/search

![Exclude File Types Run](./img/custom_reporter.png)

### Group report output
Group the report output by file type, directory, or pass/fail. Supports one or more groupings.

```
validator -groupby filetype
validator -groupby directory,pass/fail
```


#### Container Run
```
Expand Down Expand Up @@ -242,4 +253,4 @@ docker build . -t config-file-validator:v1.5.0
We welcome contributions! Please refer to our [contributing guide](/CONTRIBUTING.md)

## License
The Config File Validator is released under the [Apache 2.0](/LICENSE) License
The Config File Validator is released under the [Apache 2.0](/LICENSE) License
33 changes: 33 additions & 0 deletions cmd/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"fmt"
"log"
"os"
"slices"
"strings"

configfilevalidator "github.com/Boeing/config-file-validator"
Expand All @@ -46,6 +47,7 @@ type validatorConfig struct {
reportType *string
depth *int
versionQuery *bool
groupOutput *string
}

// Custom Usage function to cover
Expand All @@ -71,6 +73,7 @@ func getFlags() (validatorConfig, error) {
excludeFileTypesPtr := flag.String("exclude-file-types", "", "A comma separated list of file types to ignore")
depthPtr := flag.Int("depth", 0, "Depth of recursion for the provided search paths. Set depth to 0 to disable recursive path traversal")
versionPtr := flag.Bool("version", false, "Version prints the release version of validator")
groupOutputPtr := flag.String("groupby", "", "Group output by file type, directory, pass/fail")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
groupOutputPtr := flag.String("groupby", "", "Group output by file type, directory, pass/fail")
groupOutputPtr := flag.String("groupby", "", "Group output by file type, directory, pass-fail")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

flag.Parse()

searchPaths := make([]string, 0)
Expand All @@ -96,13 +99,28 @@ func getFlags() (validatorConfig, error) {
return validatorConfig{}, errors.New("Wrong parameter value for depth, value cannot be negative")
}

groupByCleanString := cleanString("groupby")
groupByUserInput := strings.Split(groupByCleanString, ",")
groupByAllowedValues := []string{"filetype", "directory", "pass/fail"}

if groupOutputPtr != nil && isFlagSet("groupby") {
for _, groupBy := range groupByUserInput {
if !slices.Contains(groupByAllowedValues, groupBy) {
fmt.Println("Wrong parameter value for groupby, only supports filetype, directory, pass/fail")
flag.Usage()
return validatorConfig{}, errors.New("Wrong parameter value for groupby, only supports filetype, directory, pass/fail")
}
}
}

config := validatorConfig{
searchPaths,
excludeDirsPtr,
excludeFileTypesPtr,
reportTypePtr,
depthPtr,
versionPtr,
groupOutputPtr,
}

return config, nil
Expand Down Expand Up @@ -132,6 +150,16 @@ func getReporter(reportType *string) reporter.Reporter {
}
}

// cleanString takes a command string and a split string
// and returns a cleaned string
func cleanString(command string) string {
cleanedString := flag.Lookup(command).Value.String()
cleanedString = strings.ToLower(cleanedString)
cleanedString = strings.TrimSpace(cleanedString)

return cleanedString
}

func mainInit() int {
validatorConfig, err := getFlags()
if err != nil {
Expand All @@ -149,6 +177,10 @@ func mainInit() int {
reporter := getReporter(validatorConfig.reportType)
excludeFileTypes := strings.Split(*validatorConfig.excludeFileTypes, ",")

// since the group output is a comma separated string
// it needs to be split into a slice of strings
groupOutput := strings.Split(*validatorConfig.groupOutput, ",")

fsOpts := []finder.FSFinderOptions{finder.WithPathRoots(validatorConfig.searchPaths...),
finder.WithExcludeDirs(excludeDirs),
finder.WithExcludeFileTypes(excludeFileTypes)}
Expand All @@ -164,6 +196,7 @@ func mainInit() int {
cli := cli.Init(
cli.WithReporter(reporter),
cli.WithFinder(fileSystemFinder),
cli.WithGroupOutput(groupOutput),
)

// Run the config file validation
Expand Down
2 changes: 2 additions & 0 deletions cmd/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ func Test_flags(t *testing.T) {
{"exclude file types set", []string{"--exclude-file-types=json", "."}, 0},
{"multiple paths", []string{"../../test/fixtures/subdir/good.json", "../../test/fixtures/good.json"}, 0},
{"version", []string{"--version"}, 0},
{"incorrect group", []string{"-groupby=badgroup", "."}, 1},
{"correct group", []string{"-groupby=directory", "."}, 0},
}
for _, tc := range cases {
// this call is required because otherwise flags panics,
Expand Down
12 changes: 12 additions & 0 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/Boeing/config-file-validator/pkg/reporter"
)

var GroupOutput []string

type CLI struct {
// FileFinder interface to search for the files
// in the SearchPath
Expand Down Expand Up @@ -36,6 +38,12 @@ func WithReporter(reporter reporter.Reporter) CLIOption {
}
}

func WithGroupOutput(groupOutput []string) CLIOption {
return func(c *CLI) {
GroupOutput = groupOutput
}
}

// Initialize the CLI object
func Init(opts ...CLIOption) *CLI {
defaultFsFinder := finder.FileSystemFinderInit()
Expand Down Expand Up @@ -88,6 +96,10 @@ func (c CLI) Run() (int, error) {
reports = append(reports, report)
}

// Group the output if the user specified a group by option
if len(GroupOutput) > 0 {
reports = GroupBy(reports, GroupOutput)
}
c.Reporter.Print(reports)

if errorFound {
Expand Down
28 changes: 28 additions & 0 deletions pkg/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
func Test_CLI(t *testing.T) {
searchPath := "../../test"
excludeDirs := []string{"subdir", "subdir2"}
groupBy := []string{}
jd4235 marked this conversation as resolved.
Show resolved Hide resolved
stdoutReporter := reporter.StdoutReporter{}

fsFinder := finder.FileSystemFinderInit(
Expand All @@ -19,6 +20,7 @@ func Test_CLI(t *testing.T) {
cli := Init(
WithFinder(fsFinder),
WithReporter(stdoutReporter),
WithGroupOutput(groupBy),
jd4235 marked this conversation as resolved.
Show resolved Hide resolved
)
exitStatus, err := cli.Run()

Expand Down Expand Up @@ -70,3 +72,29 @@ func Test_CLIBadPath(t *testing.T) {
t.Errorf("Exit status was not 1")
}
}

func Test_CLIWithGroup(t *testing.T) {
searchPath := "../../test"
excludeDirs := []string{"subdir", "subdir2"}
groupOutput := []string{"pass/fail", "directory", "filetype"}
jcsmurph marked this conversation as resolved.
Show resolved Hide resolved
stdoutReporter := reporter.StdoutReporter{}

fsFinder := finder.FileSystemFinderInit(
finder.WithPathRoots(searchPath),
finder.WithExcludeDirs(excludeDirs),
)
cli := Init(
WithFinder(fsFinder),
WithReporter(stdoutReporter),
WithGroupOutput(groupOutput),
)
exitStatus, err := cli.Run()

if err != nil {
t.Errorf("An error was returned: %v", err)
}

if exitStatus != 0 {
t.Errorf("Exit status was not 0")
}
}
94 changes: 94 additions & 0 deletions pkg/cli/group_output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cli

import (
"strings"

"github.com/Boeing/config-file-validator/pkg/reporter"
)

// Group Files by File Type
func GroupByFile(reports []reporter.Report) []reporter.Report {
mapFiles := make(map[string][]reporter.Report)
reportByFile := []reporter.Report{}

for _, report := range reports {
fileType := strings.Split(report.FileName, ".")[1]
if mapFiles[fileType] == nil {
mapFiles[fileType] = []reporter.Report{report}
} else {
mapFiles[fileType] = append(mapFiles[fileType], report)
}
}

for _, reports := range mapFiles {
reportByFile = append(reportByFile, reports...)
}

return reportByFile
}

// Group Files by Pass/Fail
func GroupByPassFail(reports []reporter.Report) []reporter.Report {
mapFiles := make(map[string][]reporter.Report)
reportByPassOrFail := []reporter.Report{}

for _, report := range reports {
if report.IsValid {
if mapFiles["pass"] == nil {
mapFiles["pass"] = []reporter.Report{report}
} else {
mapFiles["pass"] = append(mapFiles["pass"], report)
}
} else {
if mapFiles["fail"] == nil {
mapFiles["fail"] = []reporter.Report{report}
} else {
mapFiles["fail"] = append(mapFiles["fail"], report)
}
}
}

for _, reports := range mapFiles {
reportByPassOrFail = append(reportByPassOrFail, reports...)
}

return reportByPassOrFail
}

// Group Files by Directory
func GroupByDirectory(reports []reporter.Report) []reporter.Report {
mapFiles := make(map[string][]reporter.Report)
reportByDirectory := []reporter.Report{}

for _, report := range reports {
directoryPaths := strings.Split(report.FilePath, "/")
directory := directoryPaths[len(directoryPaths)-2]
if mapFiles[directory] == nil {
mapFiles[directory] = []reporter.Report{report}
} else {
mapFiles[directory] = append(mapFiles[directory], report)
}
}

for _, reports := range mapFiles {
reportByDirectory = append(reportByDirectory, reports...)
}

return reportByDirectory
}

func GroupBy(reports []reporter.Report, groupBy []string) []reporter.Report {
// Iterate through groupBy in reverse order
// This will make the first command the primary grouping
for i := len(groupBy)-1; i >= 0; i-- {
switch groupBy[i] {
case "pass/fail":
jd4235 marked this conversation as resolved.
Show resolved Hide resolved
reports = GroupByPassFail(reports)
case "filetype":
reports = GroupByFile(reports)
case "directory":
reports = GroupByDirectory(reports)
}
}
return reports
}
1 change: 0 additions & 1 deletion pkg/finder/fsfinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func WithDepth(depthVal int) FSFinderOptions {
fsf.Depth = &depthVal
}
}

func FileSystemFinderInit(opts ...FSFinderOptions) *FileSystemFinder {
var defaultExludeDirs []string
defaultPathRoots := []string{"."}
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/subdir2/bad.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ multiline = This line \
continues
# If you want your value to include a \, it should be escaped by another \.
path = c:\\wiki\\templates
# This means that if the number of \ at the end of the line is even, the next line is not included in the value.
# This means that if the number of \ at the end of the line is even, the next line is not included in the value.
# In the following example, the value for "evenKey" is "This is on one line\".
evenKey = This is on one line\\
# This line is a normal comment and is not included in the value for "evenKey"
Expand All @@ -45,4 +45,4 @@ encodedHelloInJapanese = \u3053\u3093\u306b\u3061\u306f
# But with more modern file encodings like UTF-8, you can directly use supported characters.
helloInJapanese = こんにちは
# Circular Dependency
key = ${key}
key = ${key}
8 changes: 8 additions & 0 deletions test/fixtures/subdir2/good.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Version]
First=name
second=value

[Empty]

[Last]
1=2
5 changes: 5 additions & 0 deletions test/fixtures/subdir2/good.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"name": "test"
}
}
Loading