Codegen for automation types & functions
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
. "github.com/cortezaproject/corteza-server/pkg/y7s"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type (
|
||||
// definitions are in one file
|
||||
aFuncDefs struct {
|
||||
Package string
|
||||
Name string
|
||||
Source string
|
||||
Prefix string
|
||||
outputDir string
|
||||
|
||||
// List of imports
|
||||
// Used only by generated file and not pre-generated-user-file
|
||||
Imports []string
|
||||
|
||||
Functions aFunctionSet
|
||||
}
|
||||
|
||||
aFunctionSet []aFuncDef
|
||||
|
||||
aFuncDef struct {
|
||||
Name string
|
||||
Meta *types.FunctionMeta
|
||||
Params aFuncParamSet
|
||||
Results aFuncResultSet
|
||||
}
|
||||
|
||||
aFuncParamSet []*aFuncParamDef
|
||||
aFuncResultSet []*aFuncResultDef
|
||||
|
||||
aFuncParamDef struct {
|
||||
Name string
|
||||
Required bool
|
||||
SetOf bool
|
||||
Types []*aFuncParamTypeVarDef
|
||||
Meta *types.ParamMeta
|
||||
}
|
||||
|
||||
aFuncParamTypeVarDef struct {
|
||||
WorkflowType string `yaml:"wf"`
|
||||
GoType string `yaml:"go"`
|
||||
Suffix string
|
||||
}
|
||||
|
||||
aFuncResultDef struct {
|
||||
Name string
|
||||
Required bool
|
||||
SetOf bool
|
||||
WorkflowType string `yaml:"wf"`
|
||||
GoType string `yaml:"go"`
|
||||
Meta *types.ParamMeta
|
||||
}
|
||||
)
|
||||
|
||||
func procAutomationFunctions(mm ...string) (dd []*aFuncDefs, err error) {
|
||||
for _, m := range mm {
|
||||
f, err := os.Open(m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s read failed: %w", m, err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
d = &aFuncDefs{
|
||||
Package: "automation",
|
||||
Source: m,
|
||||
Name: path.Base(m),
|
||||
outputDir: path.Dir(m),
|
||||
}
|
||||
)
|
||||
|
||||
d.Name = d.Name[:len(d.Name)-13]
|
||||
|
||||
if err := yaml.NewDecoder(f).Decode(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dd = append(dd, d)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (set *aFunctionSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return Each(n, func(k *yaml.Node, v *yaml.Node) (err error) {
|
||||
def := aFuncDef{Name: k.Value}
|
||||
|
||||
if err = v.Decode(&def); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*set = append(*set, def)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (set *aFuncParamSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return Each(n, func(k *yaml.Node, v *yaml.Node) (err error) {
|
||||
def := aFuncParamDef{}
|
||||
if k != nil {
|
||||
def.Name = k.Value
|
||||
}
|
||||
|
||||
*set = append(*set, &def)
|
||||
return v.Decode(&def)
|
||||
})
|
||||
}
|
||||
|
||||
func (set *aFuncResultSet) UnmarshalYAML(n *yaml.Node) error {
|
||||
return Each(n, func(k *yaml.Node, v *yaml.Node) (err error) {
|
||||
def := aFuncResultDef{}
|
||||
if k != nil {
|
||||
def.Name = k.Value
|
||||
}
|
||||
|
||||
*set = append(*set, &def)
|
||||
return v.Decode(&def)
|
||||
})
|
||||
}
|
||||
|
||||
func genAutomationFunctions(tpl *template.Template, dd ...*aFuncDefs) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplAFuncGen = tpl.Lookup("afunc.gen.go.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
for _, d := range dd {
|
||||
// Generic code, actions for every resource goes to a separated file
|
||||
dst = path.Join(d.outputDir, path.Base(d.Source)[:strings.LastIndex(path.Base(d.Source), ".")]+".gen.go")
|
||||
json.NewEncoder(os.Stdout).SetIndent("", " ")
|
||||
err = goTemplate(dst, tplAFuncGen, d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package {{ .Package }}
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// {{ .Source }}
|
||||
|
||||
import (
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var (
|
||||
{{ $.Name }} = &{{ $.Name }}Handler{}
|
||||
)
|
||||
|
||||
func (h {{ $.Name }}Handler) register(reg func(*atypes.Function)) {
|
||||
{{- range .Functions }}
|
||||
reg(h.{{ export .Name }}())
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ range .Functions }}
|
||||
{{ $REF := unexport $.Prefix $.Name .Name }}
|
||||
{{ $ARGS := unexport $.Name .Name "Args" }}
|
||||
{{ $RESULTS := unexport $.Name .Name "Results" }}
|
||||
|
||||
|
||||
type (
|
||||
{{ $ARGS }} struct {
|
||||
{{ range .Params }}
|
||||
{{ $NAME := .Name }}
|
||||
has{{ export .Name }} bool
|
||||
{{ if gt (len .Types) 1 }}
|
||||
{{ export .Name }} interface{}
|
||||
{{- range .Types }}
|
||||
{{ $NAME }}{{ export .Suffix }} {{ .GoType }}
|
||||
{{- end }}
|
||||
{{- else -}}
|
||||
{{ range .Types }}
|
||||
{{ export $NAME }}{{ export .Suffix }} {{ .GoType }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if .Results }}
|
||||
{{ unexport $.Name .Name }}Results struct {
|
||||
{{ range .Results }}
|
||||
{{ export .Name }} {{ .GoType }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
//
|
||||
//
|
||||
// expects implementation of {{ .Name }} function:
|
||||
// func (h {{ $.Name }}) {{ .Name }}(ctx context.Context, args *{{ $ARGS }}) (results *{{ $RESULTS }}, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h {{ $.Name }}Handler) {{ export .Name }}() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: {{ printf "%q" ( $REF ) }},
|
||||
{{- if .Meta }}
|
||||
Meta: &atypes.FunctionMeta{
|
||||
{{- if .Meta.Short }}
|
||||
Short: {{ printf "%q" .Meta.Short }},
|
||||
{{- end }}
|
||||
{{- if .Meta.Description }}
|
||||
Description: {{ printf "%q" .Meta.Description }},
|
||||
{{- end }}
|
||||
{{- if .Meta.Visual }}
|
||||
Visual: {{ printf "%#v" .Meta.Visual }},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{{- range .Params }}
|
||||
{
|
||||
Name: {{ printf "%q" .Name }},
|
||||
Types: []string{ {{ range .Types }}({{ .WorkflowType }}{}).Type(),{{ end }} },
|
||||
{{- if .Required }}Required: true,{{ end }}
|
||||
{{- if .SetOf }}SetOf: true,{{ end }}
|
||||
{{- if .Meta }}
|
||||
Meta: &atypes.ParamMeta{
|
||||
{{- if .Meta.Label }}
|
||||
Label: {{ printf "%#v" .Meta.Label }},
|
||||
{{- end }}
|
||||
{{- if .Meta.Description }}
|
||||
Description: {{ printf "%#v" .Meta.Description }},
|
||||
{{- end }}
|
||||
{{- if .Meta.Visual }}
|
||||
Visual: {{ printf "%#v" .Meta.Visual }},
|
||||
{{- end }}
|
||||
},
|
||||
{{ end }}
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
|
||||
{{ if .Results }}
|
||||
Results: []*atypes.Param{
|
||||
{{ range .Results }}
|
||||
atypes.NewParam({{ printf "%q" .Name }},
|
||||
atypes.Types(&{{ .WorkflowType }}{}),
|
||||
),
|
||||
{{ end }}
|
||||
},
|
||||
{{ end }}
|
||||
|
||||
Handler: func(ctx context.Context, in expr.Vars) (out expr.Vars, err error) {
|
||||
var (
|
||||
args = &{{ $ARGS }}{
|
||||
{{- range .Params }}
|
||||
has{{ export .Name }}: in.Has({{ printf "%q" .Name }}),
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if .Results }}
|
||||
results *{{ $RESULTS }}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
{{ range .Params }}
|
||||
{{ $NAME := .Name }}
|
||||
{{ if gt (len .Types) 1 }}
|
||||
// Converting {{ export .Name }} to go type
|
||||
switch casted := args.{{ export .Name }}.(type) {
|
||||
{{- range .Types }}
|
||||
case {{ .GoType }}:
|
||||
args.{{ $NAME }}{{ export .Suffix }} = casted
|
||||
{{- end -}}
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ if .Results }}
|
||||
if results, err = h.{{ .Name }}(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = expr.Vars{}
|
||||
|
||||
{{- range .Results }}
|
||||
if out[{{ printf "%q" .Name }}], err = ({{ .WorkflowType }}{}).Cast(results.{{ export .Name }}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
return
|
||||
{{- else }}
|
||||
return out, h.{{ .Name }}(ctx, args)
|
||||
{{- end }}
|
||||
},
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,47 @@
|
||||
package {{ .Package }}
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// {{ .Source }}
|
||||
|
||||
{{ if .Imports }}
|
||||
import (
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
{{- if ne .Package "expr" }}
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
{{- end }}
|
||||
)
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ $TypedValue := "TypedValue" }}
|
||||
{{ if ne .Package "expr" }}
|
||||
{{ $TypedValue = "expr.TypedValue" }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $exprType, $nativeType := .Types }}
|
||||
// {{ $exprType }} is an expression type, wrapper for {{ $nativeType }} type
|
||||
type {{ $exprType }} struct{ value {{ $nativeType }} }
|
||||
|
||||
// New{{ $exprType }} creates new instance of {{ $exprType }} expression type
|
||||
func New{{ $exprType }}(new interface{}) ({{ $TypedValue }}, error) {
|
||||
t := &{{ $exprType }}{}
|
||||
return t, t.Set(new)
|
||||
}
|
||||
|
||||
// Returns underlying value on {{ $exprType }}
|
||||
func (t {{ $exprType }}) Get() interface{} { return t.value }
|
||||
|
||||
// Returns type name
|
||||
func ({{ $exprType }}) Type() string { return "{{ $exprType }}" }
|
||||
|
||||
// Casts value to {{ $nativeType }}
|
||||
func ({{ $exprType }}) Cast(value interface{}) ({{ $TypedValue }}, error) { return New{{ $exprType }}(value) }
|
||||
|
||||
{{ end }}
|
||||
@@ -47,6 +47,12 @@ func Proc() {
|
||||
typeSrc []string
|
||||
typeDefs []*typesDef
|
||||
|
||||
// workaround because
|
||||
// filepath.Join merges "*","*" into "**" instead of "*/*"
|
||||
exprTypeSrcPath = filepath.Join("*"+string(filepath.Separator)+"*", "expr_types.yaml")
|
||||
exprTypeSrc []string
|
||||
exprTypeDefs []*exprTypesDef
|
||||
|
||||
restSrcPath = filepath.Join("*", "rest.yaml")
|
||||
restSrc []string
|
||||
restDefs []*restDef
|
||||
@@ -59,6 +65,10 @@ func Proc() {
|
||||
optionSrc []string
|
||||
optionDefs []*optionsDef
|
||||
|
||||
aFuncsSrcPath = filepath.Join("*", "automation", "*_handler.yaml")
|
||||
aFuncsSrc []string
|
||||
aFuncsDefs []*aFuncDefs
|
||||
|
||||
tpls *template.Template
|
||||
tplBase = template.New("").
|
||||
Funcs(map[string]interface{}{
|
||||
@@ -142,6 +152,9 @@ func Proc() {
|
||||
typeSrc = glob(typeSrcPath)
|
||||
output("loaded %d type definitions from %s\n", len(typeSrc), typeSrcPath)
|
||||
|
||||
exprTypeSrc = glob(exprTypeSrcPath)
|
||||
output("loaded %d exprType definitions from %s\n", len(exprTypeSrc), exprTypeSrcPath)
|
||||
|
||||
restSrc = glob(restSrcPath)
|
||||
output("loaded %d rest definitions from %s\n", len(restSrc), restSrcPath)
|
||||
|
||||
@@ -151,6 +164,9 @@ func Proc() {
|
||||
optionSrc = glob(optionSrcPath)
|
||||
output("loaded %d option definitions from %s\n", len(optionSrc), optionSrcPath)
|
||||
|
||||
aFuncsSrc = glob(aFuncsSrcPath)
|
||||
output("loaded %d function definitions from %s\n", len(aFuncsSrc), aFuncsSrcPath)
|
||||
|
||||
if watchChanges {
|
||||
if watcher != nil {
|
||||
watcher.Close()
|
||||
@@ -163,9 +179,11 @@ func Proc() {
|
||||
fileList = append(fileList, actionSrc...)
|
||||
fileList = append(fileList, eventSrc...)
|
||||
fileList = append(fileList, typeSrc...)
|
||||
fileList = append(fileList, exprTypeSrc...)
|
||||
fileList = append(fileList, restSrc...)
|
||||
fileList = append(fileList, storeSrc...)
|
||||
fileList = append(fileList, optionSrc...)
|
||||
fileList = append(fileList, aFuncsSrc...)
|
||||
|
||||
for _, d := range fileList {
|
||||
handleError(watcher.Add(d))
|
||||
@@ -211,6 +229,16 @@ func Proc() {
|
||||
return
|
||||
}
|
||||
|
||||
if exprTypeDefs, err = procExprTypes(exprTypeSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genExprTypes(tpls, exprTypeDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process expr types:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if restDefs, err = procRest(restSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genRest(tpls, restDefs...)
|
||||
@@ -245,6 +273,16 @@ func Proc() {
|
||||
return
|
||||
}
|
||||
|
||||
if aFuncsDefs, err = procAutomationFunctions(aFuncsSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genAutomationFunctions(tpls, aFuncsDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process store:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if !watchChanges {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type (
|
||||
exprTypesDef struct {
|
||||
// source file path
|
||||
Source string
|
||||
|
||||
// outputDir
|
||||
// dir where the source file is
|
||||
outputDir string
|
||||
|
||||
Imports []string
|
||||
Package string `yaml:"package"`
|
||||
Types map[string]string `yaml:"types"`
|
||||
}
|
||||
)
|
||||
|
||||
func procExprTypes(mm ...string) (dd []*exprTypesDef, err error) {
|
||||
dd = make([]*exprTypesDef, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
var (
|
||||
d = &exprTypesDef{
|
||||
Source: m,
|
||||
outputDir: path.Dir(m),
|
||||
|
||||
Package: "types",
|
||||
Types: make(map[string]string),
|
||||
}
|
||||
)
|
||||
|
||||
f, err := os.Open(m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s read failed: %w", m, err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if err := yaml.NewDecoder(f).Decode(d); err != nil {
|
||||
return nil, fmt.Errorf("%s decode failed: %w", m, err)
|
||||
}
|
||||
|
||||
dd = append(dd, d)
|
||||
}
|
||||
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
// Generates all type set files & accompanying tests
|
||||
//
|
||||
// generates 2 files per type definition
|
||||
func genExprTypes(tpl *template.Template, dd ...*exprTypesDef) (err error) {
|
||||
var (
|
||||
typeGen = tpl.Lookup("expr_types.gen.go.tpl")
|
||||
)
|
||||
|
||||
for _, d := range dd {
|
||||
err = goTemplate(path.Join(d.outputDir, "expr_types.gen.go"), typeGen, d)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user