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  }