From b1eea4b7a36c0b21dbf0b70157d92725045c1061 Mon Sep 17 00:00:00 2001 From: Krzysztof Reczek Date: Thu, 26 Nov 2020 20:58:21 +0100 Subject: [PATCH] Add view tags (#11) --- README.md | 4 + pkg/view/render.go | 35 +++++ pkg/view/view.go | 10 ++ pkg/view/view_test.go | 328 ++++++++++++++++++++++++++++++++++++++++++ pkg/view/yaml.go | 4 + pkg/yaml/yaml.go | 1 + 6 files changed, 382 insertions(+) create mode 100644 pkg/view/view_test.go diff --git a/README.md b/README.md index 0a7b807..316328d 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ View consists of: * title * component styles - styles are applied to the components by matching first of component tags with style ids * additional styling (i.e. line color) +* tags - if specified, view will contain only components tagged with one of the view tags. When no tag is defined, all components will be included in the rendered view. In order to instantiate default view, use the view builder: ```go @@ -110,6 +111,7 @@ v := view.NewView(). WithFontColor(color.Black). Build(), ). + WithTag("TAG"). Build() ``` @@ -124,6 +126,8 @@ view: background_color: ffffffff font_color: 000000ff border_color: 000000ff + tags: + - TAG ``` ```go diff --git a/pkg/view/render.go b/pkg/view/render.go index fdff167..93055ac 100644 --- a/pkg/view/render.go +++ b/pkg/view/render.go @@ -18,12 +18,31 @@ func (v View) render(s model.Structure) string { sb.WriteString(buildSkinParamRectangle(s.id, s.backgroundColor, s.fontColor, s.borderColor)) } + excludedComponentIds := map[string]struct{}{} for _, c := range s.Components { + if !v.hasTag(c.Tags...) { + excludedComponentIds[c.ID] = struct{}{} + } + } + + for _, c := range s.Components { + _, excluded := excludedComponentIds[c.ID] + if excluded { + continue + } sb.WriteString(buildComponent(c)) } for src, to := range s.Relations { for trg, _ := range to { + _, srcExcluded := excludedComponentIds[src] + if srcExcluded { + continue + } + _, trgExcluded := excludedComponentIds[trg] + if trgExcluded { + continue + } sb.WriteString(buildComponentConnection(src, trg, v.lineColor)) } } @@ -38,3 +57,19 @@ func (v View) RenderTo(s model.Structure, w io.Writer) error { _, err := w.Write([]byte(out)) return err } + +func (v View) hasTag(tags ...string) bool { + if len(v.tags) == 0 { + return true + } + + for _, vt := range v.tags { + for _, t := range tags { + if t == vt { + return true + } + } + } + + return false +} diff --git a/pkg/view/view.go b/pkg/view/view.go index 2f002a2..98688b7 100644 --- a/pkg/view/view.go +++ b/pkg/view/view.go @@ -9,17 +9,20 @@ import ( type View struct { title string + tags []string componentStyles map[string]ComponentStyle lineColor color.Color } func newView( title string, + tags []string, componentStyles map[string]ComponentStyle, lineColor color.Color, ) View { return View{ title: title, + tags: tags, componentStyles: componentStyles, lineColor: lineColor, } @@ -33,6 +36,7 @@ func NewView() *Builder { return &Builder{ View: View{ title: "", + tags: make([]string, 0), componentStyles: make(map[string]ComponentStyle), lineColor: color.Black, }, @@ -60,6 +64,11 @@ func (b *Builder) WithTitle(t string) *Builder { return b } +func (b *Builder) WithTag(t string) *Builder { + b.tags = append(b.tags, t) + return b +} + func (b *Builder) WithComponentStyle(s ComponentStyle) *Builder { b.componentStyles[s.id] = s return b @@ -75,6 +84,7 @@ func (b *Builder) WithLineColor(c color.Color) *Builder { func (b Builder) Build() View { return newView( b.title, + b.tags, b.componentStyles, b.lineColor, ) diff --git a/pkg/view/view_test.go b/pkg/view/view_test.go new file mode 100644 index 0000000..8c56642 --- /dev/null +++ b/pkg/view/view_test.go @@ -0,0 +1,328 @@ +package view_test + +import ( + "bytes" + "github.com/krzysztofreczek/go-structurizr/pkg/model" + "github.com/krzysztofreczek/go-structurizr/pkg/view" + "github.com/stretchr/testify/require" + "image/color" + "testing" +) + +func TestNewView_empty(t *testing.T) { + s := model.NewStructure() + + out := bytes.Buffer{} + + v := view.NewView().Build() + err := v.RenderTo(s, &out) + require.NoError(t, err) + + outString := string(out.Bytes()) + + expectedContent := ` +@startuml +title + +skinparam { + shadowing false + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} +hide stereotype +top to bottom direction + +@enduml` + + require.Equal(t, expectedContent, outString) +} + +func TestNewView_with_title(t *testing.T) { + s := model.NewStructure() + + out := bytes.Buffer{} + + v := view.NewView(). + WithTitle("TITLE"). + Build() + err := v.RenderTo(s, &out) + require.NoError(t, err) + + outString := string(out.Bytes()) + + expectedContent := `title TITLE` + + require.Contains(t, outString, expectedContent) +} + +func TestNewView_with_custom_style(t *testing.T) { + s := model.NewStructure() + + out := bytes.Buffer{} + + style := view.NewComponentStyle("STYLE"). + WithBackgroundColor(color.White). + WithFontColor(color.Black). + WithBorderColor(color.White). + Build() + v := view.NewView(). + WithComponentStyle(style). + Build() + err := v.RenderTo(s, &out) + require.NoError(t, err) + + outString := string(out.Bytes()) + + expectedContent := ` +skinparam rectangle<