Skip to content
Snippets Groups Projects
Commit b7e24f26 authored by Semyon Krestyaninov's avatar Semyon Krestyaninov :dog2:
Browse files

wip

parent 076fdd0b
No related branches found
No related tags found
No related merge requests found
......@@ -3,7 +3,9 @@ package template
import (
"bytes"
"context"
"text/template"
html "html/template"
"io"
text "text/template"
"git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/content"
......@@ -11,32 +13,57 @@ import (
"git.perx.ru/perxis/perxis-go/pkg/spaces"
)
type Template interface {
Execute(w io.Writer, data any) error
}
type Builder struct {
ctx context.Context
cnt *content.Content
SpaceID string
EnvID string
CollID string
data map[string]interface{}
data map[string]any
// Для кеширования запросов
space *spaces.Space
environment *environments.Environment
collection *collections.Collection
// templateFunc парсит строку и возвращает шаблон для подстановки значений
templateFunc func(data string) (Template, error)
}
func NewBuilder(cnt *content.Content, space, env, col string) *Builder {
return &Builder{
func NewBuilder(cnt *content.Content, space, env, col string) Builder {
b := Builder{
ctx: context.Background(),
cnt: cnt,
SpaceID: space,
EnvID: env,
CollID: col,
}
b.templateFunc = func(data string) (Template, error) {
return text.New("main_text").Funcs(b.getFuncs()).Parse(data)
}
return b
}
func (b *Builder) getFuncs() template.FuncMap {
return template.FuncMap{
func NewHTMLBuilder(cnt *content.Content, space, env, col string) Builder {
b := Builder{
ctx: context.Background(),
cnt: cnt,
SpaceID: space,
EnvID: env,
CollID: col,
}
b.templateFunc = func(data string) (Template, error) {
return html.New("main_html").Funcs(b.getFuncs()).Parse(data)
}
return b
}
func (b *Builder) getFuncs() map[string]any {
return map[string]any{
"lookup": getLookup(b),
"system": getSystem(b),
}
......@@ -84,14 +111,9 @@ func (b *Builder) Context() context.Context {
return b.ctx
}
func (b *Builder) Template() *template.Template {
return template.New("main").Funcs(b.getFuncs())
}
func (b *Builder) Execute(str string, data ...any) (string, error) {
t := b.Template()
buf := new(bytes.Buffer)
t, err := t.Parse(str)
t, err := b.templateFunc(str)
if err != nil {
return "", err
}
......@@ -102,14 +124,13 @@ func (b *Builder) Execute(str string, data ...any) (string, error) {
}
func (b *Builder) ExecuteList(str []string, data ...any) ([]string, error) {
t := b.Template()
result := make([]string, len(str))
buffer := new(bytes.Buffer)
for i, tmpl := range str {
if tmpl == "" {
continue
}
t, err := t.Parse(tmpl)
t, err := b.templateFunc(tmpl)
if err != nil {
return []string{}, err
}
......
......@@ -19,16 +19,17 @@ import (
func TestBuilder_Execute(t *testing.T) {
tests := []struct {
name string
SpaceID string
EnvID string
CollID string
str string
data any
want any
wantErr bool
name string
SpaceID string
EnvID string
CollID string
str string
data any
want any
htmlBuilder bool
wantErr bool
getCnt func() (cnt *content.Content, assertExpectations func(t *testing.T))
getCnt func(t *testing.T) *content.Content
}{
{name: "error", str: "hello {{ .a }}", data: "world", want: "", wantErr: true},
{name: "empty", str: "", data: "", want: "", wantErr: false},
......@@ -36,100 +37,164 @@ func TestBuilder_Execute(t *testing.T) {
{name: "#2", str: "{{ . }}", data: "world", want: "world", wantErr: false},
{name: "#3 ", str: "", data: "world", want: "", wantErr: false},
{name: "#4 ", str: "hello", data: "world", want: "hello", wantErr: false},
{name: "lookup", SpaceID: "space", EnvID: "env", str: "hello, {{ lookup \"secrets.dev.key\" }}", data: "", want: "hello, Luk", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
itemsSvc := &mocksitems.Items{}
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"key": "Luk",
},
}, nil).Once()
return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
}},
{name: "lookup with slice", SpaceID: "space", EnvID: "env", str: "numbers {{ lookup \"secrets.dev.slice\" }}", data: "", want: "numbers [1 2 3]", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
itemsSvc := &mocksitems.Items{}
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"slice": []int{1, 2, 3},
},
}, nil).Once()
return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
}},
{name: "lookup with empty Data", SpaceID: "space", EnvID: "env", str: "numbers {{ lookup \"secrets.dev.slice\" }}", data: "", want: "numbers <no value>", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
itemsSvc := &mocksitems.Items{}
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{},
}, nil).Once()
return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
}},
{name: "lookup with incorrect field", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.dev.incorrect\" }}", data: "", want: "hello <no value>", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
itemsSvc := &mocksitems.Items{}
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"key": "1234",
},
}, nil).Once()
return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
}},
{name: "lookup not found", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.prod.pass\" }}", data: "", want: "", wantErr: true, getCnt: func() (*content.Content, func(t *testing.T)) {
itemsSvc := &mocksitems.Items{}
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").Return(nil, errors.New("not found")).Once()
return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
}},
{
name: "lookup",
SpaceID: "space",
EnvID: "env",
str: "hello, {{ lookup \"secrets.dev.key\" }}",
data: "",
want: "hello, Luk",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"key": "Luk",
},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
},
{
name: "lookup with slice",
SpaceID: "space",
EnvID: "env",
str: "numbers {{ lookup \"secrets.dev.slice\" }}",
data: "",
want: "numbers [1 2 3]",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"slice": []int{1, 2, 3},
},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
},
{
name: "lookup with empty Data",
SpaceID: "space",
EnvID: "env",
str: "numbers {{ lookup \"secrets.dev.slice\" }}",
data: "",
want: "numbers <no value>",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
},
{
name: "lookup with incorrect field",
SpaceID: "space",
EnvID: "env",
str: "hello {{ lookup \"secrets.dev.incorrect\" }}",
data: "",
want: "hello <no value>",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]interface{}{
"id": "dev",
"key": "1234",
},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
},
{
name: "lookup not found",
SpaceID: "space",
EnvID: "env",
str: "hello {{ lookup \"secrets.prod.pass\" }}",
data: "",
want: "",
wantErr: true,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").
Return(nil, errors.New("not found")).Once()
return &content.Content{Items: itemsSvc}
},
},
{name: "lookup without itemID", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.pass\" }}", data: "", want: "", wantErr: true},
{name: "system#1", SpaceID: "space", str: "hello {{ system.SpaceID }}", data: "", want: "hello space", wantErr: false},
{name: "system space", SpaceID: "space", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
spsSvc := &spsmocks.Spaces{}
{name: "system space", SpaceID: "space", getCnt: func(t *testing.T) *content.Content {
spsSvc := spsmocks.NewSpaces(t)
spsSvc.On("Get", context.Background(), "space").Return(&spaces.Space{Description: "description"}, nil).Once()
return &content.Content{Spaces: spsSvc}, func(t *testing.T) { spsSvc.AssertExpectations(t) }
return &content.Content{Spaces: spsSvc}
}, str: "{{ system.Space.Description }}", want: "description", wantErr: false},
{name: "system environment", SpaceID: "space", EnvID: "env", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
envsSvc := &envsmocks.Environments{}
{name: "system environment", SpaceID: "space", EnvID: "env", getCnt: func(t *testing.T) *content.Content {
envsSvc := envsmocks.NewEnvironments(t)
envsSvc.On("Get", context.Background(), "space", "env").Return(&environments.Environment{Aliases: []string{"master"}}, nil).Once()
return &content.Content{Environments: envsSvc}, func(t *testing.T) { envsSvc.AssertExpectations(t) }
return &content.Content{Environments: envsSvc}
}, str: "{{ system.Environment.Aliases }}", want: "[master]", wantErr: false},
{name: "system collection", SpaceID: "space", EnvID: "env", CollID: "col", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
collsSvc := &colsmocks.Collections{}
collsSvc.On("Get", context.Background(), "space", "env", "col").Return(&collections.Collection{Name: "cars"}, nil).Once()
return &content.Content{Collections: collsSvc}, func(t *testing.T) { collsSvc.AssertExpectations(t) }
}, str: "{{ system.Collection.Name }}", want: "cars", wantErr: false},
{
name: "system collection",
SpaceID: "space",
EnvID: "env",
CollID: "col",
getCnt: func(t *testing.T) *content.Content {
collsSvc := colsmocks.NewCollections(t)
collsSvc.On("Get", context.Background(), "space", "env", "col").
Return(&collections.Collection{Name: "cars"}, nil).Once()
return &content.Content{Collections: collsSvc}
},
str: "{{ system.Collection.Name }}",
want: "cars",
wantErr: false,
},
{name: "system without account", SpaceID: "space", str: "hello {{ system.Organization.Name }}", want: "", wantErr: true},
{
name: "with html builder",
str: "{{ . }}",
data: "<script>alert(localStorage.secret)</script>",
want: "&lt;script&gt;alert(localStorage.secret)&lt;/script&gt;",
htmlBuilder: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cnt *content.Content
if tt.getCnt != nil {
var checkFn func(*testing.T)
cnt, checkFn = tt.getCnt()
defer checkFn(t)
cnt = tt.getCnt(t)
}
b := &Builder{
ctx: context.Background(),
cnt: cnt,
SpaceID: tt.SpaceID,
EnvID: tt.EnvID,
CollID: tt.CollID,
var b Builder
if tt.htmlBuilder {
b = NewHTMLBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
} else {
b = NewBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
}
got, err := b.Execute(tt.str, tt.data)
if tt.wantErr == true {
assert.Error(t, err)
......@@ -187,16 +252,11 @@ func TestBuilder_ExecuteList(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
itemsSvc := &mocksitems.Items{}
itemsSvc := mocksitems.NewItems(t)
if tt.itemsCall != nil {
tt.itemsCall(itemsSvc)
}
b := &Builder{
ctx: context.Background(),
cnt: &content.Content{Items: itemsSvc},
SpaceID: tt.SpaceID,
EnvID: tt.EnvID,
}
b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
got, err := b.ExecuteList(tt.str, tt.data)
if tt.wantErr == true {
......@@ -205,7 +265,6 @@ func TestBuilder_ExecuteList(t *testing.T) {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
itemsSvc.AssertExpectations(t)
})
}
}
......@@ -256,16 +315,11 @@ func TestBuilder_ExecuteMap(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
itemsSvc := &mocksitems.Items{}
itemsSvc := mocksitems.NewItems(t)
if tt.itemsCall != nil {
tt.itemsCall(itemsSvc)
}
b := &Builder{
ctx: context.Background(),
cnt: &content.Content{Items: itemsSvc},
SpaceID: tt.SpaceID,
EnvID: tt.EnvID,
}
b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
got, err := b.ExecuteMap(tt.str, tt.data)
if tt.wantErr == true {
......@@ -274,7 +328,6 @@ func TestBuilder_ExecuteMap(t *testing.T) {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
itemsSvc.AssertExpectations(t)
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment