mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-08 23:56:37 +02:00
Created a storage interface used by authenticator to support multiple types of storage types for private keys. Added a new file storage engine which is now the default for web-api. Migrated aws secrets manager to be optional.
346 lines
10 KiB
Go
346 lines
10 KiB
Go
package dbtable2crud
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"go/format"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"geeks-accelerator/oss/saas-starter-kit/example-project/tools/truss/internal/goparse"
|
|
"github.com/dustin/go-humanize/english"
|
|
"github.com/fatih/camelcase"
|
|
"github.com/iancoleman/strcase"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// loadTemplateObjects executes a template file based on the given model struct and
|
|
// returns the parsed go objects.
|
|
func loadTemplateObjects(log *log.Logger, model *modelDef, templateDir, filename string, tmptData map[string]interface{}) ([]*goparse.GoObject, error) {
|
|
|
|
// Data used to execute all the of defined code sections in the template file.
|
|
if tmptData == nil {
|
|
tmptData = make(map[string]interface{})
|
|
}
|
|
tmptData["Model"] = model
|
|
|
|
// geeks-accelerator/oss/saas-starter-kit/example-project
|
|
|
|
// Read the template file from the local file system.
|
|
tempFilePath := filepath.Join(templateDir, filename)
|
|
dat, err := ioutil.ReadFile(tempFilePath)
|
|
if err != nil {
|
|
err = errors.WithMessagef(err, "Failed to read template file %s", tempFilePath)
|
|
return nil, err
|
|
}
|
|
|
|
// New template with custom functions.
|
|
baseTmpl := template.New("base")
|
|
baseTmpl.Funcs(template.FuncMap{
|
|
"Concat": func(vals ...string) string {
|
|
return strings.Join(vals, "")
|
|
},
|
|
"JoinStrings": func(vals []string, sep string) string {
|
|
return strings.Join(vals, sep)
|
|
},
|
|
"PrefixAndJoinStrings": func(vals []string, pre, sep string) string {
|
|
l := []string{}
|
|
for _, v := range vals {
|
|
l = append(l, pre+v)
|
|
}
|
|
return strings.Join(l, sep)
|
|
},
|
|
"FmtAndJoinStrings": func(vals []string, fmtStr, sep string) string {
|
|
l := []string{}
|
|
for _, v := range vals {
|
|
l = append(l, fmt.Sprintf(fmtStr, v))
|
|
}
|
|
return strings.Join(l, sep)
|
|
},
|
|
"FormatCamel": func(name string) string {
|
|
return FormatCamel(name)
|
|
},
|
|
"FormatCamelTitle": func(name string) string {
|
|
return FormatCamelTitle(name)
|
|
},
|
|
"FormatCamelLower": func(name string) string {
|
|
if name == "ID" {
|
|
return "id"
|
|
}
|
|
return FormatCamelLower(name)
|
|
},
|
|
"FormatCamelLowerTitle": func(name string) string {
|
|
return FormatCamelLowerTitle(name)
|
|
},
|
|
"FormatCamelPluralTitle": func(name string) string {
|
|
return FormatCamelPluralTitle(name)
|
|
},
|
|
"FormatCamelPluralTitleLower": func(name string) string {
|
|
return FormatCamelPluralTitleLower(name)
|
|
},
|
|
"FormatCamelPluralCamel": func(name string) string {
|
|
return FormatCamelPluralCamel(name)
|
|
},
|
|
"FormatCamelPluralLower": func(name string) string {
|
|
return FormatCamelPluralLower(name)
|
|
},
|
|
"FormatCamelPluralUnderscore": func(name string) string {
|
|
return FormatCamelPluralUnderscore(name)
|
|
},
|
|
"FormatCamelPluralLowerUnderscore": func(name string) string {
|
|
return FormatCamelPluralLowerUnderscore(name)
|
|
},
|
|
"FormatCamelUnderscore": func(name string) string {
|
|
return FormatCamelUnderscore(name)
|
|
},
|
|
"FormatCamelLowerUnderscore": func(name string) string {
|
|
return FormatCamelLowerUnderscore(name)
|
|
},
|
|
"FieldTagHasOption": func(f modelField, tagName, optName string) bool {
|
|
if f.Tags == nil {
|
|
return false
|
|
}
|
|
ft, err := f.Tags.Get(tagName)
|
|
if ft == nil || err != nil {
|
|
return false
|
|
}
|
|
if ft.Name == optName || ft.HasOption(optName) {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
"FieldTag": func(f modelField, tagName string) string {
|
|
if f.Tags == nil {
|
|
return ""
|
|
}
|
|
ft, err := f.Tags.Get(tagName)
|
|
if ft == nil || err != nil {
|
|
return ""
|
|
}
|
|
return ft.String()
|
|
},
|
|
"FieldTagReplaceOrPrepend": func(f modelField, tagName, oldVal, newVal string) string {
|
|
if f.Tags == nil {
|
|
return ""
|
|
}
|
|
ft, err := f.Tags.Get(tagName)
|
|
if ft == nil || err != nil {
|
|
return ""
|
|
}
|
|
|
|
if ft.Name == oldVal || ft.Name == newVal {
|
|
ft.Name = newVal
|
|
} else if ft.HasOption(oldVal) {
|
|
for idx, val := range ft.Options {
|
|
if val == oldVal {
|
|
ft.Options[idx] = newVal
|
|
}
|
|
}
|
|
} else if !ft.HasOption(newVal) {
|
|
if ft.Name == "" {
|
|
ft.Name = newVal
|
|
} else {
|
|
ft.Options = append(ft.Options, newVal)
|
|
}
|
|
}
|
|
|
|
return ft.String()
|
|
},
|
|
"StringListHasValue": func(list []string, val string) bool {
|
|
for _, v := range list {
|
|
if v == val {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
},
|
|
})
|
|
|
|
// Load the template file using the text/template package.
|
|
tmpl, err := baseTmpl.Parse(string(dat))
|
|
if err != nil {
|
|
err = errors.WithMessagef(err, "Failed to parse template file %s", tempFilePath)
|
|
log.Printf("loadTemplateObjects : %v\n%v", err, string(dat))
|
|
return nil, err
|
|
}
|
|
|
|
// Generate a list of template names defined in the template file.
|
|
tmplNames := []string{}
|
|
for _, defTmpl := range tmpl.Templates() {
|
|
tmplNames = append(tmplNames, defTmpl.Name())
|
|
}
|
|
|
|
// Stupid hack to return template names the in order they are defined in the file.
|
|
tmplNames, err = templateFileOrderedNames(tempFilePath, tmplNames)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Loop over all the defined templates, execute using the defined data, parse the
|
|
// formatted code and append the parsed go objects to the result list.
|
|
var resp []*goparse.GoObject
|
|
for _, tmplName := range tmplNames {
|
|
// Executed the defined template with the given data.
|
|
var tpl bytes.Buffer
|
|
if err := tmpl.Lookup(tmplName).Execute(&tpl, tmptData); err != nil {
|
|
err = errors.WithMessagef(err, "Failed to execute %s from template file %s", tmplName, tempFilePath)
|
|
return resp, err
|
|
}
|
|
|
|
// Format the source code to ensure its valid and code to parsed consistently.
|
|
codeBytes, err := format.Source(tpl.Bytes())
|
|
if err != nil {
|
|
err = errors.WithMessagef(err, "Failed to format source for %s in template file %s", tmplName, filename)
|
|
|
|
dl := []string{}
|
|
for idx, l := range strings.Split(tpl.String(), "\n") {
|
|
dl = append(dl, fmt.Sprintf("%d -> ", idx)+l)
|
|
}
|
|
|
|
log.Printf("loadTemplateObjects : %v\n%v", err, strings.Join(dl, "\n"))
|
|
return resp, err
|
|
}
|
|
|
|
// Remove extra white space from the code.
|
|
codeStr := strings.TrimSpace(string(codeBytes))
|
|
|
|
// Split the code into a list of strings.
|
|
codeLines := strings.Split(codeStr, "\n")
|
|
|
|
// Parse the code lines into a set of objects.
|
|
objs, err := goparse.ParseLines(codeLines, 0)
|
|
if err != nil {
|
|
err = errors.WithMessagef(err, "Failed to parse %s in template file %s", tmplName, filename)
|
|
log.Printf("loadTemplateObjects : %v\n%v", err, codeStr)
|
|
return resp, err
|
|
}
|
|
|
|
// Append the parsed objects to the return result list.
|
|
for _, obj := range objs.List() {
|
|
if obj.Name == "" && obj.Type != goparse.GoObjectType_Import && obj.Type != goparse.GoObjectType_Var && obj.Type != goparse.GoObjectType_Const && obj.Type != goparse.GoObjectType_Comment && obj.Type != goparse.GoObjectType_LineBreak {
|
|
// All objects should have a name except for multiline var/const declarations and comments.
|
|
err = errors.Errorf("Failed to parse name with type %s from lines: %v", obj.Type, obj.Lines())
|
|
return resp, err
|
|
} else if string(obj.Type) == "" {
|
|
err = errors.Errorf("Failed to parse type for %s from lines: %v", obj.Name, obj.Lines())
|
|
return resp, err
|
|
}
|
|
|
|
resp = append(resp, obj)
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// FormatCamel formats Valdez mountain to ValdezMountain
|
|
func FormatCamel(name string) string {
|
|
return strcase.ToCamel(name)
|
|
}
|
|
|
|
// FormatCamelLower formats ValdezMountain to valdezmountain
|
|
func FormatCamelLower(name string) string {
|
|
return strcase.ToLowerCamel(FormatCamel(name))
|
|
}
|
|
|
|
// FormatCamelTitle formats ValdezMountain to Valdez Mountain
|
|
func FormatCamelTitle(name string) string {
|
|
return strings.Join(camelcase.Split(name), " ")
|
|
}
|
|
|
|
// FormatCamelLowerTitle formats ValdezMountain to valdez mountain
|
|
func FormatCamelLowerTitle(name string) string {
|
|
return strings.ToLower(FormatCamelTitle(name))
|
|
}
|
|
|
|
// FormatCamelPluralTitle formats ValdezMountain to Valdez Mountains
|
|
func FormatCamelPluralTitle(name string) string {
|
|
pts := camelcase.Split(name)
|
|
lastIdx := len(pts) - 1
|
|
pts[lastIdx] = english.PluralWord(2, pts[lastIdx], "")
|
|
return strings.Join(pts, " ")
|
|
}
|
|
|
|
// FormatCamelPluralTitleLower formats ValdezMountain to valdez mountains
|
|
func FormatCamelPluralTitleLower(name string) string {
|
|
return strings.ToLower(FormatCamelPluralTitle(name))
|
|
}
|
|
|
|
// FormatCamelPluralCamel formats ValdezMountain to ValdezMountains
|
|
func FormatCamelPluralCamel(name string) string {
|
|
return strcase.ToCamel(FormatCamelPluralTitle(name))
|
|
}
|
|
|
|
// FormatCamelPluralLower formats ValdezMountain to valdezmountains
|
|
func FormatCamelPluralLower(name string) string {
|
|
return strcase.ToLowerCamel(FormatCamelPluralTitle(name))
|
|
}
|
|
|
|
// FormatCamelPluralUnderscore formats ValdezMountain to Valdez_Mountains
|
|
func FormatCamelPluralUnderscore(name string) string {
|
|
return strings.Replace(FormatCamelPluralTitle(name), " ", "_", -1)
|
|
}
|
|
|
|
// FormatCamelPluralLowerUnderscore formats ValdezMountain to valdez_mountains
|
|
func FormatCamelPluralLowerUnderscore(name string) string {
|
|
return strings.ToLower(FormatCamelPluralUnderscore(name))
|
|
}
|
|
|
|
// FormatCamelUnderscore formats ValdezMountain to Valdez_Mountain
|
|
func FormatCamelUnderscore(name string) string {
|
|
return strings.Replace(FormatCamelTitle(name), " ", "_", -1)
|
|
}
|
|
|
|
// FormatCamelLowerUnderscore formats ValdezMountain to valdez_mountain
|
|
func FormatCamelLowerUnderscore(name string) string {
|
|
return strings.ToLower(FormatCamelUnderscore(name))
|
|
}
|
|
|
|
// templateFileOrderedNames returns the template names the in order they are defined in the file.
|
|
func templateFileOrderedNames(localPath string, names []string) (resp []string, err error) {
|
|
file, err := os.Open(localPath)
|
|
if err != nil {
|
|
return resp, errors.WithStack(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
idxList := []int{}
|
|
idxNames := make(map[int]string)
|
|
|
|
idx := 0
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
if !strings.HasPrefix(scanner.Text(), "{{") || !strings.Contains(scanner.Text(), "define ") {
|
|
continue
|
|
}
|
|
|
|
for _, name := range names {
|
|
if strings.Contains(scanner.Text(), "\""+name+"\"") {
|
|
idxList = append(idxList, idx)
|
|
idxNames[idx] = name
|
|
break
|
|
}
|
|
}
|
|
|
|
idx = idx + 1
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return resp, errors.WithStack(err)
|
|
}
|
|
|
|
sort.Ints(idxList)
|
|
|
|
for _, idx := range idxList {
|
|
resp = append(resp, idxNames[idx])
|
|
}
|
|
|
|
return resp, nil
|
|
}
|