3
0

Defer report filter validation to the runtime

This commit is contained in:
Tomaž Jerman
2021-10-08 10:13:39 +02:00
parent 7a96376865
commit b352bec657
6 changed files with 106 additions and 14 deletions
+27 -7
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/cortezaproject/corteza-server/pkg/qlng"
@@ -78,6 +79,7 @@ type (
// Filter is a qlng.ASTNode wrapper to get some unmarshal/marshal features
Filter struct {
*qlng.ASTNode
Error string `json:"error,omitempty"`
}
)
@@ -91,6 +93,7 @@ func (f *Filter) Clone() *Filter {
return &Filter{
ASTNode: f.ASTNode.Clone(),
Error: f.Error,
}
}
@@ -130,11 +133,12 @@ func (f *Filter) UnmarshalJSON(data []byte) (err error) {
return
}
if f.ASTNode, err = p.Parse(v); err != nil {
return
}
f.ASTNode, err = p.Parse(v)
f.ASTNode.Raw = v
return
if err != nil {
f.Error = err.Error()
}
return nil
}
// special case for empty JSON
@@ -144,16 +148,17 @@ func (f *Filter) UnmarshalJSON(data []byte) (err error) {
// non-string is considered an AST and we parse that
if err = json.Unmarshal(data, &f.ASTNode); err != nil {
return
f.Error = err.Error()
return nil
}
// traverse the AST to parse any raw exprs.
if f.ASTNode == nil {
return
return nil
}
// A raw expression takes priority and replaces the original AST sub-tree
return f.ASTNode.Traverse(func(n *qlng.ASTNode) (bool, *qlng.ASTNode, error) {
err = f.ASTNode.Traverse(func(n *qlng.ASTNode) (bool, *qlng.ASTNode, error) {
if n.Raw == "" {
return true, n, nil
}
@@ -166,6 +171,14 @@ func (f *Filter) UnmarshalJSON(data []byte) (err error) {
return false, aux, nil
})
if err != nil {
f.Error = err.Error()
} else {
f.Error = ""
}
return nil
}
// With guard element
@@ -418,6 +431,13 @@ func (f *FrameDefinition) Clone() (out *FrameDefinition) {
}
}
func (d FrameDefinition) Validate() error {
if d.Filter != nil && d.Filter.Error != "" {
return errors.InvalidData(d.Filter.Error)
}
return nil
}
func (dd FrameDefinitionSet) Find(name string) *FrameDefinition {
for _, d := range dd {
if d.Name == name {
+23 -5
View File
@@ -2,9 +2,9 @@ package report
import (
"context"
"errors"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/filter"
)
@@ -57,7 +57,7 @@ func Model(ctx context.Context, sources map[string]DatasourceProvider, dd ...*St
switch {
case d.Load != nil:
if sources == nil {
return errors.New("no datasource providers defined")
return fmt.Errorf("no datasource providers defined")
}
steps = append(steps, &stepLoad{def: d.Load, dsp: sources[d.Load.Source]})
@@ -70,7 +70,7 @@ func Model(ctx context.Context, sources map[string]DatasourceProvider, dd ...*St
// @todo Transform
default:
return errors.New("malformed step definition: unsupported step kind")
return fmt.Errorf("malformed step definition: unsupported step kind")
}
}
return nil
@@ -85,6 +85,24 @@ func Model(ctx context.Context, sources map[string]DatasourceProvider, dd ...*St
}, nil
}
func (ss StepDefinitionSet) Validate() error {
var f *Filter
for _, s := range ss {
switch {
case s.Load != nil:
f = s.Load.Filter
case s.Join != nil:
f = s.Join.Filter
case s.Group != nil:
f = s.Group.Filter
}
if f != nil && f.Error != "" {
return errors.InvalidData(f.Error)
}
}
return nil
}
// Add adds additional steps to the model
func (m *model) Add(ss ...step) M {
m.steps = append(m.steps, ss...)
@@ -101,11 +119,11 @@ func (m *model) Run(ctx context.Context) (err error) {
// initial validation
err = func() (err error) {
if m.ran {
return errors.New("model already ran")
return fmt.Errorf("model already ran")
}
if len(m.steps) == 0 {
return errors.New("no model steps defined")
return fmt.Errorf("no model steps defined")
}
return nil
+2 -2
View File
@@ -408,9 +408,9 @@ func (d *joinedDataset) strategizePaging(local, foreign *FrameDefinition, invert
// local otherwise.
switch pp[0].ref {
case local.Ref, "":
local.Filter = merger(&Filter{pp[0].filterCut}, local.Filter, "and")
local.Filter = merger(&Filter{pp[0].filterCut, ""}, local.Filter, "and")
case foreign.Ref:
foreign.Filter = merger(&Filter{pp[0].filterCut}, foreign.Filter, "and")
foreign.Filter = merger(&Filter{pp[0].filterCut, ""}, foreign.Filter, "and")
}
return
+14
View File
@@ -293,6 +293,10 @@ func (svc *report) DescribeFresh(ctx context.Context, src types.ReportDataSource
out = make(rep.FrameDescriptionSet, 0, len(sources)*2)
err = func() (err error) {
if !svc.ac.CanCreateReport(ctx) {
return ReportErrNotAllowedToCreate()
}
ss := src.ModelSteps()
ss = append(ss, st...)
@@ -352,6 +356,16 @@ func (svc *report) Run(ctx context.Context, reportID uint64, dd rep.FrameDefinit
ss := r.Sources.ModelSteps()
ss = append(ss, r.Blocks.ModelSteps()...)
if err = ss.Validate(); err != nil {
return ReportErrInvalidConfiguration().Wrap(err)
}
for _, d := range dd {
if err = d.Validate(); err != nil {
return ReportErrInvalidConfiguration().Wrap(err)
}
}
// Model the report
model, err := rep.Model(ctx, reporters, ss...)
if err != nil {
+36
View File
@@ -779,6 +779,42 @@ func ReportErrNotAllowedToRun(mm ...*reportActionProps) *errors.Error {
return e
}
// ReportErrInvalidConfiguration returns "system:report.invalidConfiguration" as *errors.Error
//
//
// This function is auto-generated.
//
func ReportErrInvalidConfiguration(mm ...*reportActionProps) *errors.Error {
var p = &reportActionProps{}
if len(mm) > 0 {
p = mm[0]
}
var e = errors.New(
errors.KindInternal,
p.Format("invalid report configuration", nil),
errors.Meta("type", "invalidConfiguration"),
errors.Meta("resource", "system:report"),
// action log entry; no formatting, it will be applied inside recordAction fn.
errors.Meta(reportLogMetaKey{}, "failed to run {{report}}; invalid configuration"),
errors.Meta(reportPropsMetaKey{}, p),
// translation namespace & key
errors.Meta(locale.ErrorMetaNamespace{}, "system"),
errors.Meta(locale.ErrorMetaKey{}, "report.errors.invalidConfiguration"),
errors.StackSkip(1),
)
if len(mm) > 0 {
}
return e
}
// *********************************************************************************************************************
// *********************************************************************************************************************
+4
View File
@@ -90,3 +90,7 @@ errors:
- error: notAllowedToRun
message: "not allowed to run this report"
log: "failed to run {{report}}; insufficient permissions"
- error: invalidConfiguration
message: "invalid report configuration"
log: "failed to run {{report}}; invalid configuration"