github.com/kotovmak/go-admin@v1.1.1/plugins/admin/modules/tools/generator.go (about) 1 package tools 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/format" 7 "io/ioutil" 8 "path/filepath" 9 "regexp" 10 "strings" 11 "text/template" 12 13 "github.com/kotovmak/go-admin/modules/db/dialect" 14 "github.com/kotovmak/go-admin/modules/language" 15 16 "github.com/kotovmak/go-admin/modules/db" 17 "github.com/kotovmak/go-admin/modules/utils" 18 "github.com/kotovmak/go-admin/template/types/form" 19 ) 20 21 type Param struct { 22 Connection string `json:"connection"` 23 Driver string `json:"driver"` 24 Package string `json:"package"` 25 26 Table string `json:"table"` 27 RowTable string `json:"row_table"` 28 TableTitle string `json:"table_title"` 29 TableName string `json:"table_name"` 30 31 HideFilterArea bool `json:"hide_filter_area"` 32 HideNewButton bool `json:"hide_new_button"` 33 HideExportButton bool `json:"hide_export_button"` 34 HideEditButton bool `json:"hide_edit_button"` 35 HideDeleteButton bool `json:"hide_delete_button"` 36 HideDetailButton bool `json:"hide_detail_button"` 37 HideFilterButton bool `json:"hide_filter_button"` 38 HideRowSelector bool `json:"hide_row_selector"` 39 HidePagination bool `json:"hide_pagination"` 40 HideQueryInfo bool `json:"hide_query_info"` 41 FilterFormLayout form.Layout `json:"filter_form_layout"` 42 43 HideContinueEditCheckBox bool `json:"hide_continue_edit_check_box"` 44 HideContinueNewCheckBox bool `json:"hide_continue_new_check_box"` 45 HideResetButton bool `json:"hide_reset_button"` 46 HideBackButton bool `json:"hide_back_button"` 47 48 TablePageTitle string `json:"table_page_title"` 49 TableDescription string `json:"table_description"` 50 FormTitle string `json:"form_title"` 51 FormDescription string `json:"form_description"` 52 DetailTitle string `json:"detail_title"` 53 DetailDescription string `json:"detail_description"` 54 55 ExtraImport string `json:"extra_import"` 56 ExtraCode string `json:"extra_code"` 57 58 Fields Fields `json:"fields"` 59 FormFields Fields `json:"form_fields"` 60 DetailFields Fields `json:"detail_fields"` 61 62 PrimaryKey string `json:"primary_key"` 63 PrimaryKeyType string `json:"primary_key_type"` 64 65 DetailDisplay uint8 `json:"detail_display"` 66 67 Output string `json:"output"` 68 } 69 70 type Config struct { 71 Connection string `json:"connection"` 72 Driver string `json:"driver"` 73 Package string `json:"package"` 74 Table string `json:"table"` 75 Schema string `json:"schema"` 76 Output string `json:"output"` 77 Conn db.Connection `json:"conn"` 78 HideFilterArea bool `json:"hide_filter_area"` 79 HideNewButton bool `json:"hide_new_button"` 80 HideExportButton bool `json:"hide_export_button"` 81 HideEditButton bool `json:"hide_edit_button"` 82 HideDeleteButton bool `json:"hide_delete_button"` 83 HideDetailButton bool `json:"hide_detail_button"` 84 HideFilterButton bool `json:"hide_filter_button"` 85 HideRowSelector bool `json:"hide_row_selector"` 86 HidePagination bool `json:"hide_pagination"` 87 HideQueryInfo bool `json:"hide_query_info"` 88 FilterFormLayout form.Layout `json:"filter_form_layout"` 89 ExtraImport string `json:"extra_import"` 90 91 TableTitle string `json:"table_title"` 92 TableDescription string `json:"table_description"` 93 FormTitle string `json:"form_title"` 94 FormDescription string `json:"form_description"` 95 DetailTitle string `json:"detail_title"` 96 DetailDescription string `json:"detail_description"` 97 98 HideContinueEditCheckBox bool `json:"hide_continue_edit_check_box"` 99 HideContinueNewCheckBox bool `json:"hide_continue_new_check_box"` 100 HideResetButton bool `json:"hide_reset_button"` 101 HideBackButton bool `json:"hide_back_button"` 102 103 DetailDisplay uint8 `json:"detail_display"` 104 105 ExtraCode string `json:"extra_code"` 106 } 107 108 func fixedTable(table string) string { 109 if utils.InArray(keyWords, table) { 110 return table + "_" 111 } 112 return table 113 } 114 115 var keyWords = []string{"import", "package", "chan", "const", "func", "interface", "map", "struct", "type", 116 "var", "break", "case", "continue", "default", "defer", "else", "fallthrough", "for", "go", "goto", "if", 117 "range", "return", "select", "switch"} 118 119 func NewParam(cfg Config) *Param { 120 ta := camelcase(cfg.Table) 121 dbTable := cfg.Table 122 if cfg.Schema != "" { 123 dbTable = cfg.Schema + "." + cfg.Table 124 } 125 126 fields := getFieldsFromConn(cfg.Conn, dbTable, cfg.Driver) 127 tt := strings.Title(ta) 128 129 pkey, ptype := fields.GetPrimaryKey() 130 131 return &Param{ 132 Connection: cfg.Connection, 133 Driver: cfg.Driver, 134 Package: cfg.Package, 135 Table: fixedTable(ta), 136 TableTitle: tt, 137 TableName: dbTable, 138 HideFilterArea: cfg.HideFilterArea, 139 HideNewButton: cfg.HideNewButton, 140 HideExportButton: cfg.HideExportButton, 141 HideEditButton: cfg.HideEditButton, 142 HideDeleteButton: cfg.HideDeleteButton, 143 HideDetailButton: cfg.HideDetailButton, 144 HideFilterButton: cfg.HideFilterButton, 145 HideRowSelector: cfg.HideRowSelector, 146 HidePagination: cfg.HidePagination, 147 HideQueryInfo: cfg.HideQueryInfo, 148 FilterFormLayout: cfg.FilterFormLayout, 149 HideContinueEditCheckBox: cfg.HideContinueEditCheckBox, 150 HideContinueNewCheckBox: cfg.HideContinueNewCheckBox, 151 HideResetButton: cfg.HideResetButton, 152 HideBackButton: cfg.HideBackButton, 153 RowTable: cfg.Table, 154 Fields: fields, 155 FormFields: fields, 156 DetailDisplay: cfg.DetailDisplay, 157 Output: cfg.Output, 158 ExtraImport: cfg.ExtraImport, 159 ExtraCode: cfg.ExtraCode, 160 TablePageTitle: utils.SetDefault(cfg.TableTitle, "", tt), 161 TableDescription: utils.SetDefault(cfg.TableDescription, "", tt), 162 FormTitle: utils.SetDefault(cfg.FormTitle, "", tt), 163 FormDescription: utils.SetDefault(cfg.FormDescription, "", tt), 164 165 PrimaryKey: pkey, 166 PrimaryKeyType: ptype, 167 } 168 } 169 170 func NewParamWithFields(cfg Config, fields ...Fields) *Param { 171 ta := camelcase(cfg.Table) 172 dbTable := cfg.Table 173 if cfg.Schema != "" { 174 dbTable = cfg.Schema + "." + cfg.Table 175 } 176 177 if len(cfg.Output) > 0 && cfg.Output[len(cfg.Output)-1] == '/' { 178 cfg.Output = cfg.Output[:len(cfg.Output)-1] 179 } 180 181 tt := strings.Title(ta) 182 183 detailFields := make(Fields, 0) 184 if len(fields) > 2 { 185 detailFields = fields[2] 186 } 187 188 return &Param{ 189 Connection: cfg.Connection, 190 Driver: cfg.Driver, 191 Package: cfg.Package, 192 Table: ta, 193 TableTitle: tt, 194 TableName: dbTable, 195 RowTable: cfg.Table, 196 Fields: fields[0], 197 FormFields: fields[1], 198 DetailFields: detailFields, 199 HideFilterArea: cfg.HideFilterArea, 200 HideNewButton: cfg.HideNewButton, 201 HideExportButton: cfg.HideExportButton, 202 HideEditButton: cfg.HideEditButton, 203 HideDeleteButton: cfg.HideDeleteButton, 204 HideDetailButton: cfg.HideDetailButton, 205 HideFilterButton: cfg.HideFilterButton, 206 HideRowSelector: cfg.HideRowSelector, 207 HidePagination: cfg.HidePagination, 208 HideQueryInfo: cfg.HideQueryInfo, 209 FilterFormLayout: cfg.FilterFormLayout, 210 HideContinueEditCheckBox: cfg.HideContinueEditCheckBox, 211 HideContinueNewCheckBox: cfg.HideContinueNewCheckBox, 212 HideResetButton: cfg.HideResetButton, 213 HideBackButton: cfg.HideBackButton, 214 DetailDisplay: cfg.DetailDisplay, 215 Output: cfg.Output, 216 ExtraImport: cfg.ExtraImport, 217 ExtraCode: cfg.ExtraCode, 218 TablePageTitle: utils.SetDefault(cfg.TableTitle, "", tt), 219 TableDescription: utils.SetDefault(cfg.TableDescription, "", tt), 220 FormTitle: utils.SetDefault(cfg.FormTitle, "", tt), 221 FormDescription: utils.SetDefault(cfg.FormDescription, "", tt), 222 DetailTitle: utils.SetDefault(cfg.DetailTitle, "", tt), 223 DetailDescription: utils.SetDefault(cfg.DetailDescription, "", tt), 224 } 225 } 226 227 type Fields []Field 228 229 func (fs Fields) GetPrimaryKey() (string, string) { 230 for _, field := range fs { 231 if field.IsPrimaryKey { 232 return field.Name, field.DBType 233 } 234 } 235 return "", "" 236 } 237 238 type Field struct { 239 Head string `json:"head"` 240 Name string `json:"name"` 241 DBType string `json:"db_type"` 242 FormType string `json:"form_type"` 243 Filterable bool `json:"filterable"` 244 Sortable bool `json:"sortable"` 245 InfoEditable bool `json:"info_editable"` 246 Editable bool `json:"editable"` 247 Hide bool `json:"hide"` 248 FormHide bool `json:"form_hide"` 249 EditHide bool `json:"edit_hide"` 250 CreateHide bool `json:"create_hide"` 251 Default string `json:"default"` 252 CanAdd bool `json:"can_add"` 253 ExtraFun string `json:"extra_fun"` 254 IsPrimaryKey bool `json:"is_primary_key"` 255 } 256 257 func Generate(param *Param) error { 258 t, err := template.New("table_model").Parse(tableModelTmpl) 259 if err != nil { 260 return err 261 } 262 buf := new(bytes.Buffer) 263 err = t.Execute(buf, param) 264 if err != nil { 265 return err 266 } 267 c, err := format.Source(buf.Bytes()) 268 if err != nil { 269 return err 270 } 271 return ioutil.WriteFile(filepath.FromSlash(param.Output)+"/"+param.RowTable+".go", c, 0644) 272 } 273 274 const ( 275 commentStrEnd = "example end" 276 tablesEnd = "generators end" 277 ) 278 279 func GenerateTables(outputPath, packageName string, tables []string, isNew bool) error { 280 281 if len(outputPath) > 0 && outputPath[len(outputPath)-1] == '/' { 282 outputPath = outputPath[:len(outputPath)-1] 283 } 284 285 outputPath = filepath.FromSlash(outputPath) 286 fileExist := utils.FileExist(outputPath + "/tables.go") 287 288 if !isNew && !fileExist { 289 return nil 290 } 291 292 var ( 293 tableStr = "" 294 commentStr = "" 295 tablesContentByte []byte 296 tablesContent string 297 err error 298 ) 299 if fileExist { 300 tablesContentByte, err = ioutil.ReadFile(outputPath + "/tables.go") 301 if err != nil { 302 return err 303 } 304 tablesContent = string(tablesContentByte) 305 } 306 307 for i := 0; i < len(tables); i++ { 308 lowerTable := strings.ToLower(tables[i]) 309 if !strings.Contains(tablesContent, `"`+lowerTable+`"`) { 310 tableStr += fmt.Sprintf(` 311 "%s": Get%sTable, `, lowerTable, strings.Title(camelcase(tables[i]))) 312 313 if commentStr != "" { 314 commentStr += ` 315 ` 316 } 317 commentStr += fmt.Sprintf(`// "%s" => http://localhost:9033/admin/info/%s`, lowerTable, lowerTable) 318 } 319 } 320 321 commentStr += ` 322 // 323 // ` + commentStrEnd 324 tableStr += ` 325 326 // ` + tablesEnd 327 328 content := "" 329 330 if tablesContent != "" && strings.Contains(tablesContent, "/") { 331 replacer := strings.NewReplacer(`// `+commentStrEnd, commentStr, `// `+tablesEnd, tableStr) 332 tablesContent = replacer.Replace(tablesContent) 333 keep := `// example: 334 //` 335 keep2 := `, 336 337 // ` + tablesEnd 338 replacer2 := strings.NewReplacer(keep, keep, keep2, keep2, 339 `// 340 // 341 `, "//", `// 342 343 //`, "//", `// 344 // "`, `// "`, 345 346 `, 347 348 `, ",") 349 content = replacer2.Replace(tablesContent) 350 } else { 351 content = fmt.Sprintf(`// This file is generated by GoAdmin CLI adm. 352 package %s 353 354 import "github.com/kotovmak/go-admin/plugins/admin/modules/table" 355 356 // The key of Generators is the prefix of table info url. 357 // The corresponding value is the Form and Table data. 358 // 359 // http://{{config.Domain}}:{{Port}}/{{config.Prefix}}/info/{{key}} 360 // 361 // example: 362 // 363 %s 364 var Generators = map[string]table.Generator{ 365 %s 366 } 367 `, packageName, commentStr, tableStr) 368 } 369 370 c, err := format.Source([]byte(content)) 371 372 if err != nil { 373 return err 374 } 375 return ioutil.WriteFile(outputPath+"/tables.go", c, 0644) 376 } 377 378 func InsertPermissionOfTable(conn db.Connection, table string) { 379 table = strings.ToLower(table) 380 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("query", "generator"), table+"_query", "GET", "/info/"+table) 381 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("show edit form page", "generator"), table+"_show_edit", "GET", 382 "/info/"+table+"/edit") 383 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("show create form page", "generator"), table+"_show_create", "GET", 384 "/info/"+table+"/new") 385 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("edit", "generator"), table+"_edit", "POST", 386 "/edit/"+table) 387 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("create", "generator"), table+"_create", "POST", 388 "/new/"+table) 389 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("delete", "generator"), table+"_delete", "POST", 390 "/delete/"+table) 391 InsertPermissionInfoDB(conn, table+" "+language.GetWithScope("export", "generator"), table+"_export", "POST", 392 "/export/"+table) 393 } 394 395 func InsertPermissionInfoDB(conn db.Connection, name, slug, httpMethod, httpPath string) { 396 checkExist, err := db.WithDriver(conn).Table("goadmin_permissions"). 397 Where("slug", "=", slug). 398 First() 399 400 if db.CheckError(err, db.QUERY) { 401 panic(err) 402 } 403 404 if checkExist != nil { 405 return 406 } 407 408 _, err = db.WithDriver(conn).Table("goadmin_permissions"). 409 Insert(dialect.H{ 410 "name": name, 411 "slug": slug, 412 "http_method": httpMethod, 413 "http_path": httpPath, 414 }) 415 416 if db.CheckError(err, db.INSERT) { 417 panic(err) 418 } 419 } 420 421 func camelcase(s string) string { 422 arr := strings.Split(s, "_") 423 var res = "" 424 for i := 0; i < len(arr); i++ { 425 if i == 0 { 426 res += arr[i] 427 } else { 428 res += strings.Title(arr[i]) 429 } 430 } 431 return res 432 } 433 434 func getType(typeName string) string { 435 r, _ := regexp.Compile(`\(.*?\)`) 436 typeName = r.ReplaceAllString(typeName, "") 437 r2, _ := regexp.Compile(`unsigned(.*)`) 438 return strings.TrimSpace(strings.Title(strings.ToLower(r2.ReplaceAllString(typeName, "")))) 439 } 440 441 func getFieldsFromConn(conn db.Connection, table, driver string) Fields { 442 columnsModel, _ := db.WithDriver(conn).Table(table).ShowColumns() 443 444 fields := make(Fields, len(columnsModel)) 445 446 fieldField := "Field" 447 typeField := "Type" 448 if driver == "postgresql" { 449 fieldField = "column_name" 450 typeField = "udt_name" 451 } 452 if driver == "sqlite" { 453 fieldField = "name" 454 typeField = "type" 455 } 456 if driver == "mssql" { 457 fieldField = "column_name" 458 typeField = "data_type" 459 } 460 461 for i, model := range columnsModel { 462 typeName := getType(model[typeField].(string)) 463 isPrimaryKey := false 464 if columnKey, ok := model["Key"].(string); ok { 465 isPrimaryKey = columnKey == "PRI" 466 } 467 468 fields[i] = Field{ 469 Head: strings.Title(model[fieldField].(string)), 470 Name: model[fieldField].(string), 471 DBType: typeName, 472 CanAdd: true, 473 Editable: true, 474 IsPrimaryKey: isPrimaryKey, 475 FormType: form.GetFormTypeFromFieldType(db.DT(strings.ToUpper(typeName)), model[fieldField].(string)), 476 } 477 if model[fieldField].(string) == "id" { 478 fields[i].Filterable = true 479 } 480 } 481 482 return fields 483 }