mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-07 07:42:45 +01:00
343 lines
8.0 KiB
Go
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.
|
|
}
|