Browse Source

centering resources is working!!

master
forest 2 years ago
parent
commit
58dc67615b
  1. 295
      test.go

295
test.go

@ -10,11 +10,39 @@ import (
"io/ioutil"
"os/exec"
"regexp"
"strconv"
"strings"
errors "git.sequentialread.com/forest/pkg-errors"
)
type SimplifiedTerraformStatus struct {
Modules map[string]*SimplifiedTerraformModule
Variables map[string]string
Connections []SimplifiedTerraformConnection
}
type SimplifiedTerraformModule struct {
DisplayName string
IsAnsible bool
Resources []*SimplifiedTerraformResource
}
type SimplifiedTerraformResource struct {
ResourceType string
DisplayName string
Plan string
State string
Progress int
ProgressTotal int
}
type SimplifiedTerraformConnection struct {
From string
To string
DisplayName string
}
type XMLAttr struct {
Name string
Value string
@ -35,6 +63,15 @@ type XMLQuery struct {
NodeType string
Class string
Attr string
AttrValue string
}
type Rect2D struct {
NonEmpty bool
Top float64
Left float64
Bottom float64
Right float64
}
func (node *XMLNode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
@ -115,6 +152,18 @@ func (node XMLNode) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}
func main() {
statusBytes, err := ioutil.ReadFile("test-status.json")
if err != nil {
panic(err)
}
var status SimplifiedTerraformStatus
err = json.Unmarshal(statusBytes, &status)
if err != nil {
panic(err)
}
bytes, err := ioutil.ReadFile("test-dot.txt")
if err != nil {
panic(err)
@ -164,6 +213,7 @@ func main() {
// remove the white background from the SVG
svgDoc.WithQuerySelector(
// 'g.graph > polygon'
[]XMLQuery{XMLQuery{NodeType: "g", Class: "graph"}, XMLQuery{NodeType: "polygon", DirectChild: true}},
func(node *XMLNode) {
node.SetAttr("fill", "none")
@ -173,6 +223,7 @@ func main() {
// TODO is this needed ? I don't think so...
// remove the white background from the edges
// svgDoc.WithQuerySelector(
// // 'g.edge path'
// []XMLQuery{XMLQuery{NodeType: "g", Class: "edge"}, XMLQuery{NodeType: "path"}},
// func(node *XMLNode) {
// node.SetAttr("fill", "none")
@ -181,14 +232,12 @@ func main() {
// correctly set the dot property for resources, variables, and parameters
svgDoc.WithQuerySelector(
// 'g.node > title'
[]XMLQuery{XMLQuery{NodeType: "g", Class: "node"}, XMLQuery{NodeType: "title", DirectChild: true}},
func(node *XMLNode) {
fmt.Println("----")
if node.Parent != nil {
title := html.UnescapeString(string(node.Content))
fmt.Println(title)
node.Parent.SetAttr("dot", title)
node.Parent.SetAttr("data-dot", title)
if strings.HasPrefix(title, "var_") {
node.Parent.SetAttr("class", "variable")
}
@ -205,14 +254,15 @@ func main() {
// correctly set the dot property for the "cluster"s (aka modules)
moduleNames := []string{}
svgDoc.WithQuerySelector(
// 'a[xlink:title]'
[]XMLQuery{XMLQuery{NodeType: "a", Attr: "xlink:title"}},
func(node *XMLNode) {
if node.Parent != nil && node.Parent.Parent != nil {
title := html.UnescapeString(node.GetAttr("xlink:title"))
title = regexp.MustCompile(`[.-]`).ReplaceAllString(title, "_")
if node.Parent.Parent.GetAttr("dot") == "" {
if node.Parent.Parent.GetAttr("data-dot") == "" {
moduleNames = append(moduleNames, title)
node.Parent.Parent.SetAttr("dot", title)
node.Parent.Parent.SetAttr("data-dot", title)
node.Parent.Parent.SetAttr("class", "module")
}
@ -225,6 +275,7 @@ func main() {
// not between modules. So we have to trim the resource name off of the dot attribute
// on the connections to make them match what we have in the status JSON object.
svgDoc.WithQuerySelector(
// 'g.edge title'
[]XMLQuery{XMLQuery{NodeType: "g", Class: "edge"}, XMLQuery{NodeType: "title"}},
func(node *XMLNode) {
if node.Parent != nil {
@ -236,10 +287,10 @@ func main() {
// note that we also replaced "->" with "_to_" to avoid html escape confusion
for _, moduleName := range moduleNames {
if strings.HasPrefix(fromTo[0], moduleName) {
node.Parent.SetAttr("dot", fmt.Sprintf("%s_to_%s", moduleName, fromTo[1]))
node.Parent.SetAttr("data-dot", fmt.Sprintf("%s_to_%s", moduleName, fromTo[1]))
}
if strings.HasPrefix(fromTo[1], moduleName) {
node.Parent.SetAttr("dot", fmt.Sprintf("%s_to_%s", fromTo[0], moduleName))
node.Parent.SetAttr("data-dot", fmt.Sprintf("%s_to_%s", fromTo[0], moduleName))
}
}
}
@ -247,14 +298,94 @@ func main() {
)
svgDoc.WithQuerySelector(
// '[font-family]'
[]XMLQuery{XMLQuery{Attr: "font-family"}},
func(node *XMLNode) {
node.SetAttr("font-family", "-apple-system,system-ui,BlinkMacSystemFont,Ubuntu,Roboto,Segoe UI,sans-serif")
},
)
modules := svgDoc.QuerySelectorAll([]XMLQuery{XMLQuery{Class: "module"}})
resources := svgDoc.QuerySelectorAll([]XMLQuery{XMLQuery{Class: "resource"}})
// two last things to do:
// 1. center each resource horizontally within the bounding box of its parent module
// because dot is dumb and it just kinda throws them wherever
// 2. create little create/delete/modify icons next to each changed resource
// similar to how terraform displays -, +, +/-, or ~ on each line-item in the plan
for moduleName, module := range status.Modules {
moduleId := regexp.MustCompile(`[.-]`).ReplaceAllString(moduleName, "_")
// drawRect := func(rect *Rect2D) {
// svgDoc.WithQuerySelector(
// // 'g.graph > polygon'
// []XMLQuery{XMLQuery{NodeType: "g", Class: "graph"}, XMLQuery{NodeType: "polygon", DirectChild: true}},
// func(node *XMLNode) {
// coordStrings := []string{}
// coordStrings = append(coordStrings, fmt.Sprintf("%.4f,%.4f", rect.Left-float64(1), rect.Top-float64(1)))
// coordStrings = append(coordStrings, fmt.Sprintf("%.4f,%.4f", rect.Right+float64(1), rect.Top-float64(1)))
// coordStrings = append(coordStrings, fmt.Sprintf("%.4f,%.4f", rect.Right+float64(1), rect.Bottom+float64(1)))
// coordStrings = append(coordStrings, fmt.Sprintf("%.4f,%.4f", rect.Left-float64(1), rect.Bottom+float64(1)))
// toAppend := XMLNode{
// XMLName: xml.Name{Local: "g", Space: "http://www.w3.org/2000/svg"},
// Children: []*XMLNode{
// &XMLNode{
// XMLName: xml.Name{Local: "polygon", Space: "http://www.w3.org/2000/svg"},
// Attrs: []*XMLAttr{
// &XMLAttr{
// Local: "stroke",
// Value: "#ff00ff",
// },
// &XMLAttr{
// Local: "fill",
// Value: "none",
// },
// &XMLAttr{
// Local: "points",
// Value: strings.Join(coordStrings, " "),
// },
// },
// },
// },
// }
// fmt.Println("append!!")
// node.Parent.Children = append(node.Parent.Children, &toAppend)
// },
// )
// }
//fmt.Printf("try %s..\n", moduleId)
svgDoc.WithQuerySelector(
// '[data-dot=module_dns_gandi]' for example
[]XMLQuery{XMLQuery{Attr: "data-dot", AttrValue: moduleId}},
func(node *XMLNode) {
moduleRect := node.GetBoundingBox()
//drawRect(moduleRect)
//fmt.Printf("%s: \n%v\n", moduleId, moduleRect)
for _, resource := range module.Resources {
resourceId := fmt.Sprintf("%s_%s", moduleId, regexp.MustCompile(`[.-]`).ReplaceAllString(resource.DisplayName, "_"))
//fmt.Printf("try %s..\n", resourceId)
svgDoc.WithQuerySelector(
// '[data-dot=module_dns_gandi_gandi_livedns_record_dns_entries]' for example
[]XMLQuery{XMLQuery{Attr: "data-dot", AttrValue: resourceId}},
func(node *XMLNode) {
resourceRect := node.GetBoundingBox()
//drawRect(resourceRect)
nudgeX := ((moduleRect.Left + moduleRect.Right) - (resourceRect.Left + resourceRect.Right)) * float64(0.5)
//fmt.Printf("%s: nudgeX: %.4f\n\n", resourceId, nudgeX)
node.SetAttr("transform", fmt.Sprintf("scale(1 1) rotate(0) translate(%d %d)", int(nudgeX), 0))
//node.Translate(nudgeX, 0)
},
)
}
},
)
}
// this encoder doesn't want to include my custom attributes...
// so we will hijack the id attribute! Record the value of the custom attribute so we can
@ -265,12 +396,13 @@ func main() {
// We cant serialize cyclical relationships like parent -> child -> parent
node.Parent = nil
// the XML serializer does not like my custom attributes like `data-dot` or `dot`.
// so we will just hijack the id attribute instead, poggers
dot := node.GetAttr("dot")
if dot != "" {
node.SetAttr("id", dot)
}
// TODO JK i f ixed the attrs
// // the XML serializer does not like my custom attributes like `data-dot` or `dot`.
// // so we will just hijack the id attribute instead, poggers
// dot := node.GetAttr("data-dot")
// if dot != "" {
// node.SetAttr("id", dot)
// }
// if we don't zero out the content for non-leaf nodes, we will get duplicates of everything
if len(node.Children) != 0 {
@ -400,7 +532,9 @@ func (doc *XMLNode) QuerySelectorAll(querySteps []XMLQuery) []*XMLNode {
found := false
for _, attr := range node.Attrs {
if attr.Name == q.Attr {
found = true
if q.AttrValue == "" || attr.Value == q.AttrValue {
found = true
}
}
}
if !found {
@ -452,6 +586,7 @@ func (node *XMLNode) SetAttr(attr string, value string) {
newAttr := XMLAttr{
Name: attr,
Value: value,
Local: attr,
}
node.Attrs = append(node.Attrs, &newAttr)
}
@ -465,3 +600,129 @@ func (node *XMLNode) GetAttr(attr string) string {
}
return ""
}
// Get Rect Kid
func (rect *Rect2D) Encapsulate(x, y float64) {
if !rect.NonEmpty {
rect.Bottom = y
rect.Top = y
rect.Left = x
rect.Right = x
rect.NonEmpty = true
} else {
if x < rect.Left {
rect.Left = x
}
if x > rect.Right {
rect.Right = x
}
if y < rect.Top {
rect.Top = y
}
if y > rect.Bottom {
rect.Bottom = y
}
}
}
func (node *XMLNode) GetBoundingBox() *Rect2D {
toReturn := Rect2D{}
polygons := node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "polygon"}})
ellipses := node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "ellipse"}})
for _, polygon := range polygons {
points, err := polygon.GetSVGPolygonPoints()
if err == nil {
for _, point := range points {
toReturn.Encapsulate(point[0], point[1])
}
}
}
for _, ellipse := range ellipses {
cx, errCx := strconv.ParseFloat(ellipse.GetAttr("cx"), 64)
cy, errCy := strconv.ParseFloat(ellipse.GetAttr("cy"), 64)
rx, errRx := strconv.ParseFloat(ellipse.GetAttr("rx"), 64)
ry, errRy := strconv.ParseFloat(ellipse.GetAttr("ry"), 64)
if errCx == nil && errCy == nil && errRx == nil && errRy == nil {
toReturn.Encapsulate(cx-rx, cy)
toReturn.Encapsulate(cx+rx, cy)
toReturn.Encapsulate(cx, cy-ry)
toReturn.Encapsulate(cx, cy+ry)
}
}
return &toReturn
}
func (node *XMLNode) GetSVGPolygonPoints() ([][]float64, error) {
toReturn := [][]float64{}
var err error
// endsWithSpace := false
// pointsString := node.GetAttr("points")
// if strings.HasSuffix(pointsString, " ") {
// endsWithSpace = true
// pointsString = strings.TrimSpace(pointsString)
// }
points := strings.Split(node.GetAttr("points"), " ")
for _, point := range points {
coords := strings.Split(point, ",")
if len(coords) == 2 {
x, errX := strconv.ParseFloat(coords[0], 64)
y, errY := strconv.ParseFloat(coords[1], 64)
if errX == nil && errY == nil {
toReturn = append(toReturn, []float64{x, y})
} else {
err = errors.New("parsing svg polygon points failed cuz strconv.ParseFloat failed")
}
} else {
err = errors.New("parsing svg polygon points failed cuz len(coords) != 2")
}
}
return toReturn, err
}
func (node *XMLNode) SetSVGPolygonPoints(points [][]float64) {
coordStrings := []string{}
for _, point := range points {
coordStrings = append(coordStrings, fmt.Sprintf("%.4f,%.4f", point[0], point[1]))
}
node.SetAttr("points", strings.Join(coordStrings, " "))
}
// func (node *XMLNode) Translate(x, y float64) {
// polygons := node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "polygon"}})
// polygons = append(polygons, node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "polyline"}})...)
// ellipses := node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "ellipse"}})
// texts := node.QuerySelectorAll([]XMLQuery{XMLQuery{NodeType: "text"}})
// for _, polygon := range polygons {
// points, err := polygon.GetSVGPolygonPoints()
// if err == nil {
// for _, point := range points {
// point[0] = point[0] + x
// point[1] = point[1] + y
// }
// polygon.SetSVGPolygonPoints(points)
// }
// }
// for _, ellipse := range ellipses {
// cx, errCx := strconv.ParseFloat(ellipse.GetAttr("cx"), 64)
// cy, errCy := strconv.ParseFloat(ellipse.GetAttr("cy"), 64)
// if errCx == nil && errCy == nil {
// ellipse.SetAttr("cx", fmt.Sprintf("%.4f", cx+x))
// ellipse.SetAttr("cy", fmt.Sprintf("%.4f", cy+y))
// }
// }
// for _, text := range texts {
// text_x, errX := strconv.ParseFloat(text.GetAttr("x"), 64)
// text_y, errY := strconv.ParseFloat(text.GetAttr("y"), 64)
// if errX == nil && errY == nil {
// text.SetAttr("x", fmt.Sprintf("%.4f", text_x+x))
// text.SetAttr("y", fmt.Sprintf("%.4f", text_y+y))
// }
// }
// }

Loading…
Cancel
Save