snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
297
server/pkg/codegen/actions.go
Normal file
297
server/pkg/codegen/actions.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user