Skip to content

Commit

Permalink
feat(validation): Add support for properties files (#52)
Browse files Browse the repository at this point in the history
* Add PropValidator which impliments Validator interface to prase byte array of properties

Signed-off-by: gokulav137 <[email protected]>

* Add valid and invalid scenarios for testing PropValidator

Signed-off-by: gokulav137 <[email protected]>

* Add PropFiletype to represent properties file and mapped it to PropValidator

Signed-off-by: gokulav137 <[email protected]>

* Add good and bad .properties files to test/fixtures

Signed-off-by: gokulav137 <[email protected]>

* Fix typo in test name: Properites -> Properties

Signed-off-by: gokulav137 <[email protected]>

* Add magiconair/properties to go.mod

Signed-off-by: gokulav137 <[email protected]>

* Add Properties to supported types in README

Signed-off-by: gokulav137 <[email protected]>

* Fix padding issue for multiline validation errors

Signed-off-by: gokulav137 <[email protected]>

* Add test case to cover multiline error padding case

Signed-off-by: gokulav137 <[email protected]>

* Add doc comment for padErrorString method

Signed-off-by: gokulav137 <[email protected]>

---------

Signed-off-by: gokulav137 <[email protected]>
  • Loading branch information
gokulav137 authored Oct 5, 2023
1 parent 99d67a3 commit db9e556
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ How many deployments have you done that needed to be rolled back due to a missin
* YAML
* TOML
* INI
* Properties

## Installing
There are several ways to install the config file validator tool
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.21

require (
github.com/fatih/color v1.13.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
Expand Down
6 changes: 5 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
Expand Down
9 changes: 9 additions & 0 deletions pkg/filetype/file_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ var IniFileType = FileType{
validator.IniValidator{},
}

// Instance of FileType object to
// represent a Properties file
var PropFileType = FileType{
"properties",
[]string{"properties"},
validator.PropValidator{},
}

// An array of files types that are supported
// by the validator
var FileTypes = []FileType{
Expand All @@ -62,4 +70,5 @@ var FileTypes = []FileType{
XmlFileType,
TomlFileType,
IniFileType,
PropFileType,
}
9 changes: 8 additions & 1 deletion pkg/reporter/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ func Test_stdoutReport(t *testing.T) {
errors.New("Unable to parse bad.xml file"),
}

reports := []Report{reportNoValidationError, reportWithValidationError}
reportWithMultiLineValidationError := Report{
"bad.xml",
"/fake/path/bad.xml",
false,
errors.New("Unable to parse keys:\nkey1\nkey2"),
}

reports := []Report{reportNoValidationError, reportWithValidationError, reportWithMultiLineValidationError}

stdoutReporter := StdoutReporter{}
err := stdoutReporter.Print(reports)
Expand Down
18 changes: 17 additions & 1 deletion pkg/reporter/stdout_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package reporter

import (
"fmt"
"strings"

"github.com/fatih/color"
)

Expand All @@ -16,7 +18,8 @@ func (sr StdoutReporter) Print(reports []Report) error {
if !report.IsValid {
color.Set(color.FgRed)
fmt.Println(" × " + report.FilePath)
fmt.Printf(" error: %v\n", report.ValidationError)
paddedString := sr.padErrorString(report.ValidationError.Error())
fmt.Printf(" error: %v\n", paddedString)
color.Unset()
failureCount = failureCount + 1
} else {
Expand All @@ -27,3 +30,16 @@ func (sr StdoutReporter) Print(reports []Report) error {
fmt.Printf("Summary: %d succeeded, %d failed\n", successCount, failureCount)
return nil
}

// padErrorString adds padding to every newline in the error
// string, except the first line and removes any trailing newlines
// or spaces
func (sr StdoutReporter) padErrorString(errS string) string {
errS = strings.TrimSpace(errS)
lines := strings.Split(errS, "\n")
for idx := 1; idx < len(lines); idx++ {
lines[idx] = " " + lines[idx]
}
paddedErr := strings.Join(lines, "\n")
return paddedErr
}
18 changes: 18 additions & 0 deletions pkg/validator/properties.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package validator

import (
"github.com/magiconair/properties"
)

type PropValidator struct{}

// Validate implements the Validator interface by attempting to
// parse a byte array of properties
func (pv PropValidator) Validate(b []byte) (bool, error) {
l := &properties.Loader{Encoding: properties.UTF8}
_, err := l.LoadBytes(b)
if err != nil {
return false, err
}
return true, nil
}
2 changes: 2 additions & 0 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ var testData = []struct {
{"validToml", []byte("name = 123"), true, TomlValidator{}},
{"validIni", []byte(`{[Version]\nCatalog=hidden\n}`), true, IniValidator{}},
{"invalidIni", []byte(`\nCatalog hidden\n`), false, IniValidator{}},
{"validProperties", []byte("key=value\nkey2=${key}"), true, PropValidator{}},
{"invalidProperties", []byte("key=${key}"), false, PropValidator{}},
}

func Test_ValidationInput(t *testing.T) {
Expand Down
46 changes: 46 additions & 0 deletions test/fixtures/good.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# You are reading a comment in ".properties" file.
! The exclamation mark can also be used for comments.
# Lines with "properties" contain a key and a value separated by a delimiting character.
# There are 3 delimiting characters: '=' (equal), ':' (colon) and whitespace (space, \t and \f).
website = https://en.wikipedia.org/
language : English
topic .properties files
# A word on a line will just create a key with no value.
empty
# White space that appears between the key, the value and the delimiter is ignored.
# This means that the following are equivalent (other than for readability).
hello=hello
hello = hello
# Keys with the same name will be overwritten by the key that is the furthest in a file.
# For example the final value for "duplicateKey" will be "second".
duplicateKey = first
duplicateKey = second
# To use the delimiter characters inside a key, you need to escape them with a \.
# However, there is no need to do this in the value.
delimiterCharacters\:\=\ = This is the value for the key "delimiterCharacters\:\=\ "
# Adding a \ at the end of a line means that the value continues to the next line.
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.
# 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"
# If the number of \ is odd, then the next line is included in the value.
# In the following example, the value for "oddKey" is "This is line one and\#This is line two".
oddKey = This is line one and\\\
# This is line two
# White space characters are removed before each line.
# Make sure to add your spaces before your \ if you need them on the next line.
# In the following example, the value for "welcome" is "Welcome to Wikipedia!".
welcome = Welcome to \
Wikipedia!
# If you need to add newlines and carriage returns, they need to be escaped using \n and \r respectively.
# You can also optionally escape tabs with \t for readability purposes.
valueWithEscapes = This is a newline\n and a carriage return\r and a tab\t.
# You can also use Unicode escape characters (maximum of four hexadecimal digits).
# In the following example, the value for "encodedHelloInJapanese" is "こんにちは".
encodedHelloInJapanese = \u3053\u3093\u306b\u3061\u306f
# But with more modern file encodings like UTF-8, you can directly use supported characters.
helloInJapanese = こんにちは
48 changes: 48 additions & 0 deletions test/fixtures/subdir2/bad.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# You are reading a comment in ".properties" file.
! The exclamation mark can also be used for comments.
# Lines with "properties" contain a key and a value separated by a delimiting character.
# There are 3 delimiting characters: '=' (equal), ':' (colon) and whitespace (space, \t and \f).
website = https://en.wikipedia.org/
language : English
topic .properties files
# A word on a line will just create a key with no value.
empty
# White space that appears between the key, the value and the delimiter is ignored.
# This means that the following are equivalent (other than for readability).
hello=hello
hello = hello
# Keys with the same name will be overwritten by the key that is the furthest in a file.
# For example the final value for "duplicateKey" will be "second".
duplicateKey = first
duplicateKey = second
# To use the delimiter characters inside a key, you need to escape them with a \.
# However, there is no need to do this in the value.
delimiterCharacters\:\=\ = This is the value for the key "delimiterCharacters\:\=\ "
# Adding a \ at the end of a line means that the value continues to the next line.
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.
# 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"
# If the number of \ is odd, then the next line is included in the value.
# In the following example, the value for "oddKey" is "This is line one and\#This is line two".
oddKey = This is line one and\\\
# This is line two
# White space characters are removed before each line.
# Make sure to add your spaces before your \ if you need them on the next line.
# In the following example, the value for "welcome" is "Welcome to Wikipedia!".
welcome = Welcome to \
Wikipedia!
# If you need to add newlines and carriage returns, they need to be escaped using \n and \r respectively.
# You can also optionally escape tabs with \t for readability purposes.
valueWithEscapes = This is a newline\n and a carriage return\r and a tab\t.
# You can also use Unicode escape characters (maximum of four hexadecimal digits).
# In the following example, the value for "encodedHelloInJapanese" is "こんにちは".
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}

0 comments on commit db9e556

Please sign in to comment.