/
integrationdiagram.go
98 lines (89 loc) · 3.47 KB
/
integrationdiagram.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
package integrationdiagram
import (
"errors"
"fmt"
"github.com/anz-bank/sysl/pkg/sysl"
)
const projectDir = "../../../"
//integrationPair keeps track of the application pairs we visit during the recursion
type integrationPair struct {
firstApp, secondApp string
}
//GenerateIntegrationDiagram accepts an application name as input and returns a string (and an error if any)
//The resulting string is the mermaid code for the integration diagram for that application
func GenerateIntegrationDiagram(m *sysl.Module, appName string) (string, error) {
return generateIntegrationDiagramHelper(m, appName, &[]integrationPair{}, true)
}
//generateIntegrationDiagramHelper is a helper which has additional arguments which need not be entered by the user
func generateIntegrationDiagramHelper(m *sysl.Module, appName string,
integrationPairs *[]integrationPair, theStart bool) (string, error) {
var result string
if theStart {
result = "%% AUTOGENERATED CODE -- DO NOT EDIT!\n\ngraph TD\n"
if err := IsValidAppName(m, appName); err != nil {
return "", err
}
}
endPoints := m.Apps[appName].Endpoints
//For every endpoint, the statements are retrieved and we pass it to the printer to print appropriate mermaid code
for _, endPoint := range endPoints {
statements := endPoint.Stmt
result += printIntegrationDiagramStatements(m, statements, appName, integrationPairs)
}
return result, nil
}
//printIntegrationDiagramStatements is where the printing takes place
//Uses a switch statement to decide what to print and what recursion needs to be done
func printIntegrationDiagramStatements(m *sysl.Module, statements []*sysl.Statement,
appName string, integrationPairs *[]integrationPair) string {
var result string
for _, statement := range statements {
switch c := statement.Stmt.(type) {
case *sysl.Statement_Call:
nextApp := c.Call.Target.Part[0]
pair := integrationPair{appName, nextApp}
if !integrationPairsContain(*integrationPairs, pair) {
*integrationPairs = append(*integrationPairs, pair)
result += fmt.Sprintf(" %s --> %s\n", appName, nextApp)
out, err := generateIntegrationDiagramHelper(m, nextApp, integrationPairs, false)
if err != nil {
panic("Error in generating integration diagram; check if app name is correct")
}
result += out
}
case *sysl.Statement_Group:
result += printIntegrationDiagramStatements(m, c.Group.Stmt, appName, integrationPairs)
case *sysl.Statement_Cond:
result += printIntegrationDiagramStatements(m, c.Cond.Stmt, appName, integrationPairs)
case *sysl.Statement_Loop:
result += printIntegrationDiagramStatements(m, c.Loop.Stmt, appName, integrationPairs)
case *sysl.Statement_LoopN:
result += printIntegrationDiagramStatements(m, c.LoopN.Stmt, appName, integrationPairs)
case *sysl.Statement_Foreach:
result += printIntegrationDiagramStatements(m, c.Foreach.Stmt, appName, integrationPairs)
case *sysl.Statement_Action:
result += ""
case *sysl.Statement_Ret:
result += ""
default:
result += ""
}
}
return result
}
//IsValidAppName checks if the entered application name exists in the sysl module or not
func IsValidAppName(m *sysl.Module, appName string) error {
if _, ok := m.Apps[appName]; !ok {
return errors.New("invalid app name")
}
return nil
}
//integrationPairsContain checks if the application couple have been already visited or not
func integrationPairsContain(i []integrationPair, ip integrationPair) bool {
for _, a := range i {
if a == ip {
return true
}
}
return false
}