Skip to content

Commit

Permalink
- Breaking: AWS parameter store paths now require dot in property nam…
Browse files Browse the repository at this point in the history
…e to indicate path in key (/).

- New dot-based property name format used to enable grouping and structure output.
- Added output tests.
  • Loading branch information
kristofferahl committed Jan 13, 2024
1 parent 079294c commit ea7a4c6
Show file tree
Hide file tree
Showing 20 changed files with 483 additions and 63 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
- [x] Option to sort output keys (dotenv)
- [x] UI command with web interface that allows comparing results between different exports
- [x] Support multiple paths with formatting for outputs (using --path= should override all paths specified in manifest)
- [x] Feature: Dot-based property name format for grouping and to enable structured output in json etc (name: Translation.TravelwebUrl -> {"Translation": {"TravelWebUrl": "..."}})?

- [ ] (in-progress) Initial round of real world testing

- [ ] More tests on multiple levels and components (e2e, unit etc)
- [ ] Feature: New dot-format for propety names to enable structured output in json etc (name: Translation.TravelwebUrl -> {"translation": {"travelWebUrl": "..."}})?
- [ ] Feature: Validation options, Value type (Int, String, Bool etc)
- [ ] Feature: Validation options, Value match Regexp (.\*)
- [ ] Feature: Validation options, String values - MinLength: 3, MaxLength: 16 etc
Expand Down
19 changes: 15 additions & 4 deletions internal/output/dotenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import (
)

type Dotenv struct {
Quote bool `yaml:"quote"`
Sort bool `yaml:"sort"`
Sort bool `yaml:"sort"`
Quote bool `yaml:"quote"`
Uppercase bool `yaml:"uppercase"`
WordSeparator string `yaml:"wordSeparator"`
PathSeparator string `yaml:"pathSeparator"`
}

func (o Dotenv) Type() string {
Expand All @@ -20,7 +23,11 @@ func (o Dotenv) Type() string {

func NewDotenv() Dotenv {
return Dotenv{
Quote: true,
Sort: false,
Quote: true,
Uppercase: true,
WordSeparator: "_",
PathSeparator: "_",
}
}

Expand All @@ -33,7 +40,11 @@ func (o Dotenv) Write(w io.Writer, keys []string, remap map[string]string, value
if remapped, ok := remap[k]; ok && remapped != "" {
key = remapped
} else {
key = utils.CamelCaseSplitToUpperJoinByUnderscore(k)
key = utils.FormatKey(k, utils.Formatting{
Uppercase: o.Uppercase,
WordSeparator: o.WordSeparator,
PathSeparator: o.PathSeparator,
})
}

value := strings.TrimSuffix(values[k], "\n")
Expand Down
134 changes: 134 additions & 0 deletions internal/output/dotenv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package output_test

import (
"io"
"os"
"strings"

pio "github.com/dotnetmentor/racoon/internal/io"
"github.com/dotnetmentor/racoon/internal/output"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Dotenv", func() {
Describe("Write", func() {
keys := []string{
"Foo",
"Bar",
"CamelCasedProperty",
"Path.Based.Property",
"Dotnet.Structured.FormattedProperty",
}
values := map[string]string{
"Foo": "Bar",
"Bar": "Foo",
"CamelCasedProperty": "Value",
"Path.Based.Property": "Value",
"Dotnet.Structured.FormattedProperty": "Value",
}

When("writing with defaults", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewDotenv()
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("has qouted property", func() {
Expect(result).To(ContainSubstring("FOO=\"Bar\""))
})

It("keeps the sort order", func() {
lines := strings.Split(result, "\n")
Expect(lines[0]).To(ContainSubstring("FOO="))
Expect(lines[1]).To(ContainSubstring("BAR="))
Expect(lines[2]).To(ContainSubstring("CAMEL_CASED_PROPERTY="))
Expect(lines[3]).To(ContainSubstring("PATH_BASED_PROPERTY="))
Expect(lines[4]).To(ContainSubstring("DOTNET_STRUCTURED_FORMATTED_PROPERTY="))
})
})

When("writing without quoutes", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewDotenv()
o.Quote = false
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("has unquoted property", func() {
Expect(result).To(ContainSubstring("FOO=Bar"))
})
})

When("writing with sorting enabled", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewDotenv()
o.Sort = true
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("sorts the output", func() {
lines := strings.Split(result, "\n")
Expect(lines[0]).To(ContainSubstring("BAR="))
Expect(lines[1]).To(ContainSubstring("CAMEL_CASED_PROPERTY="))
Expect(lines[2]).To(ContainSubstring("DOTNET_STRUCTURED_FORMATTED_PROPERTY="))
Expect(lines[3]).To(ContainSubstring("FOO="))
Expect(lines[4]).To(ContainSubstring("PATH_BASED_PROPERTY="))
})
})

When("writing with path separator", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewDotenv()
o.PathSeparator = "__"
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("sorts the output", func() {
Expect(result).To(ContainSubstring("PATH__BASED__PROPERTY="))
Expect(result).To(ContainSubstring("DOTNET__STRUCTURED__FORMATTED_PROPERTY="))
})
})

When("writing dotnet style output", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewDotenv()
o.Uppercase = false
o.WordSeparator = ""
o.PathSeparator = "__"
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("sorts the output", func() {
Expect(result).To(ContainSubstring("Path__Based__Property="))
Expect(result).To(ContainSubstring("Dotnet__Structured__FormattedProperty="))
})
})
})
})
40 changes: 33 additions & 7 deletions internal/output/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,56 @@ import (
"encoding/json"
"io"
"strings"

"github.com/dotnetmentor/racoon/internal/utils"
)

type Json struct {
Stuctured bool `yaml:"structured"`
}

func (o Json) Type() string {
return "json"
}

func NewJson() Json {
return Json{}
return Json{
Stuctured: true,
}
}
func (o Json) Write(w io.Writer, keys []string, remap map[string]string, values map[string]string) {
jo := map[string]string{}
jo := make(Dict)
for _, k := range keys {
var key string
var keyParts []string
if remapped, ok := remap[k]; ok && remapped != "" {
key = remapped
keyParts = []string{remapped}
} else {
key = k
if o.Stuctured {
keyParts = utils.SplitPath(k)
} else {
keyParts = []string{k}
}
}

value := strings.TrimSuffix(values[k], "\n")
jo[key] = value
set(jo, keyParts, value)
}
if err := json.NewEncoder(w).Encode(jo); err != nil {
panic(err)
}
}

type Dict map[string]interface{}

func set(d Dict, keys []string, value interface{}) {
if len(keys) == 1 {
d[keys[0]] = value
return
}
v, ok := d[keys[0]]
if !ok {
v = Dict{}
d[keys[0]] = v
}
json.NewEncoder(w).Encode(jo)
set(v.(Dict), keys[1:], value)
}
64 changes: 64 additions & 0 deletions internal/output/json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package output_test

import (
"io"
"os"

pio "github.com/dotnetmentor/racoon/internal/io"
"github.com/dotnetmentor/racoon/internal/output"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Json", func() {
Describe("Write", func() {
keys := []string{
"Foo",
"Bar",
"CamelCasedProperty",
"Path.Based.Property",
"Dotnet.Structured.FormattedProperty",
}
values := map[string]string{
"Foo": "Bar",
"Bar": "Foo",
"CamelCasedProperty": "Value",
"Path.Based.Property": "Value",
"Dotnet.Structured.FormattedProperty": "Value",
}

When("writing with defaults", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewJson()
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("has structured output", func() {
Expect(result).To(MatchJSON(`{"Bar":"Foo","CamelCasedProperty":"Value","Dotnet":{"Structured":{"FormattedProperty":"Value"}},"Foo":"Bar","Path":{"Based":{"Property":"Value"}}}`))
})
})

When("writing unstructured json", func() {
var result string

BeforeEach(func() {
_, stdout, _ := pio.Buffered(os.Stdin)
o := output.NewJson()
o.Stuctured = false
o.Write(stdout, keys, map[string]string{}, values)
b, _ := io.ReadAll(stdout)
result = string(b)
})

It("has unstructured output", func() {
Expect(result).To(MatchJSON(`{"Bar":"Foo","CamelCasedProperty":"Value","Dotnet.Structured.FormattedProperty":"Value","Foo":"Bar","Path.Based.Property":"Value"}`))
})
})
})
})
13 changes: 13 additions & 0 deletions internal/output/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package output_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestOutput(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Output Suite")
}
15 changes: 13 additions & 2 deletions internal/output/tfvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ import (
)

type Tfvars struct {
Lowercase bool `yaml:"lowercase"`
WordSeparator string `yaml:"wordSeparator"`
PathSeparator string `yaml:"pathSeparator"`
}

func (o Tfvars) Type() string {
return "tfvars"
}

func NewTfvars() Tfvars {
return Tfvars{}
return Tfvars{
Lowercase: true,
WordSeparator: "_",
PathSeparator: "_",
}
}

func (o Tfvars) Write(w io.Writer, keys []string, remap map[string]string, values map[string]string) {
Expand All @@ -25,7 +32,11 @@ func (o Tfvars) Write(w io.Writer, keys []string, remap map[string]string, value
if remapped, ok := remap[k]; ok && remapped != "" {
key = remapped
} else {
key = utils.CamelCaseSplitToLowerJoinByUnderscore(k)
key = utils.FormatKey(k, utils.Formatting{
Lowercase: o.Lowercase,
WordSeparator: o.WordSeparator,
PathSeparator: o.PathSeparator,
})
}

value := strings.TrimSuffix(values[k], "\n")
Expand Down
Loading

0 comments on commit ea7a4c6

Please sign in to comment.