Defer report filter validation to the runtime
This commit is contained in:
+27
-7
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Generated
+36
@@ -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
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user