Генерирование полной схемы БД
This commit is contained in:
parent
a70a7748fe
commit
c5572183fe
|
@ -51,6 +51,19 @@
|
|||
"--outdir-frontend",
|
||||
"../zmap/frontend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "zmap docs",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/main.go",
|
||||
"args": [
|
||||
"--metafile",
|
||||
"/home/ymnuk/projects/zmap/zmap.yml",
|
||||
"--outdir-doc",
|
||||
"../zmap/docs"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
6
go.mod
6
go.mod
|
@ -9,4 +9,8 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
require (
|
||||
github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/yuzutech/kroki-go v0.8.1 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -8,12 +8,16 @@ github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdB
|
|||
github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuzutech/kroki-go v0.8.1 h1:p6doXpgWUXgLrVWt6MA1/ODH/oMBsnNGC3f7wGUewbM=
|
||||
github.com/yuzutech/kroki-go v0.8.1/go.mod h1:JyGBdT3Od/PHEDf95GirXwhf1PM31k+tevrXGVjqNpg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -19,9 +19,7 @@ var AppConfig struct {
|
|||
OutdirDoc string `arg:"-d,--outdir-doc" help:"Директория для сохранения сгенерированной документации по проекта"`
|
||||
IsMarkdown bool `arg:"--format-markdown" help:"Выходная документация в формате Markdown"`
|
||||
IsHtml bool `arg:"--format-html" help:"Выходная документация в формате HTML"`
|
||||
IsGraphviz bool `arg:"--graphviz" help:"Генерировать графики в формате GraphViz (должен быть установлен локально)."`
|
||||
IsMermaid bool `arg:"--mermaid" help:"Генерировать графики в формате Mermaid"`
|
||||
MermaidAddr string `arg:"--mermaid-addr" default:"http://localhost" help:"Адрес сервера Mermaid. Если не указан и указан выходной формат Markdown, то просто встраивается в файл без предварительной генерации."`
|
||||
KrokiAddr string `arg:"--kroki-addr" default:"https://kroki.io" help:"Адрес сервера Kroki."`
|
||||
IsSingleDoc bool `arg:"--single-doc" help:"Генерировать документацию в одном файле без разделения"`
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"git.ymnuktech.ru/ymnuk/yt-gen-app/lib/templ"
|
||||
"git.ymnuktech.ru/ymnuk/yt-gen-app/structs"
|
||||
"github.com/yuzutech/kroki-go"
|
||||
)
|
||||
|
||||
func Documentation() {
|
||||
if !AppConfig.IsHtml {
|
||||
AppConfig.IsMarkdown = true
|
||||
}
|
||||
// Генерация общей схемы БД
|
||||
fullDocDBGen()
|
||||
// Генерация каждой таблицы по отдельности с прилегающими ближайшими таблицами
|
||||
/*for i := range Project.DB.Tables {
|
||||
docDBGen(Project.DB.Tables[i])
|
||||
}*/
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Генерирования списка таблиц
|
||||
/*func listTablesDocGen() string {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Генерирование полей таблицы
|
||||
func tableDocGen(table structs.Table) string {
|
||||
// TODO
|
||||
}*/
|
||||
|
||||
// Генерация общей схемы БД
|
||||
func fullDocDBGen() {
|
||||
// TODO
|
||||
if tmpl, err := templ.ReadTmplFile("tmpl/docs/db/full-schema.tmpl"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if buff, err := templ.ExecuteTmplFile(tmpl, Project.DB); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
//fmt.Println(string(buff))
|
||||
templ.WriteFile(AppConfig.OutdirDoc+"/test.txt", buff)
|
||||
client := NewKrokiClient()
|
||||
if result, err := client.FromString(string(buff), "dbml", kroki.SVG); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
//fmt.Println(result)
|
||||
templ.WriteFile(AppConfig.OutdirDoc+"/test.svg", []byte(result))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Генерация отдельной таблицы с прилегающими ближайшими таблицами
|
||||
func docDBGen(table structs.Table) {
|
||||
// TODO
|
||||
}
|
|
@ -21,4 +21,12 @@ func Generate() {
|
|||
}
|
||||
Frontend()
|
||||
}
|
||||
|
||||
if AppConfig.OutdirDoc != "" {
|
||||
// Генерация документации
|
||||
if err := os.MkdirAll(AppConfig.OutdirDoc, 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
Documentation()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/yuzutech/kroki-go"
|
||||
)
|
||||
|
||||
var krokiClient *kroki.Client
|
||||
|
||||
func NewKrokiClient() *kroki.Client {
|
||||
if krokiClient == nil {
|
||||
tmp := kroki.New(kroki.Configuration{
|
||||
URL: AppConfig.KrokiAddr,
|
||||
Timeout: time.Second * 60,
|
||||
})
|
||||
krokiClient = &tmp
|
||||
}
|
||||
return krokiClient
|
||||
}
|
|
@ -21,14 +21,20 @@ import (
|
|||
var Content embed.FS
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
|
||||
//"includeTemplPart": IncludeTemplPart,
|
||||
|
||||
"fieldName": FieldName,
|
||||
"fieldChildName": FieldChildName,
|
||||
"fieldNamePrepare": FieldNamePrepare,
|
||||
"fieldNameLowerPrepare": FieldNameLowerPrepare,
|
||||
"fieldJsonNameStr": FieldJsonNameStr,
|
||||
"fieldType": FieldType,
|
||||
"fieldTypeStr": FieldTypeStr,
|
||||
"fieldStringToType": FieldStringToType,
|
||||
//"fieldDbTableType": FieldDbTableType,
|
||||
"fieldTypeDB": FieldTypeDB,
|
||||
"fieldTypeDBStr": FieldTypeDBStr,
|
||||
"fieldTypeParentTable": FieldTypeParentTable,
|
||||
"hasFieldType": HasFieldType,
|
||||
"fieldDescript": FieldDescript,
|
||||
|
@ -71,6 +77,22 @@ var funcMap = template.FuncMap{
|
|||
"fieldInTableByName": FieldInTableByName,
|
||||
}
|
||||
|
||||
func IncludeTemplPart(templName string, data interface{}) string {
|
||||
var err error
|
||||
var tmpl *template.Template
|
||||
if tmpl, err = ReadTmplFile(templName); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var out []byte
|
||||
if out, err = ExecuteTmplFile(tmpl, data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
/*arr:=strings.Split(string(out))
|
||||
var newArr []string
|
||||
for*/
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func IsMethod(arr map[string][]string, method string) bool {
|
||||
if arr == nil {
|
||||
return false
|
||||
|
@ -153,15 +175,12 @@ func FieldTypeParentTable(field *structs.Field) string {
|
|||
return FieldType(&structs.Field{Type: field.TypeParentTable})
|
||||
}
|
||||
|
||||
func FieldType(field *structs.Field) string {
|
||||
if field == nil {
|
||||
log.Fatal("field is empty")
|
||||
func FieldTypeStr(value string) string {
|
||||
value = strings.ToLower(strings.Trim(value, " "))
|
||||
if value == "" {
|
||||
return value
|
||||
}
|
||||
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
|
||||
if field.Type == "" {
|
||||
field.Type = "text"
|
||||
}
|
||||
switch field.Type {
|
||||
switch value {
|
||||
case "text":
|
||||
return "*string"
|
||||
case "string":
|
||||
|
@ -186,11 +205,24 @@ func FieldType(field *structs.Field) string {
|
|||
return "[]byte"
|
||||
default:
|
||||
fkTmp := &structs.Field{
|
||||
Name: field.Type,
|
||||
Name: value,
|
||||
}
|
||||
return FieldName(fkTmp)
|
||||
//log.Fatalf("Unknow format %s", field.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func FieldType(field *structs.Field) string {
|
||||
if field == nil {
|
||||
log.Fatal("field is empty")
|
||||
}
|
||||
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
|
||||
value := FieldTypeStr(field.Type)
|
||||
if value == "" {
|
||||
field.Type = "text"
|
||||
value = "text"
|
||||
}
|
||||
return value
|
||||
//return ""
|
||||
}
|
||||
|
||||
|
@ -212,15 +244,14 @@ func HasFieldType(fields []structs.Field, typeName string) (has bool) {
|
|||
return false
|
||||
}
|
||||
|
||||
func FieldTypeDB(field *structs.Field) string {
|
||||
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
|
||||
switch field.Type {
|
||||
func FieldTypeDBStr(value string, length *int) string {
|
||||
switch value {
|
||||
case "text":
|
||||
return "TEXT"
|
||||
case "string":
|
||||
tmp := "VARCHAR"
|
||||
if field.Length != nil && *field.Length == 0 {
|
||||
tmp += fmt.Sprintf("(%d)", *field.Length)
|
||||
if length != nil && *length == 0 {
|
||||
tmp += fmt.Sprintf("(%d)", *length)
|
||||
} else {
|
||||
tmp += "(255)"
|
||||
}
|
||||
|
@ -243,11 +274,16 @@ func FieldTypeDB(field *structs.Field) string {
|
|||
case "bool":
|
||||
return "int"
|
||||
default:
|
||||
log.Fatalf("Unknow format %s", field.Type)
|
||||
log.Fatalf("Unknow format %s", value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func FieldTypeDB(field *structs.Field) string {
|
||||
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
|
||||
return FieldTypeDBStr(field.Type, field.Length)
|
||||
}
|
||||
|
||||
// Генерирование описания таблиц в БД
|
||||
func FieldDescript(field *structs.Field, fieldId bool) (str string) {
|
||||
str = "`"
|
||||
|
@ -317,7 +353,6 @@ func ConfigParamTag(field *structs.ParamConfig) string {
|
|||
tag += fmt.Sprintf(" help:\"%s\"", field.Help)
|
||||
}
|
||||
tag += "`"
|
||||
// TODO
|
||||
return tag
|
||||
}
|
||||
|
||||
|
@ -333,6 +368,46 @@ func WriteTmplFile(filename string, outname string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Чтение шаблона и его подготовка
|
||||
func ReadTmplFile(filename string) (tmpl *template.Template, err error) {
|
||||
var (
|
||||
buff []byte
|
||||
)
|
||||
if buff, err = Content.ReadFile(filename); err != nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
tmp string
|
||||
//tmpl *template.Template
|
||||
)
|
||||
if tmp, err = RandomHex(4); err != nil {
|
||||
return
|
||||
}
|
||||
if _, ok := funcMap["includeTemplPart"]; !ok {
|
||||
funcMap["includeTemplPart"] = IncludeTemplPart
|
||||
}
|
||||
tmpl, err = template.New(tmp).Funcs(funcMap).Parse(string(buff))
|
||||
return
|
||||
}
|
||||
|
||||
// Запись файла на диск
|
||||
func WriteFile(outname string, data []byte) (err error) {
|
||||
err = os.WriteFile(outname, data, 0755)
|
||||
return
|
||||
}
|
||||
|
||||
// Выполнить шаблон
|
||||
func ExecuteTmplFile(tmpl *template.Template, data interface{}) (out []byte, err error) {
|
||||
var b bytes.Buffer
|
||||
w := bufio.NewWriter(&b)
|
||||
if err = tmpl.Execute(w, data); err != nil {
|
||||
return
|
||||
}
|
||||
w.Flush()
|
||||
out = b.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
// Использование шаблона
|
||||
func PrepareTmplFile(filename string, data interface{}, outname string) (err error) {
|
||||
dir := filepath.Dir(outname)
|
||||
|
@ -341,36 +416,25 @@ func PrepareTmplFile(filename string, data interface{}, outname string) (err err
|
|||
}
|
||||
|
||||
fmt.Printf("Generate: %s\n", outname)
|
||||
var (
|
||||
buff []byte
|
||||
)
|
||||
if buff, err = Content.ReadFile(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
tmp string
|
||||
tmpl *template.Template
|
||||
)
|
||||
if tmp, err = RandomHex(4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tmpl, err = template.New(tmp).Funcs(funcMap).Parse(string(buff)); err != nil {
|
||||
/*var b bytes.Buffer
|
||||
w := bufio.NewWriter(&b)*/
|
||||
var tmpl *template.Template
|
||||
if tmpl, err = ReadTmplFile(filename); err != nil {
|
||||
return
|
||||
}
|
||||
/*if err = tmpl.Execute(w, data); err != nil {
|
||||
return err
|
||||
}
|
||||
var b bytes.Buffer
|
||||
w := bufio.NewWriter(&b)
|
||||
|
||||
if err = tmpl.Execute(w, data); err != nil {
|
||||
return err
|
||||
w.Flush()*/
|
||||
var out []byte
|
||||
if out, err = ExecuteTmplFile(tmpl, data); err != nil {
|
||||
return
|
||||
}
|
||||
w.Flush()
|
||||
if err = os.WriteFile(outname, b.Bytes(), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
return WriteFile(outname, out)
|
||||
}
|
||||
|
||||
// Подготовка файла, если его нет на диске
|
||||
func PrepareTmplIsNotExists(filename string, data interface{}, outname string) (err error) {
|
||||
|
||||
if err = MkdirIsNotExists(filepath.Dir(outname)); err != nil {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{{ $varNameField := fieldNamePrepare .Name }}
|
||||
Table {{ fieldNameLowerPrepare $varNameField }} {
|
||||
id {{ fieldTypeDBStr .Pk nil }} [primary key]
|
||||
{{ range $index, $field := .Fields }}
|
||||
{{ fieldNameLowerPrepare $field.Name }} {{ fieldTypeDB $field }}
|
||||
{{ end }}
|
||||
{{ range $index, $field := .FkFields }}
|
||||
{{ fieldNameLowerPrepare $field.Name }}_id {{ fieldNameLowerPrepare $field.TypeParentTable }}
|
||||
{{ end }}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{{ includeTemplPart "tmpl/docs/db/user.tmpl" nil }}
|
||||
{{ includeTemplPart "tmpl/docs/db/role.tmpl" nil }}
|
||||
{{ includeTemplPart "tmpl/docs/db/user-role.tmpl" nil }}
|
||||
{{ range $index, $table := .Tables }}
|
||||
{{ includeTemplPart "tmpl/docs/db/entity.tmpl" $table }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $indexi, $table := .Tables }}
|
||||
{{ $varNameTable := fieldNamePrepare $table.Name }}
|
||||
{{range $indexj, $field := $table.FkFields }}
|
||||
{{ $fieldTypeParentTable := "" }}
|
||||
{{ if eq $field.Name "Parent" }}{{ $fieldTypeParentTable = fieldNamePrepare $.Name }}{{ else }}{{ $fieldTypeParentTable = fieldType $field }}{{ end }}
|
||||
Ref: {{ fieldNameLowerPrepare $varNameTable }}.{{ fieldNameLowerPrepare $field.Name }}_id > {{ fieldNameLowerPrepare $fieldTypeParentTable }}.id
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ includeTemplPart "tmpl/docs/db/user-role-relations.tmpl" nil }}
|
|
@ -0,0 +1,5 @@
|
|||
Table role {
|
||||
id UUID [primary key]
|
||||
name VARCHAR(20)
|
||||
description VARCHAR(20)
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Ref: user_role.user_id > user.id
|
||||
Ref: user_role.role_id > role.id
|
|
@ -0,0 +1,5 @@
|
|||
Table user_role {
|
||||
id UUID
|
||||
user_id UUID
|
||||
role_id UUID
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
Table user {
|
||||
id UUID [primary key]
|
||||
login VARCHAR
|
||||
surname VARCHAR(100)
|
||||
name VARCHAR(100)
|
||||
farname VARCHAR(100)
|
||||
email VARCHAR(200)
|
||||
password VARCGAR
|
||||
ldap BOOLEAN
|
||||
attempt INT
|
||||
}
|
Loading…
Reference in New Issue