/
endpointanalysisdiagram.go
115 lines (106 loc) · 4.25 KB
/
endpointanalysisdiagram.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package endpointanalysisdiagram
import (
"fmt"
"github.com/anz-bank/sysl/pkg/mermaid"
"github.com/anz-bank/sysl/pkg/sysl"
)
//externalLink keeps track of the statement-endpoint pairs we visit during execution
type externalLink struct {
statement, endPoint string
}
//GenerateEndpointAnalysisDiagram accepts the sysl module and returns a string (and an error if any)
//The resulting string is the mermaid code for the endpoint analysis for that application and endpoint
func GenerateEndpointAnalysisDiagram(m *sysl.Module) (string, error) {
return generateEndpointAnalysisDiagramHelper(m, &[]externalLink{}, true)
}
//Similar to the above, but accepts a slice of application names
//and returns a diagram only including applications specified
func GenerateMultipleAppEndpointAnalysisDiagram(m *sysl.Module, appNames []string) (string, error) {
return generateMultipleAppEndpointAnalysisDiagramHelper(m, appNames, &[]externalLink{}, true)
}
//generateEndpointAnalysisDiagram is a helper which has additional arguments which need not be entered by the user
func generateEndpointAnalysisDiagramHelper(m *sysl.Module,
externalLinks *[]externalLink, theStart bool) (string, error) {
var result string
if theStart {
result = mermaid.GeneratedHeader + "graph TD\n"
}
count := 1
for appName, app := range m.Apps {
result += fmt.Sprintf(" subgraph %d[\"%s\"]\n", count, appName)
for epName, endPoint := range app.Endpoints {
statements := endPoint.Stmt
result += printEndpointAnalysisStatements(m, statements, mermaid.CleanString(epName), externalLinks)
}
result += " end\n"
count++
}
for _, eLink := range *externalLinks {
result += fmt.Sprintf(" %s --> %s\n", eLink.statement, eLink.endPoint)
}
return result, nil
}
func generateMultipleAppEndpointAnalysisDiagramHelper(m *sysl.Module, appNames []string,
externalLinks *[]externalLink, theStart bool) (string, error) {
var result string
if theStart {
result = mermaid.GeneratedHeader + "graph TD\n"
}
count := 1
for _, appName := range appNames {
result += fmt.Sprintf(" subgraph %d[\"%s\"]\n", count, appName)
endPoints := m.Apps[appName].Endpoints
for epName, endPoint := range endPoints {
statements := endPoint.Stmt
result += printEndpointAnalysisStatements(m, statements, mermaid.CleanString(epName), externalLinks)
}
result += " end\n"
count++
}
for _, eLink := range *externalLinks {
result += fmt.Sprintf(" %s --> %s\n", eLink.statement, eLink.endPoint)
}
return result, nil
}
//printEndpointAnalysisStatements is used to print the mermaid code for different sysl statements
func printEndpointAnalysisStatements(m *sysl.Module, statements []*sysl.Statement,
endPoint string, externalLinks *[]externalLink) string {
var result string
for _, statement := range statements {
switch c := statement.Stmt.(type) {
case *sysl.Statement_Call:
appEndPoint := fmt.Sprintf("%s-%s", mermaid.CleanString(c.Call.Target.Part[0]), mermaid.CleanString(c.Call.Endpoint))
result += fmt.Sprintf(" %s --> %s\n", endPoint, appEndPoint)
pair := externalLink{appEndPoint, mermaid.CleanString(c.Call.Endpoint)}
if !externalLinksContain(*externalLinks, pair) {
*externalLinks = append(*externalLinks, pair)
}
case *sysl.Statement_Group:
result += printEndpointAnalysisStatements(m, c.Group.Stmt, endPoint, externalLinks)
case *sysl.Statement_Cond:
result += printEndpointAnalysisStatements(m, c.Cond.Stmt, endPoint, externalLinks)
case *sysl.Statement_Loop:
result += printEndpointAnalysisStatements(m, c.Loop.Stmt, endPoint, externalLinks)
case *sysl.Statement_LoopN:
result += printEndpointAnalysisStatements(m, c.LoopN.Stmt, endPoint, externalLinks)
case *sysl.Statement_Foreach:
result += printEndpointAnalysisStatements(m, c.Foreach.Stmt, endPoint, externalLinks)
case *sysl.Statement_Action:
result += fmt.Sprintf(" %s --> %s\n", endPoint, mermaid.CleanString(c.Action.Action))
case *sysl.Statement_Ret:
result += fmt.Sprintf(" %s --> %s\n", endPoint, mermaid.CleanString(c.Ret.Payload))
default:
result += ""
}
}
return result
}
//externalLinksContain checks if the statement-endpoint group have been already visited or not
func externalLinksContain(i []externalLink, ip externalLink) bool {
for _, a := range i {
if a == ip {
return true
}
}
return false
}