-
Notifications
You must be signed in to change notification settings - Fork 26
/
vistecture.go
267 lines (244 loc) · 7.64 KB
/
vistecture.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package main
import (
"fmt"
"log"
"net/http"
"os"
"path"
"time"
"github.com/AOEpeople/vistecture/v2/application"
"github.com/AOEpeople/vistecture/v2/controller"
"github.com/AOEpeople/vistecture/v2/controller/web"
"github.com/AOEpeople/vistecture/v2/model/core"
"github.com/gorilla/mux"
"github.com/urfave/cli"
)
type (
projectInjectAble interface {
Inject(*core.Project)
}
)
var (
//global cli flags
projectConfigFile, projectSubViewName string
skipValidation bool
//server cli flags
serverPort int
localTemplateFolder string
staticDocumentsFolder string
)
func actionFunc(lazyProjectInjectAble projectInjectAble, cb func()) func(c *cli.Context) error {
return func(c *cli.Context) error {
project := loadProject(projectConfigFile, projectSubViewName, skipValidation)
lazyProjectInjectAble.Inject(project)
cb()
return nil
}
}
func main() {
var componentName, templatePath, iconPath, summaryRelation, hidePlanned string
app := cli.NewApp()
app.Name = "vistecture tool "
app.Version = "2.0.11"
app.Usage = "describing and analysing distributed or microservice-style architectures with its depenendcies."
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "config",
Value: "",
Usage: "Path to the project config file",
Destination: &projectConfigFile,
},
cli.StringFlag{
Name: "subview",
Value: "",
Usage: "Name of the projects subview - if you want to limit the action to subview",
Destination: &projectSubViewName,
},
cli.BoolFlag{
Name: "skipValidation",
Usage: "Skip the validation of the project",
Destination: &skipValidation,
},
}
analyzeController := &controller.AnalyzeController{}
documentationController := &controller.DocumentationController{}
app.Commands = []cli.Command{
{
Name: "validate",
Usage: "Validates project JSON",
Action: validate,
},
{
Name: "list",
Usage: "lists the apps",
Action: listApps,
},
{
Name: "analyze",
Usage: "Analyses project structure. Detects cyclic dependencies etc",
Action: actionFunc(analyzeController, analyzeController.AnalyzeAction),
},
{
Name: "documentation",
Usage: "Creates (living) documentation",
Action: actionFunc(documentationController, func() { documentationController.HTMLDocumentAction(templatePath, iconPath) }),
Flags: []cli.Flag{
cli.StringFlag{
Name: "templatePath",
Value: "templates/htmldocument.tmpl",
Usage: "Path of template that will be used",
Destination: &templatePath,
},
cli.StringFlag{
Name: "iconPath",
Value: "templates/icons",
Usage: "Path of icons that will be in drawing components",
Destination: &iconPath,
},
},
},
{
Name: "graph",
Usage: "Build graphviz format which can be used by dot or any other graphviz command. \n go run main.go graph | dot -Tpng -o graph.png \n See: http://www.graphviz.org/pdf/twopi.1.pdf",
Action: actionFunc(documentationController, func() { documentationController.GraphvizAction(componentName, iconPath, hidePlanned, skipValidation) }),
Flags: []cli.Flag{
cli.StringFlag{
Name: "application",
Value: "",
Usage: "Name of a application - then only a graph for this application will be drawn",
Destination: &componentName,
},
cli.StringFlag{
Name: "iconPath",
Value: "templates/icons",
Usage: "Path of icons that will be in drawing components",
Destination: &iconPath,
},
cli.StringFlag{
Name: "hidePlanned",
Value: "",
Usage: "Flag if planned applications should be drawn or not",
Destination: &hidePlanned,
},
},
},
{
Name: "groupGraph",
Usage: "Build graphviz format that shows only the group of services and its dependencies.",
Action: actionFunc(documentationController, func() { documentationController.GroupGraphvizAction(summaryRelation) }),
Flags: []cli.Flag{
cli.StringFlag{
Name: "summaryRelation",
Value: "",
Usage: "if set then only one arrow is drawn between the teams",
Destination: &summaryRelation,
},
},
},
{
Name: "teamGraph",
Usage: "Build a overview of involved teams and the relations based from the architecture (Conways law)",
Action: actionFunc(documentationController, func() { documentationController.TeamGraphvizAction(summaryRelation) }),
Flags: []cli.Flag{
cli.StringFlag{
Name: "summaryRelation",
Value: "",
Usage: "if set then only one arrow is drawn between the teams",
Destination: &summaryRelation,
},
},
},
{
Name: "serve",
Usage: "Runs the vistecture webserver",
Action: startServer,
Flags: []cli.Flag{
cli.IntFlag{
Name: "port",
Value: 8080,
Usage: "set the port (default 8080)",
Destination: &serverPort,
},
cli.StringFlag{
Name: "localTemplateFolder",
Value: "",
Usage: "if set then this template folder will be used to serve the view - otherwise a standard template gets loaded automatically",
Destination: &localTemplateFolder,
},
cli.StringFlag{
Name: "staticDocumentsFolder",
Value: "",
Usage: "if set then this folder will be scanned for files that are linked in the mainmenu then",
Destination: &staticDocumentsFolder,
},
},
},
}
_ = app.Run(os.Args)
}
func loadProject(configFile string, subViewName string, skipValidation bool) *core.Project {
loader := application.ProjectLoader{StrictMode: !skipValidation}
project, err := loader.LoadProjectFromConfigFile(configFile, subViewName)
if err != nil {
log.Println(err)
if !skipValidation {
log.Fatal("project loading aborted.")
}
}
return project
}
func validate(_ *cli.Context) error {
loader := application.ProjectLoader{StrictMode: !skipValidation}
project, err := loader.LoadProjectFromConfigFile(projectConfigFile, projectSubViewName)
if err != nil {
log.Println(err)
}
var validationErrors []error
if project != nil {
validationErrors = project.Validate()
for _, valErr := range validationErrors {
log.Println(valErr)
}
}
if err != nil || len(validationErrors) > 0 {
log.Fatal("Not valid")
} else {
log.Println("valid")
}
return nil
}
func listApps(_ *cli.Context) error {
project := loadProject(projectConfigFile, projectSubViewName, true)
for _, app := range project.Applications {
log.Printf("Name: %v Id: %v", app.Name, app.Id)
}
return nil
}
func startServer(_ *cli.Context) error {
r := mux.NewRouter()
srv := &http.Server{
Handler: r,
Addr: fmt.Sprintf(":%v", serverPort),
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
webProjectController := web.ProjectController{}
loader := application.ProjectLoader{}
definitions, err := loader.LoadProjectConfig(projectConfigFile)
if err != nil {
log.Fatal(err)
return nil
}
webProjectController.Inject(definitions, &loader, path.Dir(projectConfigFile), skipValidation)
// This will serve files under http://localhost:8000/documents/<filename>
r.PathPrefix("/documents/").Handler(http.StripPrefix("/documents/", http.FileServer(http.Dir(staticDocumentsFolder))))
r.HandleFunc("/data", func(w http.ResponseWriter, r *http.Request) {
webProjectController.DataAction(w, r, staticDocumentsFolder)
})
r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
webProjectController.IndexAction(w, r, localTemplateFolder)
})
log.Printf("Starting server:%v \n", serverPort)
log.Fatal(srv.ListenAndServe())
return nil
}