Skip to content

Commit

Permalink
added support for global / local prefix
Browse files Browse the repository at this point in the history
It is now possible to set default prefix in the SSMSerializer.
This will be applied by default if not explicit set in tags.
  • Loading branch information
mariotoffia committed Oct 21, 2020
1 parent 9d1827e commit e677ffa
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 53 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"cSpell.words": [
"AWSCURRENT",
"AWSPREVIOUS",
"Childs",
"Msgf",
"Printf",
"Upsert",
Expand All @@ -20,6 +21,7 @@
"g",
"gurka",
"gördis",
"hasnoprefix",
"hunden",
"kalle",
"keyid",
Expand Down
33 changes: 27 additions & 6 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
"github.com/rs/zerolog/log"
)

// Parser parses structs and produces a tree of nodes
// Parser parses struct(s) and produces a tree of nodes
// along with fields, values and possibly tags.
type Parser struct {
// Contains a registration of what name of tag
// and the parser instance to invoke. for exmaple
// and the parser instance to invoke. for example
// 'pms' and the parameter store tag parser instance.
tagparsers map[string]TagParser
// The service currently using this parser
Expand All @@ -25,6 +25,27 @@ type Parser struct {
}

// New creates a new instrance of the Parser
//
// When a _prefix_ is passed, it acts as a default prefix if no
// prefix is specified in the tag. Prefix operates under two modes
// _Local_ and _Global_.
//
// .Local vs Global Mode
// [cols="1,1,4"]
// |===
// |Mode |Example |Description
//
// |Local
// |my-local-prefix/nested
// |This will render environment/service/my-local-prefix/nested/property. E.g. dev/tes-service/my-local-prefix/nested/password
//
// |Global
// |/my-global-prefix/nested
// |This will render environment/my-global-prefix/nested/property. E.g. dev/my-global-prefix/nested/password
//
// |===
//
// NOTE: When global prefix, the _service_ element is eliminated (in order to have singeltons).
func New(service string, environment string, prefix string) *Parser {
return &Parser{
tagparsers: map[string]TagParser{},
Expand All @@ -35,13 +56,13 @@ func New(service string, environment string, prefix string) *Parser {
}

// RegisterTagParser registers a tag parser that parses
// a speicfied tag.
// a specified tag.
func (p *Parser) RegisterTagParser(tag string, parser TagParser) *Parser {
p.tagparsers[tag] = parser
return p
}

// Parse will parse the inparam value. It may either be a type
// Parse will parse the in param value. It may either be a type
// such as var s MyStruct or a instance such as s := MyStruct{...}
// and then do reflect.ValueOf(&s) and send that to Parse.
func (p *Parser) Parse(v reflect.Value) (*StructNode, error) {
Expand All @@ -63,9 +84,9 @@ func (p *Parser) Parse(v reflect.Value) (*StructNode, error) {
return node, nil
}

// NodesToParameterMap grabs all tag FullNames on nodes that do have atleast
// NodesToParameterMap grabs all tag FullNames on nodes that do have at least
// one tag in the StructNode.Tag property. The tags full name is the associated
// with the node itself. This is to gain a more accessable structure to seach
// with the node itself. This is to gain a more accessable structure to search
// for nodes. Note if multiple tag FullName are present for same StructNode,
// multiple entries in the paths map will be created, one per tag.FullName.
func NodesToParameterMap(node *StructNode,
Expand Down
113 changes: 108 additions & 5 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package parser

import (
"fmt"
"reflect"
"testing"

"github.com/mariotoffia/ssm/internal/testsupport"
"github.com/stretchr/testify/assert"
)

func TestSingleStringStruct(t *testing.T) {
var test testsupport.SingleStringPmsStruct
// TestFieldWithPrefixNoGlobal makes sure that inline prefix
// with no slash at the beginning gets inserted after service
// and before field name.
func TestFieldWithPrefixNoGlobal(t *testing.T) {

type Test struct {
Name string `pms:"test, prefix=simple,tag1=nanna banna panna"`
}

var test Test
tp := reflect.ValueOf(&test)
node, err := New("test-service", "dev", "").
RegisterTagParser("pms", NewTagParser([]string{})).
Expand All @@ -19,11 +27,106 @@ func TestSingleStringStruct(t *testing.T) {
assert.Equal(t, nil, err)
}

DumpNode(node)
assert.Equal(t,
"/dev/test-service/simple",
node.Childs[0].Tag["pms"].GetNamed()["prefix"],
)

// DumpNode(node)
}

// TestFieldWithPrefixGlobal makes sure that inline prefix
// with a slash at the beginning gets inserted after environment
// and removes the service name. It is appended directly after
// the environment and thus is a _global_ parameter.
func TestFieldWithPrefixGlobal(t *testing.T) {

type Test struct {
Name string `pms:"test, prefix=/global/simple,tag1=nanna banna panna"`
}

var test Test
tp := reflect.ValueOf(&test)
node, err := New("test-service", "dev", "").
RegisterTagParser("pms", NewTagParser([]string{})).
Parse(tp)

if err != nil {
assert.Equal(t, nil, err)
}

assert.Equal(t,
"/dev/global/simple",
node.Childs[0].Tag["pms"].GetNamed()["prefix"],
)

// DumpNode(node)
}

func TestFieldNoGlobalPrefixInParser(t *testing.T) {

type Test struct {
HasLocalPrefix string `pms:"hasprefix, prefix=simple"`
HasNoPrefix string `pms:"hasnoprefix"`
}

var test Test
tp := reflect.ValueOf(&test)
node, err := New("test-service", "dev", "parser-prefix").
RegisterTagParser("pms", NewTagParser([]string{})).
Parse(tp)

if err != nil {
assert.Equal(t, nil, err)
}

prefix1 := node.Childs[0].Tag["pms"].GetNamed()["prefix"]
prefix2 := node.Childs[1].Tag["pms"].GetNamed()["prefix"]

assert.Equal(t, "/dev/test-service/simple", prefix1)
assert.Equal(t, "/dev/test-service/parser-prefix", prefix2)
//DumpNode(node)
}

func TestFieldGlobalPrefixInParser(t *testing.T) {

type Test struct {
HasLocalPrefix string `pms:"hasprefix, prefix=simple"`
HasNoPrefix string `pms:"hasnoprefix"`
}

var test Test
tp := reflect.ValueOf(&test)
node, err := New("test-service", "dev", "/global/parser-prefix").
RegisterTagParser("pms", NewTagParser([]string{})).
Parse(tp)

if err != nil {
assert.Equal(t, nil, err)
}

prefix1 := node.Childs[0].Tag["pms"].GetNamed()["prefix"]
prefix2 := node.Childs[1].Tag["pms"].GetNamed()["prefix"]

assert.Equal(t, "/dev/test-service/simple", prefix1)
assert.Equal(t, "/dev/global/parser-prefix", prefix2)

fmt.Println(prefix1)
fmt.Println(prefix2)
//DumpNode(node)
}

func TestNestedPropertyJsonExpanded(t *testing.T) {
var test testsupport.MyDbServiceConfig

type Test struct {
Connection struct {
User string `json:"user"`
Password string `json:"password,omitempty"`
Timeout int `json:"timeout"`
} `pms:"bubbibobbo"`
}

var test Test
tp := reflect.ValueOf(&test)
node, err := New("test-service", "dev", "").
RegisterTagParser("pms", NewTagParser([]string{})).
Expand Down
14 changes: 10 additions & 4 deletions parser/tagparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (p *tagParser) ParseTagString(tagstring string,
case "name":
st.Named["name"] = strings.ToLower(kv[1])
case "prefix":
st.Named["prefix"] = RenderPrefix(kv[1], env, "")
st.Named["prefix"] = RenderPrefix(kv[1], env, svc)
break
default:
if stringInSlice(kv[0], p.named) {
Expand All @@ -74,7 +74,7 @@ func stringInSlice(a string, list []string) bool {
return false
}

// RenderPrefix renders a prefix based on the inparam strings
// RenderPrefix renders a prefix based on the in param strings
func RenderPrefix(prefix string, env string, svc string) string {
if strings.HasPrefix(env, "/") {
env = env[1:]
Expand All @@ -88,8 +88,12 @@ func RenderPrefix(prefix string, env string, svc string) string {
if strings.HasSuffix(svc, "/") {
svc = svc[:1]
}
if prefix != "" && !strings.HasPrefix(prefix, "/") {
prefix = "/" + prefix
if prefix != "" {
if strings.HasPrefix(prefix, "/") {
svc = "" // Global prefix when starting with '/'
} else {
prefix = "/" + prefix
}
}
if strings.HasSuffix(prefix, "/") {
prefix = prefix[:1]
Expand All @@ -98,8 +102,10 @@ func RenderPrefix(prefix string, env string, svc string) string {
if prefix == "" {
return strings.ToLower(fmt.Sprintf("/%s/%s", env, svc))
}

if svc == "" {
return strings.ToLower(fmt.Sprintf("/%s%s", env, prefix))
}

return strings.ToLower(fmt.Sprintf("/%s/%s%s", env, svc, prefix))
}
16 changes: 8 additions & 8 deletions parser/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
type TagParser interface {
// ParseTagString is implemented by the user of the parser. The tagstring is the complete
// tag string for the registered tag name. The parameter prefix is the navigation
// when having sub structs separated with slashes. In this way the parser may
// add it as prefix on some occations. The env is the inparam environment data and
// when having sub struct separated with slashes. In this way the parser may
// add it as prefix on some occasions. The env is the in param environment data and
// that can be anything. The svc is the service currently using this parser.
//
// The ParseTagString returns either a StructTag (struct) or an error.
Expand Down Expand Up @@ -52,14 +52,14 @@ type StructTagImpl struct {
FullName string
}

// Named gets the named parametres indexed by name. If not key = value,
// GetNamed gets the named parametres indexed by name. If not key = value,
// instead just a value the key is "" and the value is the value.
func (t *StructTagImpl) GetNamed() map[string]string { return t.Named }

// Tags conatains all tags that do not have a special meaning.
// GetTags contains all tags that do not have a special meaning.
func (t *StructTagImpl) GetTags() map[string]string { return t.Tags }

// FullName is the fully qualified name, i.e. prefix + name
// GetFullName is the fully qualified name, i.e. prefix + name
func (t *StructTagImpl) GetFullName() string { return t.FullName }

// StructNode is a node representing a struct
Expand Down Expand Up @@ -90,7 +90,7 @@ type StructNode struct {
// HasChildren returns true if this node has children
func (s *StructNode) HasChildren() bool { return len(s.Childs) > 0 }

// HasTag checks if inparam tag name exists int the Tag map
// HasTag checks if in param tag name exists int the Tag map
func (s *StructNode) HasTag(tag string) bool {
_, ok := s.Tag[tag]
return ok
Expand Down Expand Up @@ -147,8 +147,8 @@ func (s *StructNode) ToString(children bool) string {
ot, s.Type.Name(), s.FqName, s.Tag, owner)

if children && s.Childs != nil && len(s.Childs) > 0 {
for _, chld := range s.Childs {
str += fmt.Sprintf("[%s --> %s]", str, chld.ToString(children))
for _, child := range s.Childs {
str += fmt.Sprintf("[%s --> %s]", str, child.ToString(children))
}
}

Expand Down
Loading

0 comments on commit e677ffa

Please sign in to comment.