Files
catalyst/generator/generator.go
2021-12-13 00:39:15 +01:00

343 lines
8.0 KiB
Go

package main
import (
"bytes"
"embed"
"encoding/json"
"flag"
"fmt"
"go/format"
"log"
"net/url"
"os"
"path"
"strings"
"text/template"
"github.com/go-openapi/analysis"
"github.com/go-swagger/go-swagger/generator"
"github.com/iancoleman/strcase"
"github.com/tidwall/sjson"
"gopkg.in/yaml.v3"
)
//go:embed templates/simplemodel.gotmpl
var model embed.FS
func gotype(name string, s Schema, required []string) string {
_, x := sgotype(name, s, required, false)
return x
}
func sgotype(name string, s Schema, required []string, nopointer bool) (bool, string) {
req := ""
if !nopointer && !contains(required, name) {
req = "*"
}
if s.Ref != "" {
return false, req + path.Base(s.Ref)
}
primitive := false
t := ""
switch s.Type {
case "string":
if s.Format == "date-time" {
t = req + "time.Time"
} else {
t = req + "string"
primitive = true
}
case "boolean":
t = req + "bool"
primitive = true
case "object":
if s.AdditionalProperties != nil {
subPrimitive, subType := sgotype(name, *s.AdditionalProperties, required, true)
if subPrimitive {
t = "map[string]" + subType
} else {
t = "map[string]*" + subType
}
} else {
t = "interface{}"
}
case "number", "integer":
if s.Format != "" {
t = req + s.Format
} else {
t = req + "int"
}
primitive = true
case "array":
subPrimitive, subType := sgotype(name, *s.Items, required, true)
if subPrimitive {
t = "[]" + subType
} else {
t = "[]*" + subType
}
case "":
t = "interface{}"
default:
panic(fmt.Sprintf("%#v", s))
}
return primitive, t
}
func omitempty(name string, required []string) bool {
return !contains(required, name)
}
func contains(required []string, name string) bool {
for _, r := range required {
if r == name {
return true
}
}
return false
}
func tojson(name string, i Definition) string {
b, _ := json.Marshal(i)
b, _ = sjson.SetBytes(b, "$id", "#/definitions/"+name)
return string(b)
}
func camel(s string) string {
if s == "id" {
return "ID"
}
return strcase.ToCamel(s)
}
func main() {
flag.Parse()
p := flag.Arg(0)
log.SetFlags(log.LstdFlags | log.Lshortfile)
f, err := os.Open("generated/community.yml")
if err != nil {
log.Fatalln(err)
}
defer f.Close()
s := Swagger{}
dec := yaml.NewDecoder(f)
err = dec.Decode(&s)
if err != nil {
log.Fatalln(err)
}
t := template.New("simplemodel.gotmpl")
t.Funcs(map[string]interface{}{
"camel": camel,
"gotype": gotype,
"omitempty": omitempty,
"tojson": tojson,
})
templ := template.Must(t.ParseFS(model, "templates/simplemodel.gotmpl"))
err = os.MkdirAll("generated/models", os.ModePerm)
if err != nil {
log.Fatalln(err)
}
buf := bytes.NewBufferString("")
props := map[string][]string{}
for defName, definition := range s.Definitions {
for propName := range definition.Properties {
props[defName] = append(props[defName], propName)
}
}
// for _, definition := range s.Definitions {
// if definition.Embed != "" {
// if parentProps, ok := props[definition.Embed]; ok {
// for _, parentProp := range parentProps {
// delete(definition.Properties, parentProp)
// }
// }
// }
// }
err = templ.Execute(buf, &s)
if err != nil {
log.Fatalln(err)
}
fmtCode, err := format.Source(buf.Bytes())
if err != nil {
log.Println(err)
fmtCode = buf.Bytes()
}
err = os.WriteFile("generated/models/models.go", fmtCode, os.ModePerm)
if err != nil {
log.Fatalln(err)
}
generator.FuncMapFunc = func(opts *generator.LanguageOpts) template.FuncMap {
df := generator.DefaultFuncMap(opts)
df["path"] = func(basePath, lpath string, parameters generator.GenParameters) string {
u := url.URL{Path: path.Join(basePath, lpath)}
q := u.Query()
for _, p := range parameters {
if p.Location == "path" {
if example, ok := p.Extensions["x-example"]; ok {
u.Path = strings.ReplaceAll(u.Path, "{"+p.Name+"}", fmt.Sprint(example))
}
}
if p.Location == "query" {
if example, ok := p.Extensions["x-example"]; ok {
q.Set(p.Name, fmt.Sprint(example))
}
}
}
u.RawQuery = q.Encode()
return u.String()
}
df["body"] = func(parameters generator.GenParameters) interface{} {
for _, p := range parameters {
if p.Location == "body" {
if example, ok := p.Extensions["x-example"]; ok {
return example
}
}
}
return nil
}
df["ginizePath"] = func(path string) string {
return strings.Replace(strings.Replace(path, "{", ":", -1), "}", "", -1)
}
df["export"] = func(name string) string {
return strings.ToUpper(name[0:1]) + name[1:]
}
df["basePaths"] = func(operations []generator.GenOperation) []string {
var l []string
var seen = map[string]bool{}
for _, operation := range operations {
if _, ok := seen[operation.BasePath]; !ok {
l = append(l, strings.TrimPrefix(operation.BasePath, "/"))
seen[operation.BasePath] = true
}
}
return l
}
df["roles"] = func(reqs []analysis.SecurityRequirement) string {
for _, req := range reqs {
if req.Name == "roles" {
var roles []string
for _, scope := range req.Scopes {
roles = append(roles, "role."+strcase.ToCamel(strings.ReplaceAll(scope, ":", "_")))
// roles = append(roles, permission.FromString(scope))
}
return strings.Join(roles, ", ")
}
}
return ""
}
return df
}
opts := &generator.GenOpts{
Spec: "generated/community.yml",
Target: "generated",
APIPackage: "operations",
ModelPackage: "models",
ServerPackage: "restapi",
ClientPackage: "client",
DefaultScheme: "http",
IncludeModel: true,
IncludeValidator: true,
IncludeHandler: true,
IncludeParameters: true,
IncludeResponses: true,
IncludeURLBuilder: true,
IncludeMain: true,
IncludeSupport: true,
ValidateSpec: true,
FlattenOpts: &analysis.FlattenOpts{
Minimal: true,
Verbose: true,
},
Name: "catalyst-test",
FlagStrategy: "go-flags",
CompatibilityMode: "modern",
Sections: generator.SectionOpts{
Application: []generator.TemplateOpts{
{
Name: "api-server-test",
Source: path.Join(p, "templates/api_server_test.gotmpl"),
Target: "{{ .Target }}/test",
FileName: "api_server_test.go",
},
// {
// Name: "configure",
// Source: "generator/config.gotmpl",
// Target: "{{ joinFilePath .Target .ServerPackage }}",
// FileName: "config.go",
// SkipExists: false,
// SkipFormat: false,
// },
{
Name: "embedded_spec",
Source: "asset:swaggerJsonEmbed",
Target: "{{ joinFilePath .Target .ServerPackage }}",
FileName: "embedded_spec.go",
},
{
Name: "server",
Source: path.Join(p, "templates/api.gotmpl"),
Target: "{{ joinFilePath .Target .ServerPackage }}",
FileName: "api.go",
},
{
Name: "response.go",
Source: path.Join(p, "templates/response.gotmpl"),
Target: "{{ .Target }}/restapi/api",
FileName: "response.go",
},
},
Operations: []generator.TemplateOpts{
{
Name: "parameters",
Source: path.Join(p, "templates/parameter.gotmpl"),
Target: "{{ if gt (len .Tags) 0 }}{{ joinFilePath .Target .ServerPackage .APIPackage .Package }}{{ else }}{{ joinFilePath .Target .ServerPackage .Package }}{{ end }}",
FileName: "{{ (snakize (pascalize .Name)) }}_parameters.go",
},
},
Models: []generator.TemplateOpts{
{
Name: "definition",
Source: "asset:model",
Target: "{{ joinFilePath .Target .ModelPackage }}/old",
FileName: "{{ (snakize (pascalize .Name)) }}.go",
},
// {
// Name: "model",
// Source: "generator/model.gotmpl",
// Target: "{{ joinFilePath .Target .ModelPackage }}/old2",
// FileName: "{{ (snakize (pascalize .Name)) }}.go",
// },
},
},
}
err = opts.EnsureDefaults()
if err != nil {
log.Fatalln(err)
}
err = generator.GenerateServer("catalyst", nil, nil, opts)
if err != nil {
log.Fatalln(err)
}
// loads.Spec()
// swagger.
}