Moving server files to ./server
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
// definitions are in multiple files and each definition
|
||||
// should produce one output
|
||||
actionsDef struct {
|
||||
Component string
|
||||
Source string
|
||||
outputDir string
|
||||
|
||||
// List of imports
|
||||
// Used only by generated file and not pre-generated-user-file
|
||||
Import []string `yaml:"import"`
|
||||
|
||||
Service string `yaml:"service"`
|
||||
Resource string `yaml:"resource"`
|
||||
|
||||
// Default severity for actions
|
||||
DefaultActionSeverity string `yaml:"defaultActionSeverity"`
|
||||
|
||||
// Default severity for errors
|
||||
DefaultErrorSeverity string `yaml:"defaultErrorSeverity"`
|
||||
|
||||
// If at least one of the errors has HTTP status defined,
|
||||
// add support for http errors
|
||||
SupportHttpErrors bool
|
||||
|
||||
Props []*propsDef
|
||||
Actions []*actionDef
|
||||
Errors []*errorDef
|
||||
}
|
||||
|
||||
// List of event/log properties that can/will be captured
|
||||
// and injected into log or message string
|
||||
propsDef struct {
|
||||
Name string
|
||||
Type string
|
||||
Fields []string
|
||||
Builtin bool
|
||||
}
|
||||
|
||||
actionDef struct {
|
||||
// Action name
|
||||
Action string `yaml:"action"`
|
||||
|
||||
// String to log when action is successful
|
||||
Log string `yaml:"log"`
|
||||
|
||||
// String to log when error was yield
|
||||
//ErrorLog string `yaml:"errorLog"`
|
||||
|
||||
// Action severity
|
||||
Severity string `yaml:"severity"`
|
||||
}
|
||||
|
||||
// Event definition
|
||||
errorDef struct {
|
||||
// Error key
|
||||
// message can contain {variables} from meta data
|
||||
Error string `yaml:"error"`
|
||||
|
||||
// Error key
|
||||
// message can contain {variables} from meta data
|
||||
Message string `yaml:"message"`
|
||||
|
||||
// Formatted and readable audit log message
|
||||
// message can contain {variables} from meta data
|
||||
Log string `yaml:"log"`
|
||||
|
||||
// Longer message or error description that can help resolving the error
|
||||
Details string `yaml:"details"`
|
||||
|
||||
// Relative link to content in the documentation
|
||||
Documentation string `yaml:"documentation"`
|
||||
|
||||
// Reference to "safe" error
|
||||
// safe error should hide any information that might cause
|
||||
// personal data leakage or expose system internals
|
||||
MaskedWith string `yaml:"maskedWith"`
|
||||
|
||||
// Error severity
|
||||
Severity string `yaml:"severity"`
|
||||
|
||||
// HTTP Status code for this error
|
||||
HttpStatus string `yaml:"httpStatus"`
|
||||
}
|
||||
)
|
||||
|
||||
// Processes multiple action definitions
|
||||
func procActions(mm ...string) (dd []*actionsDef, err error) {
|
||||
var (
|
||||
f io.ReadCloser
|
||||
d *actionsDef
|
||||
)
|
||||
|
||||
dd = make([]*actionsDef, 0)
|
||||
for _, m := range mm {
|
||||
err = func() error {
|
||||
|
||||
if f, err = os.Open(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
d = &actionsDef{Component: strings.SplitN(m, string(filepath.Separator), 2)[0]}
|
||||
|
||||
if err := yaml.NewDecoder(f).Decode(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = actionNormalize(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Source = m
|
||||
d.outputDir = path.Dir(m)
|
||||
|
||||
dd = append(dd, d)
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not process %s: %w", m, err)
|
||||
}
|
||||
}
|
||||
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
func actionNormalize(d *actionsDef) error {
|
||||
// Prepend generic error
|
||||
d.Errors = append([]*errorDef{{
|
||||
Error: "generic",
|
||||
Message: "failed to complete request due to internal error",
|
||||
Log: "{err}",
|
||||
Severity: "error",
|
||||
}}, d.Errors...)
|
||||
|
||||
// index known meta fields and sanitize types (no type => string type)
|
||||
knownProps := map[string]bool{
|
||||
"err": true,
|
||||
}
|
||||
|
||||
for _, m := range d.Props {
|
||||
if m.Type == "" {
|
||||
m.Type = "string"
|
||||
}
|
||||
|
||||
// very optimistic check if referenced type is builtin or not
|
||||
m.Builtin = !strings.Contains(m.Type, ".")
|
||||
|
||||
knownProps[m.Name] = true
|
||||
|
||||
for _, f := range m.Fields {
|
||||
knownProps[fmt.Sprintf("%s.%s", m.Name, f)] = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for _, a := range d.Actions {
|
||||
if a.Severity == "" {
|
||||
a.Severity = d.DefaultActionSeverity
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range d.Errors {
|
||||
if e.Severity == "" {
|
||||
e.Severity = d.DefaultErrorSeverity
|
||||
}
|
||||
|
||||
if e.HttpStatus != "" {
|
||||
d.SupportHttpErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
checkHandle := func(s string) error {
|
||||
if !handle.IsValid(s) {
|
||||
return fmt.Errorf("handle empty")
|
||||
|
||||
}
|
||||
|
||||
if !handle.IsValid(s) {
|
||||
return fmt.Errorf("invalid handle format: %q", s)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
placeholderMatcher := regexp.MustCompile(`\{\{(.+?)\}\}`)
|
||||
checkPlaceholders := func(def string, kind, s string) error {
|
||||
for _, match := range placeholderMatcher.FindAllStringSubmatch(s, 1) {
|
||||
placeholder := match[1]
|
||||
if !knownProps[placeholder] {
|
||||
return fmt.Errorf("unknown placeholder %q used in %s for %s", placeholder, def, kind)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, a := range d.Actions {
|
||||
checkHandle(a.Action)
|
||||
if a.Log == "" {
|
||||
// If no log is defined, use action handle
|
||||
a.Log = a.Action
|
||||
}
|
||||
|
||||
if err := checkPlaceholders(a.Action, "log", a.Log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range d.Errors {
|
||||
if err := checkHandle(e.Error); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkPlaceholders(e.Error, "message", e.Message); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkPlaceholders(e.Error, "log", e.Log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a actionsDef) Package() string {
|
||||
return path.Base(path.Dir(a.Source))
|
||||
}
|
||||
|
||||
func (a actionDef) SeverityConstName() string {
|
||||
return severityConstName(a.Severity)
|
||||
}
|
||||
|
||||
func (e errorDef) SeverityConstName() string {
|
||||
return severityConstName(e.Severity)
|
||||
}
|
||||
|
||||
func severityConstName(s string) string {
|
||||
switch strings.ToLower(s) {
|
||||
case "emergency":
|
||||
return "actionlog.Emergency"
|
||||
case "alert":
|
||||
return "actionlog.Alert"
|
||||
case "crit", "critical":
|
||||
return "actionlog.Critical"
|
||||
case "warn", "warning":
|
||||
return "actionlog.Warning"
|
||||
case "notice":
|
||||
return "actionlog.Notice"
|
||||
case "info", "informational":
|
||||
return "actionlog.Info"
|
||||
case "debug":
|
||||
return "actionlog.Debug"
|
||||
default:
|
||||
return "actionlog.Err"
|
||||
}
|
||||
}
|
||||
|
||||
func genActions(tpl *template.Template, dd ...*actionsDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplActionsGen = tpl.Lookup("actions.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")
|
||||
err = goTemplate(dst, tplActionsGen, d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
. "github.com/cortezaproject/corteza-server/pkg/y7s"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
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
|
||||
Kind string
|
||||
Labels map[string]string
|
||||
Meta *aFuncMetaDef
|
||||
Params aFuncParamSet
|
||||
Results aFuncResultSet
|
||||
}
|
||||
|
||||
aFuncParamSet []*aFuncParamDef
|
||||
aFuncResultSet []*aFuncResultDef
|
||||
|
||||
aFuncParamDef struct {
|
||||
Name string
|
||||
Required bool
|
||||
IsArray bool `yaml:"isArray"`
|
||||
Types []*aFuncParamTypeVarDef
|
||||
Meta *aFuncParamMetaDef
|
||||
}
|
||||
|
||||
aFuncParamTypeVarDef struct {
|
||||
WorkflowType string `yaml:"wf"`
|
||||
GoType string `yaml:"go"`
|
||||
Suffix string
|
||||
}
|
||||
|
||||
aFuncResultDef struct {
|
||||
Name string
|
||||
IsArray bool `yaml:"isArray"`
|
||||
WorkflowType string `yaml:"wf"`
|
||||
GoType string `yaml:"go"`
|
||||
Meta *aFuncParamMetaDef
|
||||
}
|
||||
|
||||
aFuncMetaDef struct {
|
||||
Short string
|
||||
Description string
|
||||
Visual map[string]interface{}
|
||||
}
|
||||
|
||||
aFuncParamMetaDef struct {
|
||||
Label string
|
||||
Description string
|
||||
Visual map[string]interface{}
|
||||
}
|
||||
)
|
||||
|
||||
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, fmt.Errorf("could not decode %s: %w", m, 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
|
||||
}
|
||||
|
||||
if def.Kind == "" {
|
||||
def.Kind = "function"
|
||||
}
|
||||
|
||||
*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 expandAutomationFunctionTypes(ff []*aFuncDefs, tt []*exprTypesDef) {
|
||||
// index of all known types
|
||||
ti := make(map[string]*exprTypeDef)
|
||||
|
||||
for _, t := range tt {
|
||||
for typ, d := range t.Types {
|
||||
ti[typ] = d
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range ff {
|
||||
for _, fn := range f.Functions {
|
||||
for _, p := range fn.Params {
|
||||
for _, t := range p.Types {
|
||||
if ti[t.WorkflowType] == nil {
|
||||
fmt.Printf("%s/%s(): unknown type %q used for param %q\n", f.Prefix, fn.Name, t.WorkflowType, p.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if t.GoType == "" {
|
||||
t.GoType = ti[t.WorkflowType].As
|
||||
if "[]TypedValue" == t.GoType {
|
||||
t.GoType = "[]expr.TypedValue"
|
||||
}
|
||||
}
|
||||
|
||||
if t.Suffix == "" && len(p.Types) > 1 {
|
||||
t.Suffix = t.WorkflowType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range fn.Results {
|
||||
if ti[r.WorkflowType] == nil {
|
||||
fmt.Printf("%s/%s(): unknown type %q used for result %q\n", f.Prefix, fn.Name, r.WorkflowType, r.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if r.GoType == "" {
|
||||
r.GoType = ti[r.WorkflowType].As
|
||||
if "[]TypedValue" == r.GoType {
|
||||
r.GoType = "[]expr.TypedValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// genAutomationFunctionDocs look for afunc.gen.adoc.tpl and generates afunc.gen.adoc from it
|
||||
func genAutomationFunctionDocs(tpl *template.Template, docsPath string, dd ...*aFuncDefs) (err error) {
|
||||
var (
|
||||
typeGenAdoc = tpl.Lookup("afunc.gen.adoc.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
dst = path.Join(docsPath, "afunc.gen.adoc")
|
||||
return plainTemplate(dst, typeGenAdoc, map[string]interface{}{
|
||||
"Definitions": dd,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
{{- range .Import }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
type (
|
||||
{{ $.Service }}ActionProps struct {
|
||||
{{- range $prop := $.Props }}
|
||||
{{ $prop.Name }} {{ $prop.Type }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if $.Actions }}
|
||||
{{ $.Service }}Action struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *{{ $.Service }}ActionProps
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ $.Service }}LogMetaKey struct {}
|
||||
{{ $.Service }}PropsMetaKey struct {}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
|
||||
{{- range $prop := $.Props }}
|
||||
// {{ camelCase "set" $prop.Name }} updates {{ $.Service }}ActionProps's {{ $prop.Name }}
|
||||
//
|
||||
// Allows method chaining
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *{{ $.Service }}ActionProps) {{ camelCase "set" $prop.Name }}({{ $prop.Name }} {{ $prop.Type }}) *{{ $.Service }}ActionProps {
|
||||
p.{{ $prop.Name }} = {{ $prop.Name }}
|
||||
return p
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
// Serialize converts {{ $.Service }}ActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p {{ $.Service }}ActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
{{ range $prop := $.Props }}
|
||||
{{- if $prop.Builtin }}
|
||||
m.Set("{{ $prop.Name }}", p.{{ $prop.Name }}, true)
|
||||
{{- else }}
|
||||
if p.{{ $prop.Name }} != nil {
|
||||
{{- range $f := $prop.Fields }}
|
||||
m.Set("{{ $prop.Name }}.{{ $f }}", p.{{ $prop.Name }}.{{ camelCase " " $f }}, true)
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p {{ $.Service }}ActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{"{{"}}err}}"}
|
||||
|
||||
{{- if $.Props }}
|
||||
// first non-empty string
|
||||
fns = func(ii ... interface{}) string {
|
||||
for _, i:= range ii {
|
||||
if s :=fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
{{- range $prop := $.Props }}
|
||||
{{- if $prop.Builtin }}
|
||||
pairs = append(pairs, "{{"{{"}}{{ $prop.Name }}}}", fns(p.{{ $prop.Name }}))
|
||||
{{- else }}
|
||||
|
||||
if p.{{ $prop.Name }} != nil {
|
||||
// replacement for "{{"{{"}}{{ $prop.Name }}}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{"{{"}}{{ $prop.Name }}}}",
|
||||
fns(
|
||||
{{- range $f := $prop.Fields }}
|
||||
p.{{ $prop.Name }}.{{ camelCase " " $f }},
|
||||
{{- end }}
|
||||
),
|
||||
)
|
||||
|
||||
{{- range $f := $prop.Fields }}
|
||||
pairs = append(pairs, "{{"{{"}}{{ $prop.Name }}.{{ $f }}}}", fns(p.{{ $prop.Name }}.{{ camelCase " " $f }}))
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
{{ if $.Actions }}
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (a *{{ $.Service }}Action) String() string {
|
||||
var props = &{{ $.Service }}ActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *{{ $.Service }}Action) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $.Actions }}
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
{{ range $a := $.Actions }}
|
||||
// {{ camelCase "" $.Service "Action" $a.Action }} returns "{{ $.Resource }}.{{ $a.Action }}" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func {{ camelCase "" $.Service "Action" $a.Action }}(props ... *{{ $.Service }}ActionProps) *{{ $.Service }}Action {
|
||||
a := &{{ $.Service }}Action{
|
||||
timestamp: time.Now(),
|
||||
resource: "{{ $.Resource }}",
|
||||
action: "{{ $a.Action }}",
|
||||
log: "{{ $a.Log }}",
|
||||
severity: {{ $a.SeverityConstName }},
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $.Errors }}
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
{{ range $e := $.Errors }}
|
||||
// {{ camelCase "" $.Service "Err" $e.Error }} returns "{{ $.Resource }}.{{ $e.Error }}" as *errors.Error
|
||||
//
|
||||
{{- if $e.MaskedWith }}
|
||||
// Note: This error will be wrapped with safe ({{ $.Resource }}.{{ $e.MaskedWith }}) error!
|
||||
{{- end }}
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func {{ camelCase "" $.Service "Err" $e.Error }}(mm ... *{{ $.Service }}ActionProps) *errors.Error {
|
||||
var p = &{{ $.Service }}ActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
{{ if $e.Message }}p.Format({{ printf "%q" $e.Message }}, nil){{ else }}{{ printf "%q" $e.Error }}{{ end }},
|
||||
|
||||
errors.Meta("type", {{ printf "%q" $e.Error }}),
|
||||
errors.Meta("resource", {{ printf "%q" $.Resource }}),
|
||||
|
||||
{{ if $e.Documentation }}
|
||||
// link to documentation; formatting applies in case we need some special link formatting
|
||||
errors.Meta("documentation", p.Format({{ printf "%q" $e.Documentation }}, nil)),
|
||||
{{ end -}}
|
||||
|
||||
{{ if $e.Details }}
|
||||
// details, used in detailed eror reporting
|
||||
errors.Meta("details", p.Format({{ printf "%q" $e.Details }}, nil)),
|
||||
{{ end -}}
|
||||
|
||||
|
||||
{{- if $e.Log }}
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta({{ $.Service }}LogMetaKey{}, {{ printf "%q" $e.Log }}),
|
||||
{{ end -}}
|
||||
errors.Meta({{ $.Service }}PropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, {{ printf "%q" $.Component }}),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "{{ kebabCase $.Service }}.errors.{{ $e.Error }}"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
{{ if $e.MaskedWith }}
|
||||
// Wrap with safe error
|
||||
e = {{ camelCase "" $.Service "Err" $e.MaskedWith }}().Wrap(e)
|
||||
{{ end }}
|
||||
|
||||
return e
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (svc {{ $.Service }}) recordAction(ctx context.Context, props *{{ $.Service }}ActionProps, actionFn func(... *{{ $.Service }}ActionProps) *{{ $.Service }}Action, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString({{ $.Service }}LogMetaKey{}), err)
|
||||
|
||||
if p, has := m[{{ $.Service }}PropsMetaKey{}]; has {
|
||||
a.Meta = p.(*{{ $.Service }}ActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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:
|
||||
{{- range .Definitions }}
|
||||
// - {{ .Source }}
|
||||
{{- end }}
|
||||
|
||||
{{ range $d := .Definitions }}
|
||||
= `{{ $d.Name }}`
|
||||
|
||||
[cols="2m,4a,3a"]
|
||||
|===
|
||||
| Name | Description | I/O
|
||||
|
||||
{{- range $f := .Functions }}
|
||||
|
||||
{{- if eq $f.Kind "function" }}
|
||||
|
||||
| [#fnc-{{ toLower $d.Name }}-{{ toLower $f.Name }}]#<<fnc-{{ toLower $d.Name }}-{{ toLower $f.Name }},{{ if $f.Meta.Short }}{{ $f.Meta.Short }}{{ else }}{{ $f.Name }}{{ end }}>>#
|
||||
| {{ if $f.Meta.Description }}{{ $f.Meta.Description }}{{ end }}
|
||||
|
|
||||
{{- if gt (len $f.Params) 0}}
|
||||
.Parameters:
|
||||
{{- range $p := $f.Params}}
|
||||
* {{ if $p.Required }}#*# {{ end }}`{{ if $p.Meta }}
|
||||
{{- if $p.Meta.Label }}
|
||||
{{- $p.Meta.Label }}
|
||||
{{- else }}
|
||||
{{- $p.Name }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- $p.Name }}
|
||||
{{- end }}`
|
||||
({{- range $pti, $pt := $p.Types }}
|
||||
`{{- $pt.WorkflowType }}`,
|
||||
{{- end }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if gt (len $f.Results) 0}}
|
||||
|
||||
.Results:
|
||||
{{- range $r := $f.Results}}
|
||||
* {{ if $r.Meta }}
|
||||
{{- if $r.Meta.Label }}
|
||||
{{- $r.Meta.Label }}
|
||||
{{- else }}
|
||||
{{- $r.Name }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- $r.Name }}
|
||||
{{- end }} (`{{ $r.WorkflowType }}`)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|===
|
||||
{{- end }}
|
||||
@@ -0,0 +1,269 @@
|
||||
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"
|
||||
"github.com/cortezaproject/corteza-server/pkg/wfexec"
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
{{ $.Name }}HandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h {{ $.Name }}Handler) register() {
|
||||
h.reg.AddFunctions(
|
||||
{{- range .Functions }}
|
||||
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 }}
|
||||
{{ $isArray := .IsArray }}
|
||||
has{{ export .Name }} bool
|
||||
{{- if gt (len .Types) 1 }}
|
||||
{{ export .Name }} interface{}
|
||||
{{- range .Types }}
|
||||
{{ $name }}{{ export .Suffix }} {{ if $isArray }}[]{{ end }}{{ .GoType }}
|
||||
{{- end }}
|
||||
{{- else -}}
|
||||
{{ range .Types }}
|
||||
{{ export $name }}{{ export .Suffix }} {{ if $isArray }}[]{{ end }}{{ .GoType }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if .Results }}
|
||||
{{ unexport $.Name .Name }}Results struct {
|
||||
{{ range .Results }}
|
||||
{{ export .Name }} {{ if .IsArray }}[]{{ end }}{{ .GoType }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
{{ range .Params }}
|
||||
{{- if gt (len .Types) 1 }}
|
||||
{{ $name := .Name }}
|
||||
{{ $isArray := .IsArray }}
|
||||
func (a {{ $ARGS }}) {{ export "get" $name }}() (bool, {{ range .Types }}{{ if $isArray }}[]{{ end }}{{ .GoType }},{{ end }}) {
|
||||
return a.has{{ export $name }}{{ range .Types }}, a.{{ $name }}{{ export .Suffix }}{{ end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
// {{ export .Name }} function {{ .Meta.Short }}
|
||||
//
|
||||
// expects implementation of {{ .Name }} function:
|
||||
// func (h {{ $.Name }}Handler) {{ .Name }}(ctx context.Context, args *{{ $ARGS }}) ({{ if .Results }}results *{{ $RESULTS }}, {{ end }}err error) {
|
||||
// return
|
||||
// }
|
||||
func (h {{ $.Name }}Handler) {{ export .Name }}() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: {{ printf "%q" ( $REF ) }},
|
||||
Kind: {{ printf "%q" .Kind }},
|
||||
Labels: {{ printf "%#v" .Labels }},
|
||||
{{- 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 }}{{ printf "%q" .WorkflowType }},{{ end }} },
|
||||
{{- if .Required }}Required: true,{{ end }}
|
||||
{{- if .IsArray }}IsArray: 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 }}
|
||||
{
|
||||
Name: {{ printf "%q" .Name }},
|
||||
Types: []string{ {{ printf "%q" .WorkflowType }} },
|
||||
{{ if .IsArray }}IsArray: 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 }}
|
||||
},
|
||||
{{ end }}
|
||||
|
||||
{{ if eq .Kind "iterator" }}
|
||||
Iterator: func(ctx context.Context, in *expr.Vars) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
args = &{{ $ARGS }}{
|
||||
{{- range .Params }}
|
||||
has{{ export .Name }}: in.Has({{ printf "%q" .Name }}),
|
||||
{{- end }}
|
||||
}
|
||||
)
|
||||
|
||||
{{ template "params" .Params }}
|
||||
|
||||
return h.{{ .Name }}(ctx, args)
|
||||
},
|
||||
{{ else }}
|
||||
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 }}
|
||||
}
|
||||
)
|
||||
|
||||
{{ template "params" .Params }}
|
||||
|
||||
{{ if .Results }}
|
||||
var results *{{ $RESULTS }}
|
||||
if results, err = h.{{ .Name }}(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{{- range .Results }}
|
||||
{{ if .IsArray }}
|
||||
|
||||
{
|
||||
// converting results.{{ export .Name }} ({{ .GoType }}) to Array (of {{ .WorkflowType }})
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
tarr = make([]expr.TypedValue, len(results.{{ export .Name }}))
|
||||
)
|
||||
|
||||
for i := range results.{{ export .Name }} {
|
||||
if tarr[i], err = h.reg.Type({{ printf "%q" .WorkflowType }}).Cast(results.{{ export .Name }}[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if tval, err = expr.NewArray(tarr); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, {{ printf "%q" .Name }}, tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{{ else }}
|
||||
|
||||
{
|
||||
// converting results.{{ export .Name }} ({{ .GoType }}) to {{ .WorkflowType }}
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type({{ printf "%q" .WorkflowType }}).Cast(results.{{ export .Name }}); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, {{ printf "%q" .Name }}, tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
return
|
||||
{{- else }}
|
||||
return out, h.{{ .Name }}(ctx, args)
|
||||
{{- end }}
|
||||
},
|
||||
{{ end }}
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ define "params" }}
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
{{ range . }}
|
||||
{{ $name := .Name }}
|
||||
{{ $isArray := .IsArray }}
|
||||
{{ if gt (len .Types) 1 }}
|
||||
// Converting {{ export .Name }} argument
|
||||
if args.has{{ export .Name }} {
|
||||
aux := expr.Must(expr.Select(in, {{ printf "%q" .Name }}))
|
||||
switch aux.Type() {
|
||||
{{- range .Types }}
|
||||
case h.reg.Type({{ printf "%q" .WorkflowType }}).Type():
|
||||
args.{{ $name }}{{ export .Suffix }} = aux.Get().({{ if $isArray }}[]{{ end }}{{ .GoType }})
|
||||
{{- end -}}
|
||||
}
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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:
|
||||
{{- range .Definitions }}
|
||||
// - {{ .Source }}
|
||||
{{- end }}
|
||||
|
||||
= Resources and events
|
||||
{{- range .Definitions }}
|
||||
{{- range .Resources }}
|
||||
|
||||
== {{ .ResourceString }}
|
||||
|
||||
=== Events
|
||||
|
||||
{{- if .BeforeAfter }}
|
||||
.Before/after events:
|
||||
{{- range $ba := .BeforeAfter }}
|
||||
* `before('{{ $ba }}')`
|
||||
{{- end }}
|
||||
{{- range $ba := .BeforeAfter }}
|
||||
* `after('{{ $ba }}')`
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ if .On -}}
|
||||
.On events:
|
||||
{{- range $on := .On }}
|
||||
* `on('{{ $on }}')`
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
=== Exec arguments
|
||||
|
||||
.Argument properties:
|
||||
[%header, cols=3*]
|
||||
|===
|
||||
|Name|Type|Mutable
|
||||
{{- range $p := .Properties }}
|
||||
| `{{ $p.Name }}`
|
||||
| `{{ $p.Type }}`
|
||||
{{- if $p.Immutable }}
|
||||
| no
|
||||
{{ else }}
|
||||
| yes
|
||||
{{ end -}}
|
||||
|
||||
{{ end -}}
|
||||
|===
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,240 @@
|
||||
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 (
|
||||
"encoding/json"
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
)
|
||||
|
||||
// dummy placing to simplify import generation logic
|
||||
var _ = json.NewEncoder
|
||||
|
||||
type (
|
||||
{{ range $r := $.Resources }}
|
||||
// {{ camelCase $r.ResourceIdent "base" }}
|
||||
//
|
||||
// This type is auto-generated.
|
||||
{{ camelCase $r.ResourceIdent "base" }} struct {
|
||||
immutable bool
|
||||
{{- range $r.Properties }}
|
||||
{{ .Name }} {{ .Type }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ range $event := $r.Events }}
|
||||
// {{ camelCase $r.ResourceIdent $event }}
|
||||
//
|
||||
// This type is auto-generated.
|
||||
{{ camelCase $r.ResourceIdent $event }} struct {
|
||||
*{{ camelCase $r.ResourceIdent "base" }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
|
||||
{{ range $r := $.Resources }}
|
||||
|
||||
// ResourceType returns "{{ $r.ResourceString }}"
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ({{ camelCase .ResourceIdent "base" }}) ResourceType() string {
|
||||
return "{{ .ResourceString }}"
|
||||
}
|
||||
|
||||
{{ range $event := $r.Events }}
|
||||
// EventType on {{ camelCase $r.ResourceIdent $event }} returns "{{ $event }}"
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ({{ camelCase $r.ResourceIdent $event }}) EventType() string {
|
||||
return "{{ $event }}"
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range $event := $r.Events }}
|
||||
// {{ camelCase "" $r.ResourceIdent $event }} creates {{ $event }} for {{ $r.ResourceString }} resource
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func {{ camelCase "" $r.ResourceIdent $event }}(
|
||||
{{- range $r.Properties }}
|
||||
{{- if not .Internal }}
|
||||
{{ camelCase "arg" .Name }} {{ .Type }},
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
) *{{ camelCase $r.ResourceIdent $event }} {
|
||||
return &{{ camelCase $r.ResourceIdent $event }}{
|
||||
{{ camelCase $r.ResourceIdent "base" }}: &{{ camelCase $r.ResourceIdent "base" }}{
|
||||
immutable: false,
|
||||
{{- range $r.Properties }}
|
||||
{{- if not .Internal }}
|
||||
{{ .Name }}: {{ camelCase "arg" .Name }},
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// {{ camelCase "" $r.ResourceIdent $event "Immutable" }} creates {{ $event }} for {{ $r.ResourceString }} resource
|
||||
//
|
||||
// None of the arguments will be mutable!
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func {{ camelCase "" $r.ResourceIdent $event "Immutable" }}(
|
||||
{{- range $r.Properties }}
|
||||
{{- if not .Internal }}
|
||||
{{ camelCase "arg" .Name }} {{ .Type }},
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
) *{{ camelCase $r.ResourceIdent $event }} {
|
||||
return &{{ camelCase $r.ResourceIdent $event }}{
|
||||
{{ camelCase $r.ResourceIdent "base" }}: &{{ camelCase $r.ResourceIdent "base" }}{
|
||||
immutable: true,
|
||||
{{- range $r.Properties }}
|
||||
{{- if not .Internal }}
|
||||
{{ .Name }}: {{ camelCase "arg" .Name }},
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
},
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
{{ range $r.Properties }}
|
||||
{{ if not .Immutable }}
|
||||
// {{ camelCase "Set" .Name }} sets new {{ .Name }} value
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (res *{{ camelCase $r.ResourceIdent "base" }}) {{ camelCase "Set" .Name }}({{ camelCase "arg" .Name }} {{ .Type }}) {
|
||||
res.{{ .Name }} = {{ camelCase "arg" .Name }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// {{ camelCase "" .Name }} returns {{ .Name }}
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (res {{ camelCase $r.ResourceIdent "base" }}) {{ camelCase "" .Name }}() {{ .Type }} {
|
||||
return res.{{ .Name }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
// Encode internal data to be passed as event params & arguments to triggered Corredor script
|
||||
func (res {{ camelCase .ResourceIdent "base" }}) Encode() (args map[string][]byte, err error) {
|
||||
{{- if $r.Properties }}
|
||||
args = make(map[string][]byte)
|
||||
|
||||
{{ range $prop := $r.Properties }}
|
||||
if args["{{ $prop.Name }}"], err = json.Marshal(res.{{ $prop.Name }}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
// Handle argument encoding
|
||||
{{ end -}}
|
||||
return
|
||||
}
|
||||
|
||||
// Encode internal data to be passed as event params & arguments to workflow
|
||||
func (res {{ camelCase .ResourceIdent "base" }}) EncodeVars() (out *expr.Vars, err error) {
|
||||
{{- if $r.Properties }}
|
||||
out = &expr.Vars{}
|
||||
var v expr.TypedValue
|
||||
|
||||
{{ range $r.Properties }}
|
||||
{{- if .ExprType }}
|
||||
if v, err = automation.{{ export "new" .ExprType }}(res.{{ .Name }}); err == nil {
|
||||
err = out.Set({{ printf "%q" .Name }},v)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
{{- else }}
|
||||
// Could not found expression-type counterpart for {{ .Type }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
_ = v
|
||||
return
|
||||
}
|
||||
|
||||
// Decode return values from Corredor script into struct props
|
||||
func (res *{{ camelCase .ResourceIdent "base" }}) Decode(results map[string][]byte)( err error) {
|
||||
if res.immutable {
|
||||
// Respect immutability
|
||||
return
|
||||
}
|
||||
|
||||
{{- if $r.Result }}
|
||||
if res.{{ $r.Result }} != nil {
|
||||
if r, ok := results["result"]; ok && len(results) == 1 {
|
||||
if err = json.Unmarshal(r, res.{{ $r.Result }}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
{{ end -}}
|
||||
|
||||
{{- range $prop := $r.Properties }}
|
||||
{{- if not $prop.Immutable }}
|
||||
if res.{{ $prop.Name }} != nil {
|
||||
if r, ok := results["{{ $prop.Name }}"]; ok {
|
||||
if err = json.Unmarshal(r, res.{{ $prop.Name }}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
{{ else }}
|
||||
// Do not decode {{ $prop.Name }}; marked as immutable
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (res *{{ camelCase .ResourceIdent "base" }}) DecodeVars(vars *expr.Vars) (err error) {
|
||||
if res.immutable {
|
||||
// Respect immutability
|
||||
return
|
||||
}
|
||||
|
||||
{{- range $r.Properties }}
|
||||
{{- if .Immutable }}
|
||||
// {{ .Name }} marked as immutable
|
||||
{{- else }}
|
||||
{{- if .ExprType }}
|
||||
if res.{{ .Name }} != nil && vars.Has({{ printf "%q" .Name }}) {
|
||||
var aux *automation.{{ export .ExprType }}
|
||||
aux, err = automation.{{ export "new" .ExprType }}(expr.Must(vars.Select({{ printf "%q" .Name }})))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res.{{ .Name }} = aux.GetValue()
|
||||
}
|
||||
{{- else }}
|
||||
// Could not find expression-type counterpart for {{ .Type }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
)
|
||||
|
||||
var _=eventbus.ConstraintMaker
|
||||
|
||||
// Match returns false if given conditions do not match event & resource internals
|
||||
func (res {{ camelCase .ResourceIdent "base" }}) Match(c eventbus.ConstraintMatcher) bool {
|
||||
// By default we match no mather what kind of constraints we receive
|
||||
//
|
||||
// Function will be called multiple times - once for every trigger constraint
|
||||
// All should match (return true):
|
||||
// constraint#1 AND constraint#2 AND constraint#3 ...
|
||||
//
|
||||
// When there are multiple values, Match() can decide how to treat them (OR, AND...)
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package rest
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
||||
func getEventTypeDefinitions() []eventTypeDef {
|
||||
return []eventTypeDef{
|
||||
{{ range .Definitions }}
|
||||
{{ range $r := .Resources }}
|
||||
{{ template "eventTypeDefinitions" dict "res" $r "type" "on" "types" .On }}
|
||||
{{ template "eventTypeDefinitions" dict "res" $r "type" "before" "types" .BeforeAfter }}
|
||||
{{ template "eventTypeDefinitions" dict "res" $r "type" "after" "types" .BeforeAfter }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
}
|
||||
}
|
||||
|
||||
{{ define "eventTypeDefinitions" }}
|
||||
{{ range $ev := $.types }}
|
||||
{
|
||||
ResourceType: {{ printf "%q" $.res.ResourceString }},
|
||||
EventType: {{ printf "%q" (camelCase $.type $ev) }},
|
||||
Properties: []eventTypePropertyDef{
|
||||
{{ range $.res.Properties }}
|
||||
{{ if not .Internal }}
|
||||
{
|
||||
Name: {{ printf "%q" .Name }},
|
||||
Type: {{ printf "%q" .ExprType }},
|
||||
Immutable: {{ printf "%v" .Immutable }},
|
||||
},
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
},
|
||||
Constraints: []eventTypeConstraintDef{
|
||||
{{ range $.res.Constraints }}
|
||||
{
|
||||
Name: {{ printf "%q" .Name }},
|
||||
},
|
||||
{{ end }}
|
||||
},
|
||||
},
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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:
|
||||
{{- range .Definitions }}
|
||||
// - {{ .Source }}
|
||||
{{- end }}
|
||||
|
||||
[cols="2m,3a"]
|
||||
|===
|
||||
| Type | Structure
|
||||
{{- range .Definitions }}
|
||||
{{- range $tName, $tDef := .Types }}
|
||||
{{- if gt (len $tDef.Struct) 0}}
|
||||
| [#objref-{{ toLower $tName }}]#<<objref-{{ toLower $tName }},{{ $tName }}>>#
|
||||
|
|
||||
{{- if $tDef.Struct }}
|
||||
[source]
|
||||
----
|
||||
{
|
||||
{{- range $s := .Struct }}
|
||||
{{ $s.Name }} ({{ $s.ExprType }})
|
||||
{{- end }}
|
||||
}
|
||||
----
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|===
|
||||
@@ -0,0 +1,218 @@
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
{{- if ne .Package "expr" }}
|
||||
. "github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var _ = context.Background
|
||||
var _ = fmt.Errorf
|
||||
|
||||
{{ range $exprType, $def := .Types }}
|
||||
{{ if not $def.CustomType }}
|
||||
// {{ $exprType }} is an expression type, wrapper for {{ $def.As }} type
|
||||
type {{ $exprType }} struct{
|
||||
value {{ $def.As }}
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// New{{ $exprType }} creates new instance of {{ $exprType }} expression type
|
||||
func New{{ $exprType }}(val interface{}) (*{{ $exprType }}, error) {
|
||||
if c, err := {{ export "CastTo" $exprType }}(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create {{ $exprType }}: %w", err)
|
||||
} else {
|
||||
return &{{ $exprType }}{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get return underlying value on {{ $exprType }}
|
||||
func (t *{{ $exprType }}) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on {{ $exprType }}
|
||||
func (t *{{ $exprType }}) GetValue() {{ $def.As }} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func ({{ $exprType }}) Type() string { return "{{ $exprType }}" }
|
||||
|
||||
// Cast converts value to {{ $def.As }}
|
||||
func ({{ $exprType }}) Cast(val interface{}) (TypedValue, error) {
|
||||
return New{{ $exprType }}(val)
|
||||
}
|
||||
|
||||
// Assign new value to {{ $exprType }}
|
||||
//
|
||||
// value is first passed through {{ export "CastTo" $exprType }}
|
||||
func (t *{{ $exprType }}) Assign(val interface{}) (error) {
|
||||
if c, err := {{ export "CastTo" $exprType }}(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
{{ if $def.Comparable }}
|
||||
{{ if not $def.CustomComparator }}
|
||||
// Compare the two {{ $exprType }} values
|
||||
func (t {{ $exprType }}) Compare(to TypedValue) (int, error) {
|
||||
c, err := New{{ $exprType }}(to)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot compare %s and %s: %s", t.Type(), c.Type(), err.Error())
|
||||
}
|
||||
|
||||
switch {
|
||||
case t.value == c.value:
|
||||
return 0, nil
|
||||
case t.value < c.value:
|
||||
return -1, nil
|
||||
case t.value > c.value:
|
||||
return 1, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("cannot compare %s and %s: unknown state", t.Type(), c.Type())
|
||||
}
|
||||
}
|
||||
{{ else }}
|
||||
// Compare the two {{ $exprType }} values
|
||||
func (t {{ $exprType }}) Compare(to TypedValue) (int, error) {
|
||||
return compareTo{{ $exprType }}(t, to)
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $def.Struct }}
|
||||
{{ if not $def.CustomFieldAssigner }}
|
||||
func (t *{{ $exprType }}) AssignFieldValue(key string, val TypedValue) error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return {{ $def.AssignerFn }}(t.value, key, val)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not $def.CustomGValSelector }}
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access {{ $exprType }}'s underlying value ({{ $def.As }})
|
||||
// and it's fields
|
||||
//
|
||||
func (t *{{ $exprType }}) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return {{ unexport $exprType "GValSelector" }}(t.value, k)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not $def.CustomSelector }}
|
||||
// Select is field accessor for {{ $def.As }}
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t *{{ $exprType }}) Select(k string) (TypedValue, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return {{ unexport $exprType "TypedValueSelector" }}(t.value, k)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (t *{{ $exprType }}) Has(k string) bool {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
switch k {
|
||||
{{- range $def.Struct }}
|
||||
{{- if .ExprType }}
|
||||
case {{ printf "%q" .Name }}{{ if .Alias }}, {{ printf "%q" .Alias }}{{ end }}:
|
||||
return true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// {{ unexport $exprType "GValSelector" }} is field accessor for {{ $def.As }}
|
||||
func {{ unexport $exprType "GValSelector" }}(res {{ $def.As }}, k string) (interface{}, error) {
|
||||
{{- if hasPtr $def.As }}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
switch k {
|
||||
{{- range $def.Struct }}
|
||||
{{- if .ExprType }}
|
||||
case {{ printf "%q" .Name }}{{ if .Alias }}, {{ printf "%q" .Alias }}{{ end }}:
|
||||
return res.{{ export .Name }}, nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)}
|
||||
|
||||
// {{ unexport $exprType "TypedValueSelector" }} is field accessor for {{ $def.As }}
|
||||
func {{ unexport $exprType "TypedValueSelector" }}(res {{ $def.As }}, k string) (TypedValue, error) {
|
||||
{{- if hasPtr $def.As }}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
switch k {
|
||||
{{- range $def.Struct }}
|
||||
{{- if .ExprType }}
|
||||
case {{ printf "%q" .Name }}{{ if .Alias }}, {{ printf "%q" .Alias }}{{ end }}:
|
||||
return {{ export "New" .ExprType }}(res.{{ export .Name }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
{{ if $def.BuiltInAssignerFn }}
|
||||
// {{ $def.AssignerFn }} is field value setter for {{ $def.As }}
|
||||
func {{ $def.AssignerFn }}(res {{ $def.As }}, k string, val interface{}) (error) {
|
||||
switch k {
|
||||
{{- range $def.Struct }}
|
||||
case {{ printf "%q" .Name }}{{ if .Alias }}, {{ printf "%q" .Alias }}{{ end }}:
|
||||
{{- if .Readonly }}
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
{{- else }}
|
||||
aux, err := {{ export "CastTo" .ExprType }}(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.{{ export .Name }} = aux
|
||||
return nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }} {{/* if $def.Struct */}}
|
||||
{{ end }} {{/* if not $def.CustomType */}}
|
||||
{{ end }} {{/* types loop */}}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package handlers
|
||||
|
||||
// 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 (
|
||||
"context"
|
||||
"net/http"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/cortezaproject/corteza-server/{{ .App }}/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
{{ export $.Endpoint.Entrypoint }}API interface {
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
{{ export $a.Name }}(context.Context, *request.{{ export $.Endpoint.Entrypoint $a.Name }}) (interface{}, error)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
{{ export .Endpoint.Entrypoint }} struct {
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
{{ export $a.Name }} func(http.ResponseWriter, *http.Request)
|
||||
{{- end }}
|
||||
}
|
||||
)
|
||||
|
||||
func {{ export "New" $.Endpoint.Entrypoint }}(h {{ export $.Endpoint.Entrypoint }}API) *{{ export $.Endpoint.Entrypoint }} {
|
||||
return &{{ export $.Endpoint.Entrypoint }}{
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
{{ export $a.Name }}: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.New{{ export $.Endpoint.Entrypoint $a.Name }}()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.{{ export $a.Name }}(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
func (h {{ export $.Endpoint.Entrypoint }}) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
|
||||
{{- range $a := .Endpoint.Apis }}
|
||||
r.{{ export ( toLower $a.Method ) }}("{{ $.Endpoint.Path }}{{ $a.Path }}", h.{{ export $a.Name }})
|
||||
{{- end }}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package request
|
||||
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
{{- range .Imports }}
|
||||
{{ normalizeImport . }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
{{ export $.Endpoint.Entrypoint $a.Name }} struct {
|
||||
{{- range $p := $a.Params.All }}
|
||||
// {{ export $p.Name }} {{ $p.Origin }} parameter
|
||||
//
|
||||
// {{ $p.Title }}
|
||||
{{ export $p.Name }} {{ $p.Type }} {{ $p.FieldTag }}
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
{{- range $a := $.Endpoint.Apis }}
|
||||
// {{ export "New" $.Endpoint.Entrypoint $a.Name }} request
|
||||
func {{ export "New" $.Endpoint.Entrypoint $a.Name }}() *{{ export $.Endpoint.Entrypoint $a.Name }} {
|
||||
return &{{ export $.Endpoint.Entrypoint $a.Name }}{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r {{ export $.Endpoint.Entrypoint $a.Name }}) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
{{- range $p := $a.Params.All }}
|
||||
{{- if not $p.Sensitive }}
|
||||
"{{ $p.Name }}": r.{{ export $p.Name }},
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
{{- range $p := $a.Params.All }}
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r {{ export $.Endpoint.Entrypoint $a.Name }}) Get{{ export $p.Name }}() {{ $p.Type }} {
|
||||
return r.{{ export $p.Name }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *{{ export $.Endpoint.Entrypoint $a.Name }}) Fill(req *http.Request) (err error) {
|
||||
{{ if $a.Params.Post }}
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{ if $a.Params.Get }}
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
{{ range $p := $a.Params.Get }}
|
||||
{{- if or $p.IsSlice $p.HasExplicitParser }}
|
||||
if val, ok := tmp["{{ $p.Name }}[]"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["{{ $p.Name }}"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
if val, ok := tmp["{{ $p.Name }}"]; ok && len(val) > 0 {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ if $a.Params.Post }}
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
{{ range $p := $a.Params.Post }}
|
||||
{{ if $p.IsUpload }}
|
||||
// Ignoring {{ $p.Name }} as its handled in the POST params section
|
||||
{{- else }}
|
||||
{{- if or $p.HasExplicitParser }}
|
||||
if val, ok := req.MultipartForm.Value["{{ $p.Name }}[]"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := req.MultipartForm.Value["{{ $p.Name }}"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- else if not $p.IsSlice }}
|
||||
if val, ok := req.MultipartForm.Value["{{ $p.Name }}"]; ok && len(val) > 0 {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
{{ range $p := $a.Params.Post }}
|
||||
{{ if $p.IsUpload }}
|
||||
if _, r.{{ export $p.Name }}, err = req.FormFile("{{ $p.Name }}"); err != nil {
|
||||
return fmt.Errorf("error processing uploaded file: %w", err)
|
||||
}
|
||||
{{ else }}
|
||||
{{- if or $p.HasExplicitParser }}
|
||||
if val, ok := req.Form["{{ $p.Name }}[]"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := req.Form["{{ $p.Name }}"]; ok {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- else if or $p.IsSlice }}
|
||||
//if val, ok := req.Form["{{ $p.Name }}[]"]; ok && len(val) > 0 {
|
||||
// r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
{{- else }}
|
||||
if val, ok := req.Form["{{ $p.Name }}"]; ok && len(val) > 0 {
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val[0]" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $a.Params.Path }}
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
{{ range $p := $a.Params.Path }}
|
||||
val = chi.URLParam(req, "{{ $p.Name }}")
|
||||
r.{{ export $p.Name }}, err = {{ $p.Parser "val" }}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,38 @@
|
||||
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 }}
|
||||
|
||||
|
||||
{{ range $name, $set := .Types }}
|
||||
{{ if $set.LabelResourceType }}
|
||||
// SetLabel adds new label to label map
|
||||
func (m *{{ $name }}) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m {{ $name }}) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func ({{ $name }}) LabelResourceKind() string {
|
||||
return {{ printf "%q" $set.LabelResourceType }}
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (m {{ $name }}) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,89 @@
|
||||
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 $i, $import := .Imports }}
|
||||
"{{ $import }}"
|
||||
{{ end }}
|
||||
)
|
||||
{{ end }}
|
||||
|
||||
type (
|
||||
{{ range $name, $set := .Types }}
|
||||
// {{ $name }}Set slice of {{ $name }}
|
||||
//
|
||||
// This type is auto-generated.
|
||||
{{ $name }}Set []*{{ $name }}
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
{{ range $name, $set := .Types }}
|
||||
// Walk iterates through every slice item and calls w({{ $name }}) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set {{ $name }}Set) Walk(w func(*{{ $name }}) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f({{ $name }}) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set {{ $name }}Set) Filter(f func(*{{ $name }}) (bool, error)) (out {{ $name }}Set, err error) {
|
||||
var ok bool
|
||||
out = {{ $name }}Set{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
{{ if not $set.NoIdField }}
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set {{ $name }}Set) FindByID(ID uint64) *{{ $name }} {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set {{ $name }}Set) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ end }}
|
||||
@@ -0,0 +1,116 @@
|
||||
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 (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
|
||||
{{ range $i, $import := .Imports }}
|
||||
"{{ $import }}"
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
|
||||
{{ range $name, $set := .Types }}
|
||||
|
||||
func Test{{ $name }}SetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make({{ $name }}Set, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*{{ $name }}) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*{{ $name }}) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func Test{{ $name }}SetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make({{ $name }}Set, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*{{ $name }}) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*{{ $name }}) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*{{ $name }}) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{ if not $set.NoIdField }}
|
||||
func Test{{ $name }}SetIDs(t *testing.T) {
|
||||
var (
|
||||
value = make({{ $name }}Set, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// construct objects
|
||||
value[0] = new({{ $name }})
|
||||
value[1] = new({{ $name }})
|
||||
value[2] = new({{ $name }})
|
||||
// set ids
|
||||
value[0].ID = 1
|
||||
value[1].ID = 2
|
||||
value[2].ID = 3
|
||||
|
||||
// Find existing
|
||||
{
|
||||
val := value.FindByID(2)
|
||||
req.Equal(uint64(2), val.ID)
|
||||
}
|
||||
|
||||
// Find non-existing
|
||||
{
|
||||
val := value.FindByID(4)
|
||||
req.Nil(val)
|
||||
}
|
||||
|
||||
// List IDs from set
|
||||
{
|
||||
val := value.IDs()
|
||||
req.Equal(len(val), len(value))
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
Generated
+304
@@ -0,0 +1,304 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func Proc() {
|
||||
const (
|
||||
docGenBase = "/generated/partials"
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
|
||||
watchChanges bool
|
||||
beVerbose bool
|
||||
docPath string
|
||||
|
||||
genCode = true
|
||||
genDocs = false
|
||||
|
||||
fileList []string
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
templatesPath = filepath.Join("pkg", "codegen", "assets", "*.tpl")
|
||||
templatesSrc []string
|
||||
|
||||
actionSrcPath = filepath.Join("*", "service", "*_actions.yaml")
|
||||
actionSrc []string
|
||||
actionDefs []*actionsDef
|
||||
|
||||
eventSrcPath = filepath.Join("*", "service", "event", "events.yaml")
|
||||
eventSrc []string
|
||||
eventDefs []*eventsDef
|
||||
|
||||
// workaround because
|
||||
// filepath.Join merges "*","*" into "**" instead of "*/*"
|
||||
pkgTypeSrcPath = filepath.Join("*"+string(filepath.Separator)+"*"+string(filepath.Separator)+"*", "types.yaml")
|
||||
typeSrcPath = filepath.Join("*"+string(filepath.Separator)+"*", "types.yaml")
|
||||
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
|
||||
|
||||
aFuncsSrcPath = filepath.Join("*", "automation", "*_handler.yaml")
|
||||
aFuncsSrc []string
|
||||
aFuncsDefs []*aFuncDefs
|
||||
|
||||
tpls *template.Template
|
||||
tplBase = template.New("").
|
||||
Funcs(map[string]interface{}{
|
||||
"camelCase": camelCase,
|
||||
"kebabCase": kebabCase,
|
||||
"export": export,
|
||||
"unexport": unexport,
|
||||
"removePtr": removePtr,
|
||||
"hasPtr": hasPtr,
|
||||
"toggleExport": toggleExport,
|
||||
"toLower": strings.ToLower,
|
||||
"toUpper": strings.ToUpper,
|
||||
"cc2underscore": cc2underscore,
|
||||
"normalizeImport": normalizeImport,
|
||||
"comment": func(text string, skip1st bool) string {
|
||||
ll := strings.Split(text, "\n")
|
||||
s := 0
|
||||
out := ""
|
||||
if skip1st {
|
||||
s = 1
|
||||
out = ll[0] + "\n"
|
||||
}
|
||||
|
||||
for ; s < len(ll); s++ {
|
||||
out += "// " + ll[s] + "\n"
|
||||
}
|
||||
|
||||
return out
|
||||
},
|
||||
}).
|
||||
Funcs(sprig.TxtFuncMap())
|
||||
|
||||
output = func(format string, aa ...interface{}) {
|
||||
if beVerbose {
|
||||
fmt.Fprintf(os.Stdout, format, aa...)
|
||||
}
|
||||
}
|
||||
|
||||
outputErr = func(err error, format string, aa ...interface{}) bool {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stdout, format, aa...)
|
||||
fmt.Fprintf(os.Stdout, "%v\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
flag.BoolVar(&watchChanges, "w", false, "regenerate on change of template or definition files")
|
||||
flag.BoolVar(&beVerbose, "v", false, "output loaded definitions, templates and outputs")
|
||||
flag.StringVar(&docPath, "d", "", "generate docs on template or definition change")
|
||||
flag.Parse()
|
||||
|
||||
defer func() {
|
||||
if watcher != nil {
|
||||
watcher.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if len(docPath) > 0 {
|
||||
docPath = strings.TrimRight(docPath, "/") + "/src/modules"
|
||||
if i, err := os.Stat(docPath); err != nil {
|
||||
handleError(err)
|
||||
} else if !i.IsDir() {
|
||||
handleError(fmt.Errorf("expecting directory: %q", docPath))
|
||||
}
|
||||
|
||||
genDocs = true
|
||||
}
|
||||
|
||||
for {
|
||||
fileList = make([]string, 0, 100)
|
||||
|
||||
templatesSrc = glob(templatesPath)
|
||||
output("loaded %d templates from %s\n", len(templatesSrc), templatesPath)
|
||||
|
||||
actionSrc = glob(actionSrcPath)
|
||||
output("loaded %d action definitions from %s\n", len(actionSrc), actionSrcPath)
|
||||
|
||||
eventSrc = glob(eventSrcPath)
|
||||
output("loaded %d event definitions from %s\n", len(eventSrc), eventSrcPath)
|
||||
|
||||
typeSrc = glob(typeSrcPath)
|
||||
typeSrc = append(typeSrc, glob(pkgTypeSrcPath)...)
|
||||
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)
|
||||
|
||||
aFuncsSrc = glob(aFuncsSrcPath)
|
||||
output("loaded %d function definitions from %s\n", len(aFuncsSrc), aFuncsSrcPath)
|
||||
|
||||
if watchChanges {
|
||||
if watcher != nil {
|
||||
watcher.Close()
|
||||
}
|
||||
|
||||
watcher, err = fsnotify.NewWatcher()
|
||||
handleError(err)
|
||||
|
||||
fileList = append(fileList, templatesSrc...)
|
||||
fileList = append(fileList, actionSrc...)
|
||||
fileList = append(fileList, eventSrc...)
|
||||
fileList = append(fileList, typeSrc...)
|
||||
fileList = append(fileList, exprTypeSrc...)
|
||||
fileList = append(fileList, restSrc...)
|
||||
fileList = append(fileList, aFuncsSrc...)
|
||||
|
||||
for _, d := range fileList {
|
||||
handleError(watcher.Add(d))
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
tpls, err = tplBase.ParseFiles(templatesSrc...)
|
||||
if outputErr(err, "could not parse templates:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if actionDefs, err = procActions(actionSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genActions(tpls, actionDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process actions:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if exprTypeDefs, err = procExprTypes(exprTypeSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genExprTypes(tpls, exprTypeDefs...)
|
||||
}
|
||||
if genDocs && err == nil {
|
||||
err = genExprTypeDocs(tpls, docPath+docGenBase, exprTypeDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process expr types:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if eventDefs, err = procEvents(eventSrc...); err == nil {
|
||||
if genCode {
|
||||
expandEventTypes(eventDefs, exprTypeDefs)
|
||||
err = genEvents(tpls, eventDefs...)
|
||||
}
|
||||
if genDocs && err == nil {
|
||||
err = genEventsDocs(tpls, docPath+docGenBase, eventDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process events:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if typeDefs, err = procTypes(typeSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genTypes(tpls, typeDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process types:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if restDefs, err = procRest(restSrc...); err == nil {
|
||||
if genCode {
|
||||
err = genRest(tpls, restDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process rest:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process store:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if outputErr(err, "fail to process options:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
if aFuncsDefs, err = procAutomationFunctions(aFuncsSrc...); err == nil {
|
||||
if genCode {
|
||||
expandAutomationFunctionTypes(aFuncsDefs, exprTypeDefs)
|
||||
|
||||
err = genAutomationFunctions(tpls, aFuncsDefs...)
|
||||
}
|
||||
if genDocs && err == nil {
|
||||
err = genAutomationFunctionDocs(tpls, docPath+docGenBase, aFuncsDefs...)
|
||||
}
|
||||
}
|
||||
|
||||
if outputErr(err, "failed to process automation functions:\n") {
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if !watchChanges {
|
||||
break
|
||||
}
|
||||
|
||||
// @todo fix this (without causing too many "too-many-files" issues :)
|
||||
output("waiting for changes (if you add a new file, restart codegen manually)\n")
|
||||
|
||||
select {
|
||||
case <-watcher.Events:
|
||||
case err = <-watcher.Errors:
|
||||
handleError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func glob(path string) []string {
|
||||
src, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
handleError(fmt.Errorf("failed to glob %q: %w", path, err))
|
||||
}
|
||||
|
||||
return src
|
||||
}
|
||||
|
||||
// Similar to cli.HandleError but without cli pkg dependencies
|
||||
//
|
||||
// pkg/cli deps can give us issues when generating go files with invalid code
|
||||
func handleError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/slice"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
// definitions are in one file
|
||||
eventsDef struct {
|
||||
Package string
|
||||
App string
|
||||
Source string
|
||||
outputDir string
|
||||
|
||||
// List of imports
|
||||
// Used only by generated file and not pre-generated-user-file
|
||||
Imports []string
|
||||
|
||||
Resources evResourceDefMap
|
||||
}
|
||||
|
||||
evResourceDefMap map[string]*evResourceDef
|
||||
|
||||
evResourceDef struct {
|
||||
// used as string
|
||||
ResourceString string
|
||||
|
||||
// used as (go) ident
|
||||
ResourceIdent string
|
||||
|
||||
// used for filename
|
||||
ResourceFile string
|
||||
|
||||
On []string `yaml:"on"`
|
||||
BeforeAfter []string `yaml:"ba"`
|
||||
Properties []*eventProps `yaml:"props"`
|
||||
Constraints []*eventConstraint `yaml:"constraints"`
|
||||
Result string `yaml:"result"`
|
||||
}
|
||||
|
||||
eventProps struct {
|
||||
Name string
|
||||
Type string
|
||||
ExprType 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
|
||||
}
|
||||
|
||||
eventConstraint struct {
|
||||
Name string
|
||||
}
|
||||
)
|
||||
|
||||
func procEvents(mm ...string) (dd []*eventsDef, err error) {
|
||||
// <app>/service/event/events.yaml
|
||||
const (
|
||||
importTypePathTpl = "github.com/cortezaproject/corteza-server/%s/types"
|
||||
importAutoPathTpl = "github.com/cortezaproject/corteza-server/%s/automation"
|
||||
importAuthPath = "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
dd = make([]*eventsDef, 0)
|
||||
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 (
|
||||
e = evResourceDefMap{}
|
||||
d = &eventsDef{
|
||||
Package: "event",
|
||||
Source: m,
|
||||
App: m[:strings.Index(m, "/")],
|
||||
outputDir: path.Dir(m),
|
||||
Resources: map[string]*evResourceDef{},
|
||||
}
|
||||
)
|
||||
|
||||
if err := yaml.NewDecoder(f).Decode(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for resName, evDef := range e {
|
||||
|
||||
d.Imports = []string{
|
||||
fmt.Sprintf(importTypePathTpl, d.App),
|
||||
}
|
||||
|
||||
if d.App != "messaging" {
|
||||
d.Imports = append(d.Imports, fmt.Sprintf(importAutoPathTpl, d.App))
|
||||
}
|
||||
|
||||
evDef.ResourceString = resName
|
||||
|
||||
if l := strings.Index(resName, ":"); l > 0 {
|
||||
evDef.ResourceIdent = resName[l+1:]
|
||||
} else {
|
||||
evDef.ResourceIdent = resName
|
||||
}
|
||||
|
||||
// make filename
|
||||
evDef.ResourceFile = strings.ReplaceAll(evDef.ResourceIdent, ":", "_")
|
||||
evDef.ResourceFile = strings.ReplaceAll(evDef.ResourceFile, "-", "_")
|
||||
|
||||
// make identifier (string that will be used for struct name)
|
||||
evDef.ResourceIdent = camelCase(strings.Split(evDef.ResourceFile, "_")...)
|
||||
|
||||
// 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,
|
||||
})
|
||||
|
||||
// Ensure all imports are checked
|
||||
for _, p := range evDef.Properties {
|
||||
if p.Import == "" {
|
||||
if strings.HasPrefix(p.Type, "*types.") || strings.HasPrefix(p.Type, "types.") {
|
||||
p.Import = fmt.Sprintf(importTypePathTpl, d.App)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Import != "" && !slice.HasString(d.Imports, p.Import) {
|
||||
d.Imports = append(d.Imports, p.Import)
|
||||
}
|
||||
|
||||
p.Import = ""
|
||||
}
|
||||
|
||||
d.Resources[resName] = evDef
|
||||
}
|
||||
|
||||
dd = append(dd, d)
|
||||
}
|
||||
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
func expandEventTypes(ee []*eventsDef, tt []*exprTypesDef) {
|
||||
// index of all known types
|
||||
expTypes := make(map[string]*exprTypeDef)
|
||||
goTypes := make(map[string]string)
|
||||
|
||||
for _, t := range tt {
|
||||
for typ, d := range t.Types {
|
||||
expTypes[typ] = d
|
||||
goTypes[d.As] = typ
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range ee {
|
||||
for _, r := range e.Resources {
|
||||
for _, p := range r.Properties {
|
||||
if p.ExprType != "" && expTypes[p.ExprType] == nil {
|
||||
fmt.Printf("unknown type %q used for param %q for events on resource %s\n", p.ExprType, p.Name, r.ResourceString)
|
||||
}
|
||||
|
||||
if p.ExprType == "" && goTypes[p.Type] != "" {
|
||||
p.ExprType = goTypes[p.Type]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genEvents(tpl *template.Template, dd ...*eventsDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplEvents = tpl.Lookup("events.go.tpl")
|
||||
|
||||
// Always regenerated
|
||||
tplEventsGen = tpl.Lookup("events.gen.go.tpl")
|
||||
|
||||
// List of event-type definitions for automation REST endpoint
|
||||
tplAutomationRestDefGen = tpl.Lookup("events_rest_def.gen.go.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
for _, d := range dd {
|
||||
// Generic code, all events go into one file (per app)
|
||||
err = goTemplate(path.Join(d.outputDir, "events.gen.go"), tplEventsGen, d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, r := range d.Resources {
|
||||
dst = path.Join(d.outputDir, r.ResourceFile+".go")
|
||||
_, err = os.Stat(dst)
|
||||
if os.IsNotExist(err) {
|
||||
err = goTemplate(dst, tplEvents, map[string]interface{}{
|
||||
"Package": d.Package,
|
||||
"ResourceIdent": r.ResourceIdent,
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove messaging
|
||||
var msgIndex = -1
|
||||
for i := range dd {
|
||||
if dd[i].App == "messaging" {
|
||||
msgIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
if msgIndex > -1 {
|
||||
dd = append(dd[:msgIndex], dd[msgIndex+1:]...)
|
||||
}
|
||||
|
||||
err = goTemplate(
|
||||
path.Join("automation", "rest", "eventTypes.gen.go"),
|
||||
tplAutomationRestDefGen,
|
||||
map[string]interface{}{
|
||||
"Definitions": dd,
|
||||
"Imports": collectEventDefImports("", dd...),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge on/before/after events
|
||||
func (def evResourceDef) Events() []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
|
||||
}
|
||||
|
||||
func genEventsDocs(tpl *template.Template, docsPath string, dd ...*eventsDef) (err error) {
|
||||
var (
|
||||
tplEventsAdoc = tpl.Lookup("events.gen.adoc.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
dst = path.Join(docsPath, "resource-events.gen.adoc")
|
||||
return plainTemplate(dst, tplEventsAdoc, map[string]interface{}{
|
||||
"Definitions": dd,
|
||||
})
|
||||
}
|
||||
|
||||
func collectEventDefImports(basePkg string, dd ...*eventsDef) []string {
|
||||
ii := make([]string, 0, len(dd))
|
||||
for _, d := range dd {
|
||||
for _, i := range d.Imports {
|
||||
if !slice.HasString(ii, i) && (basePkg == "" || !strings.HasSuffix(i, basePkg)) {
|
||||
ii = append(ii, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ii
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
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]*exprTypeDef `yaml:"types"`
|
||||
}
|
||||
|
||||
exprTypeDef struct {
|
||||
As string
|
||||
RawDefault string `yaml:"default"`
|
||||
AssignerFn string `yaml:"assignerFn"`
|
||||
BuiltInCastFn bool
|
||||
BuiltInAssignerFn bool
|
||||
CustomType bool `yaml:"customType"`
|
||||
CustomGValSelector bool `yaml:"customGValSelector"`
|
||||
CustomSelector bool `yaml:"customSelector"`
|
||||
CustomFieldAssigner bool `yaml:"customFieldAssigner"`
|
||||
Comparable bool `yaml:"comparable"`
|
||||
CustomComparator bool `yaml:"customComparator"`
|
||||
Struct []*exprTypeStructDef
|
||||
|
||||
// @todo custom setters
|
||||
// @todo custom getters
|
||||
}
|
||||
|
||||
exprTypeStructDef struct {
|
||||
Name string
|
||||
Alias string
|
||||
ExprType string `yaml:"exprType"`
|
||||
GoType string `yaml:"goType"`
|
||||
Mode string
|
||||
|
||||
// @todo custom expr-type-constructor NewExprType
|
||||
}
|
||||
)
|
||||
|
||||
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]*exprTypeDef),
|
||||
}
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
for tName, tdef := range d.Types {
|
||||
if tdef.AssignerFn == "" {
|
||||
tdef.BuiltInAssignerFn = true
|
||||
tdef.AssignerFn = unexport("assignTo", tName)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// genExprTypeDocs look for expr_types.gen.adoc.tpl and generates expr_types.gen.adoc from it
|
||||
func genExprTypeDocs(tpl *template.Template, docsPath string, dd ...*exprTypesDef) (err error) {
|
||||
var (
|
||||
typeGenAdoc = tpl.Lookup("expr_types.gen.adoc.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
dst = path.Join(docsPath, "expr-types.gen.adoc")
|
||||
return plainTemplate(dst, typeGenAdoc, map[string]interface{}{
|
||||
"Definitions": dd,
|
||||
})
|
||||
}
|
||||
|
||||
func (s exprTypeDef) Default() string {
|
||||
if s.RawDefault == "" {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
return s.RawDefault
|
||||
}
|
||||
|
||||
func (s exprTypeStructDef) Readonly() bool {
|
||||
return s.Mode == "ro"
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
// The following structure represents
|
||||
// legacy API definition (spec.json)
|
||||
//
|
||||
//
|
||||
|
||||
// definitions are in one file
|
||||
restDef struct {
|
||||
App string
|
||||
Source string
|
||||
outputDir string
|
||||
|
||||
Endpoints []*restEndpointDef
|
||||
}
|
||||
|
||||
restEndpointDef struct {
|
||||
Title string `yaml:"title"`
|
||||
Path string `yaml:"path"`
|
||||
Entrypoint string `yaml:"entrypoint"`
|
||||
Authentication []interface{} `yaml:"authentication,omitempty"`
|
||||
Apis []*restEndpointApi `yaml:"apis"`
|
||||
Imports []string `yaml:"imports"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Params restEndpointParamsDef `yaml:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
restEndpointApi struct {
|
||||
Name string `yaml:"name"`
|
||||
Method string `yaml:"method"`
|
||||
Title string `yaml:"title"`
|
||||
Path string `yaml:"path"`
|
||||
Params restEndpointParamsDef `yaml:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
restEndpointParamsDef struct {
|
||||
Post []*restEndpointParamDef `yaml:"post"`
|
||||
Path []*restEndpointParamDef `yaml:"path"`
|
||||
Get []*restEndpointParamDef `yaml:"get"`
|
||||
}
|
||||
|
||||
restEndpointParamDef struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Required bool `yaml:"required"`
|
||||
Title string `yaml:"title"`
|
||||
Origin string
|
||||
|
||||
Sensitive bool `yaml:"sensitive"`
|
||||
|
||||
DefinedParser string `yaml:"parser"`
|
||||
}
|
||||
)
|
||||
|
||||
func procRest(mm ...string) (dd []*restDef, err error) {
|
||||
dd = make([]*restDef, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
err = func() error {
|
||||
f, err := os.Open(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s read failed: %w", m, err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var d = &restDef{}
|
||||
|
||||
if err := yaml.NewDecoder(f).Decode(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.outputDir = path.Dir(m)
|
||||
|
||||
// Append params from endpoit to all apis
|
||||
for _, e := range d.Endpoints {
|
||||
for _, a := range e.Apis {
|
||||
a.Params.Path = append(e.Params.Path, a.Params.Path...)
|
||||
a.Params.Post = append(e.Params.Post, a.Params.Post...)
|
||||
a.Params.Get = append(e.Params.Get, a.Params.Get...)
|
||||
}
|
||||
}
|
||||
|
||||
dd = append(dd, d)
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to process %s: %w", m, err)
|
||||
}
|
||||
}
|
||||
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
func genRest(tpl *template.Template, dd ...*restDef) (err error) {
|
||||
var (
|
||||
// Will only be generated if file does not exist previously
|
||||
tplHandler = tpl.Lookup("rest_handler.go.tpl")
|
||||
tplRequest = tpl.Lookup("rest_request.go.tpl")
|
||||
|
||||
dst string
|
||||
)
|
||||
|
||||
for _, d := range dd {
|
||||
for _, e := range d.Endpoints {
|
||||
|
||||
// Generic code, every event goes into one file (per app)
|
||||
dst = path.Join(d.outputDir, "rest", "handlers", e.Entrypoint+".go")
|
||||
err = goTemplate(dst, tplHandler, map[string]interface{}{
|
||||
"Source": d.Source,
|
||||
"Endpoint": e,
|
||||
"App": path.Base(d.outputDir),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Generic code, every event goes into one file (per app)
|
||||
dst = path.Join(d.outputDir, "rest", "request", e.Entrypoint+".go")
|
||||
err = goTemplate(dst, tplRequest, map[string]interface{}{
|
||||
"Source": d.Source,
|
||||
"Endpoint": e,
|
||||
"Imports": e.Imports,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *restEndpointParamsDef) All() []*restEndpointParamDef {
|
||||
var pp = make([]*restEndpointParamDef, 0)
|
||||
|
||||
for _, p := range d.Path {
|
||||
p.Origin = "PATH"
|
||||
pp = append(pp, p)
|
||||
}
|
||||
|
||||
for _, p := range d.Get {
|
||||
p.Origin = "GET"
|
||||
pp = append(pp, p)
|
||||
}
|
||||
|
||||
for _, p := range d.Post {
|
||||
p.Origin = "POST"
|
||||
pp = append(pp, p)
|
||||
}
|
||||
|
||||
return pp
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) IsUpload() bool {
|
||||
switch d.Type {
|
||||
case "*multipart.FileHeader":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) IsSlice() bool {
|
||||
return strings.HasPrefix(d.Type, "[]") || strings.HasSuffix(d.Type, "Set")
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) IsString() bool {
|
||||
switch d.Type {
|
||||
case "string", "[]string", "[]*string":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) FieldTag() string {
|
||||
switch d.Type {
|
||||
case "uint64":
|
||||
return "`json:\",string\"`"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) HasExplicitParser() bool {
|
||||
return d.DefinedParser != ""
|
||||
}
|
||||
|
||||
func (d *restEndpointParamDef) Parser(arg string) string {
|
||||
if d.HasExplicitParser() {
|
||||
return fmt.Sprintf("%s(%s)", d.DefinedParser, arg)
|
||||
}
|
||||
|
||||
switch d.Type {
|
||||
case "[]uint64":
|
||||
return fmt.Sprintf("payload.ParseUint64s(%s), nil", arg)
|
||||
case "[]uint":
|
||||
return fmt.Sprintf("payload.ParseUints(%s), nil", arg)
|
||||
case "time.Time":
|
||||
return fmt.Sprintf("payload.ParseISODateWithErr(%s)", arg)
|
||||
case "*time.Time":
|
||||
return fmt.Sprintf("payload.ParseISODatePtrWithErr(%s)", arg)
|
||||
case "sqlxTypes.JSONText":
|
||||
return fmt.Sprintf("payload.ParseJSONTextWithErr(%s)", arg)
|
||||
case "int", "uint", "uint64", "int64", "float", "float64", "bool":
|
||||
return fmt.Sprintf("payload.Parse%s(%s), nil", export(d.Type), arg)
|
||||
case "string", "[]string":
|
||||
return fmt.Sprintf("%s, nil", arg)
|
||||
case "filter.State":
|
||||
return fmt.Sprintf("payload.ParseFilterState(%s), nil", arg)
|
||||
default:
|
||||
return fmt.Sprintf("%s(%s), nil", d.Type, arg)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func goTemplate(dst string, tpl *template.Template, payload interface{}) (err error) {
|
||||
var output io.WriteCloser
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
if err := tpl.Execute(&buf, payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmtsrc, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s fmt warn: %v\n", dst, err)
|
||||
|
||||
err = nil
|
||||
fmtsrc = buf.Bytes()
|
||||
}
|
||||
|
||||
if dst == "" || dst == "-" {
|
||||
output = os.Stdout
|
||||
} else {
|
||||
if output, err = os.Create(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer output.Close()
|
||||
}
|
||||
|
||||
if _, err := output.Write(fmtsrc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func plainTemplate(dst string, tpl *template.Template, payload interface{}) (err error) {
|
||||
var output io.WriteCloser
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
if err := tpl.Execute(&buf, payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dst == "" || dst == "-" {
|
||||
output = os.Stdout
|
||||
} else {
|
||||
if output, err = os.Create(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer output.Close()
|
||||
}
|
||||
|
||||
if _, err := output.Write(buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func camelCase(pp ...string) (out string) {
|
||||
for i, p := range pp {
|
||||
if i > 0 && len(p) > 1 {
|
||||
p = strings.ToUpper(p[:1]) + p[1:]
|
||||
}
|
||||
|
||||
out = out + p
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func kebabCase(pp string) (out string) {
|
||||
var buf bytes.Buffer
|
||||
for _, p := range pp {
|
||||
if 'A' <= p && p <= 'Z' {
|
||||
// just convert [A-Z] to _[a-z]
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteRune('-')
|
||||
}
|
||||
buf.WriteRune(p - 'A' + 'a')
|
||||
} else {
|
||||
buf.WriteRune(p)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// PubIdent returns published identifier by uppercasing
|
||||
// input, cammelcasing it and removing ident unfriendly characters
|
||||
var nonIdentChars = regexp.MustCompile(`[\s\\/]+`)
|
||||
|
||||
func export(pp ...string) (out string) {
|
||||
for _, p := range pp {
|
||||
if len(p) > 1 {
|
||||
p = strings.ToUpper(p[:1]) + p[1:]
|
||||
} else {
|
||||
p = strings.ToUpper(p)
|
||||
}
|
||||
|
||||
if ss := nonIdentChars.Split(p, -1); len(ss) > 1 {
|
||||
p = export(ss...)
|
||||
}
|
||||
|
||||
out = out + p
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func unexport(pp ...string) (out string) {
|
||||
out = export(pp...)
|
||||
return strings.ToLower(out[:1]) + out[1:]
|
||||
}
|
||||
|
||||
func removePtr(name string) string {
|
||||
return strings.TrimLeft(name, "*")
|
||||
}
|
||||
|
||||
func hasPtr(name string) bool {
|
||||
return len(name) > 0 && name[0:1] == "*"
|
||||
}
|
||||
|
||||
func toggleExport(e bool, pp ...string) (out string) {
|
||||
if e {
|
||||
return export(pp...)
|
||||
}
|
||||
|
||||
return unexport(pp...)
|
||||
}
|
||||
|
||||
// convets to underscore
|
||||
func cc2underscore(cc string) string {
|
||||
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
||||
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
||||
u := matchFirstCap.ReplaceAllString(cc, "${1}_${2}")
|
||||
u = matchAllCap.ReplaceAllString(u, "${1}_${2}")
|
||||
return strings.ToLower(u)
|
||||
}
|
||||
|
||||
// Handle list of imports, adds quotes around each import
|
||||
//
|
||||
// If import string contains a space, assume import alias and
|
||||
// quotes only the 2nd part
|
||||
func normalizeImport(i string) string {
|
||||
if strings.Contains(i, " ") {
|
||||
p := strings.SplitN(i, " ", 2)
|
||||
return fmt.Sprintf(`%s "%s"`, p[0], strings.Trim(p[1], `"`))
|
||||
} else {
|
||||
return fmt.Sprintf(`"%s"`, strings.Trim(i, `"`))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type (
|
||||
typesDef struct {
|
||||
// source file path
|
||||
Source string
|
||||
|
||||
// outputDir
|
||||
// dir where the source file is
|
||||
outputDir string
|
||||
|
||||
Imports []string
|
||||
Package string `yaml:"package"`
|
||||
Types map[string]typeDef `yaml:"types"`
|
||||
}
|
||||
|
||||
typeDef struct {
|
||||
NoIdField bool `yaml:"noIdField"`
|
||||
LabelResourceType string `yaml:"labelResourceType"`
|
||||
}
|
||||
)
|
||||
|
||||
func procTypes(mm ...string) (dd []*typesDef, err error) {
|
||||
dd = make([]*typesDef, 0)
|
||||
|
||||
for _, m := range mm {
|
||||
var (
|
||||
d = &typesDef{
|
||||
Source: m,
|
||||
outputDir: path.Dir(m),
|
||||
|
||||
Package: "types",
|
||||
Types: map[string]typeDef{},
|
||||
}
|
||||
)
|
||||
|
||||
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 genTypes(tpl *template.Template, dd ...*typesDef) (err error) {
|
||||
var (
|
||||
typeGen = tpl.Lookup("type_set.gen.go.tpl")
|
||||
typeGenTest = tpl.Lookup("type_set.gen_test.go.tpl")
|
||||
|
||||
typeLabelsGen = tpl.Lookup("type_labels.gen.go.tpl")
|
||||
)
|
||||
|
||||
for _, d := range dd {
|
||||
err = goTemplate(path.Join(d.outputDir, "type_set.gen.go"), typeGen, d)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = goTemplate(path.Join(d.outputDir, "type_set.gen_test.go"), typeGenTest, d)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
labelsOutput := path.Join(d.outputDir, "type_labels.gen.go")
|
||||
if d.HasLabels() {
|
||||
err = goTemplate(labelsOutput, typeLabelsGen, d)
|
||||
} else if err = syscall.Unlink(labelsOutput); os.IsNotExist(err) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d typesDef) HasLabels() bool {
|
||||
for _, t := range d.Types {
|
||||
if len(t.LabelResourceType) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user