3
0
Files
corteza/codegen/v2/events/main.go

238 lines
5.7 KiB
Go

package main
import (
"flag"
"fmt"
"github.com/cortezaproject/corteza-server/codegen/v2/internal"
"github.com/cortezaproject/corteza-server/pkg/cli"
"gopkg.in/yaml.v2"
"os"
"strings"
"text/template"
)
const (
templateFiles = "codegen/v2/events/*.tpl"
)
type (
eventDefMap map[string]eventDef
eventDef struct {
On []string `yaml:"on"`
BeforeAfter []string `yaml:"ba"`
Properties []eventProps `yaml:"props"`
Result string `yaml:"result"`
}
eventProps struct {
Name string
Type string
// Import path for prop type, use package's type by default (see importTypePathTpl)
Import string
// Set property internally only, not via constructor
Internal bool
// Do not allow change of the variable through
Immutable bool
}
tplPayload struct {
Command string
YAML string
Service string
Package string
// List of imports
// Used only by generated file and not pre-generated-user-file
Imports []string
// will be used as string
ResourceString string
// will be used as (go) ident
ResourceIdent string
Events eventDef
}
)
// Use for generating .go and .adoc files from yaml
//
// To generate .go files, run for each service:
// ./event-gen --service messaging
//
// To generate .adoc files, run for each service + add path to docs
//// ./event-gen --service system --docs ../corteza-docs/src/extdev/development/events/
func main() {
tpl := template.New("").Funcs(map[string]interface{}{
"camelCase": internal.CamelCase,
"makeEvents": makeEvents,
})
tpl = template.Must(tpl.ParseGlob(templateFiles))
var (
definitionsPathStr string
serviceStr string
docsPathStr string
overwrite bool
decoder *yaml.Decoder
tplData = &tplPayload{}
)
const (
defResArgName = "result"
yamlDefFileName = "events.yaml"
definitionsPathTpl = "%s/service/event/"
importTypePathTpl = "github.com/cortezaproject/corteza-server/%s/types"
importAuthPath = "github.com/cortezaproject/corteza-server/pkg/auth"
)
flag.StringVar(&definitionsPathStr, "definitions", "", "Location of event definitions file (generated from service if omitted) and output dir")
flag.StringVar(&serviceStr, "service", "", "Service name (system, compose, messaging)")
flag.BoolVar(&overwrite, "overwrite", false, "Overwrite all files")
flag.StringVar(&docsPathStr, "docs", "", "Path to docs (generates .adoc files with documentation)")
flag.Parse()
if serviceStr == "" {
cli.HandleError(fmt.Errorf("can not generate event code without service"))
}
if definitionsPathStr == "" {
definitionsPathStr = fmt.Sprintf(definitionsPathTpl, serviceStr)
}
if f, err := os.Open(definitionsPathStr + yamlDefFileName); err != nil {
cli.HandleError(err)
} else {
decoder = yaml.NewDecoder(f)
}
tplData.Command = "go run ./codegen/v2/events --service " + serviceStr
tplData.YAML = definitionsPathStr + yamlDefFileName
tplData.Package = "event"
tplData.Service = serviceStr
defs := eventDefMap{}
cli.HandleError(decoder.Decode(&defs))
for resName, evDef := range defs {
var (
l = len(serviceStr)
fname = resName
)
tplData.Imports = make([]string, 0)
tplData.ResourceString = resName
if resName == serviceStr {
// if resource name = service name, leave it as-is
tplData.ResourceIdent = resName
} else if len(resName) <= l || resName[:l+2] == serviceStr+":" {
// check if resource name is shorter and has invalid prefix
cli.HandleError(fmt.Errorf("invalid resource prefix: %q", resName))
} else {
tplData.ResourceIdent = internal.CamelCase(strings.Split(resName[l+1:], ":")...)
fname = resName[l+1:]
fname = strings.ReplaceAll(fname, ":", "_")
fname = strings.ReplaceAll(fname, "-", "_")
}
// Prepare the data
// no default ("result") result set, use first one from properties
if evDef.Result == "" && len(evDef.Properties) > 0 {
evDef.Result = evDef.Properties[0].Name
}
// Invoker - user that invoked (triggered) the event
evDef.Properties = append(evDef.Properties, eventProps{
Name: "invoker",
Type: "auth.Identifiable",
Import: importAuthPath,
Immutable: false,
Internal: true,
})
for _, p := range evDef.Properties {
if p.Import == "" {
if strings.HasPrefix(p.Type, "*types.") || strings.HasPrefix(p.Type, "types.") {
p.Import = fmt.Sprintf(importTypePathTpl, serviceStr)
}
}
if p.Import == "" {
// Do not import empty paths
continue
}
if inSlice(p.Import, tplData.Imports) {
continue
}
tplData.Imports = append(tplData.Imports, p.Import)
}
tplData.Events = evDef
switch true {
case len(docsPathStr) > 0:
var (
docOutput = fmt.Sprintf("%s/%s/%s.gen.adoc", docsPathStr, serviceStr, fname)
)
internal.WritePlainTo(tpl, tplData, "events.gen.adoc.tpl", docOutput)
default:
var (
usrOutput = fmt.Sprintf("%s%s.go", definitionsPathStr, fname)
genOutput = fmt.Sprintf("%s/%s.gen.go", definitionsPathStr, fname)
)
_, err := os.Stat(usrOutput)
if overwrite || os.IsNotExist(err) {
internal.WriteFormattedTo(tpl, tplData, "events.go.tpl", usrOutput)
}
internal.WriteFormattedTo(tpl, tplData, "events.gen.go.tpl", genOutput)
}
}
}
func inSlice(s string, ss []string) bool {
for i := range ss {
if ss[i] == s {
return true
}
}
return false
}
// Merge on/before/after events
func makeEvents(def eventDef) []string {
return append(
makeEventGroup("on", def.On),
append(
makeEventGroup("before", def.BeforeAfter),
makeEventGroup("after", def.BeforeAfter)...,
)...,
)
}
func makeEventGroup(pfix string, ee []string) (out []string) {
for _, e := range ee {
out = append(out, pfix+strings.ToUpper(e[:1])+e[1:])
}
return
}