From db5e2f396889a369b423bfd1a267a39c58f095ac Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 13 Dec 2022 22:08:44 +0800 Subject: [PATCH] [add] widget chart permission support --- widgets/chart/chart.go | 48 +++++-------- widgets/chart/fields.go | 28 +++++++- widgets/chart/layout.go | 75 ++++++++++++++++++--- widgets/chart/{compute.go => mapping.go} | 85 +++++++++++++++++++++++- widgets/chart/process.go | 5 +- widgets/chart/process_test.go | 36 ++++++++++ widgets/chart/types.go | 2 + 7 files changed, 234 insertions(+), 45 deletions(-) rename widgets/chart/{compute.go => mapping.go} (51%) diff --git a/widgets/chart/chart.go b/widgets/chart/chart.go index 35dd3f55..ecc3f980 100644 --- a/widgets/chart/chart.go +++ b/widgets/chart/chart.go @@ -86,10 +86,10 @@ func LoadFrom(dir string, prefix string) error { dsl.Layout = &LayoutDSL{} } - // Parse - err = dsl.Parse() + // mapping + err = dsl.mapping() if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) + messages = append(messages, fmt.Sprintf("[%s] Mapping %s", id, err.Error())) return } @@ -138,39 +138,15 @@ func MustGet(chart interface{}) *DSL { return t } -// Parse Layout -func (dsl *DSL) Parse() error { - - // ComputeFields - err := dsl.computeMapping() - if err != nil { - return err - } - - // Filters - err = dsl.Fields.Filter.CPropsMerge(dsl.CProps, func(name string, filter field.FilterDSL) (xpath string) { - return fmt.Sprintf("fields.filter.%s.edit.props", name) - }) - - if err != nil { - return err - } - - // Columns - return dsl.Fields.Chart.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) { - return fmt.Sprintf("fields.chart.%s.%s.props", name, kind) - }) -} - // Xgen trans to xgen setting -func (dsl *DSL) Xgen() (map[string]interface{}, error) { +func (dsl *DSL) Xgen(data map[string]interface{}, excludes map[string]bool) (map[string]interface{}, error) { - setting, err := dsl.Layout.Xgen() + layout, err := dsl.Layout.Xgen(data, excludes, dsl.Mapping) if err != nil { return nil, err } - fields, err := dsl.Fields.Xgen() + fields, err := dsl.Fields.Xgen(layout) if err != nil { return nil, err } @@ -180,6 +156,17 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { dsl.Config["full"] = true } + setting := map[string]interface{}{} + bytes, err := jsoniter.Marshal(layout) + if err != nil { + return nil, err + } + + err = jsoniter.Unmarshal(bytes, &setting) + if err != nil { + return nil, err + } + setting["fields"] = fields setting["config"] = dsl.Config for _, cProp := range dsl.CProps { @@ -189,7 +176,6 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { "params": cProp.Query, } }) - if err != nil { return nil, err } diff --git a/widgets/chart/fields.go b/widgets/chart/fields.go index a5952978..923946be 100644 --- a/widgets/chart/fields.go +++ b/widgets/chart/fields.go @@ -1,11 +1,33 @@ package chart -import jsoniter "github.com/json-iterator/go" +import ( + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/yao/widgets/field" +) // Xgen trans to xgen setting -func (fields *FieldsDSL) Xgen() (map[string]interface{}, error) { +func (fields *FieldsDSL) Xgen(layout *LayoutDSL) (map[string]interface{}, error) { res := map[string]interface{}{} - data, err := jsoniter.Marshal(fields) + + filters := map[string]field.FilterDSL{} + if layout.Filter != nil && layout.Filter.Columns != nil { + for _, inst := range layout.Filter.Columns { + if c, has := fields.Filter[inst.Name]; has { + filters[inst.Name] = c + } + } + } + + columns := map[string]field.ColumnDSL{} + if layout.Chart != nil && layout.Chart.Columns != nil { + for _, inst := range layout.Chart.Columns { + if c, has := fields.Chart[inst.Name]; has { + columns[inst.Name] = c + } + } + } + + data, err := jsoniter.Marshal(map[string]interface{}{"filter": filters, "chart": columns}) if err != nil { return nil, err } diff --git a/widgets/chart/layout.go b/widgets/chart/layout.go index b4197b49..627764f9 100644 --- a/widgets/chart/layout.go +++ b/widgets/chart/layout.go @@ -1,19 +1,78 @@ package chart -import jsoniter "github.com/json-iterator/go" +import ( + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/mapping" +) // Xgen trans to Xgen setting -func (layout *LayoutDSL) Xgen() (map[string]interface{}, error) { - res := map[string]interface{}{} - data, err := jsoniter.Marshal(layout) +func (layout *LayoutDSL) Xgen(data map[string]interface{}, excludes map[string]bool, mapping *mapping.Mapping) (*LayoutDSL, error) { + clone, err := layout.Clone() if err != nil { return nil, err } - err = jsoniter.Unmarshal(data, &res) - if err != nil { - return nil, err + // Filter + if clone.Filter != nil { + if clone.Filter.Actions != nil { + clone.Filter.Actions = clone.Filter.Actions.Filter(excludes) + } + + if clone.Filter.Columns != nil { + columns := []component.InstanceDSL{} + for _, column := range clone.Filter.Columns { + id, has := mapping.Filters[column.Name] + if !has { + continue + } + + if _, has := excludes[id]; has { + continue + } + + columns = append(columns, column) + } + clone.Filter.Columns = columns + } } - return res, nil + // Operations + if clone.Operation != nil && clone.Operation.Actions != nil { + clone.Operation.Actions = clone.Operation.Actions.Filter(excludes) + } + + // Columns + if clone.Chart != nil && clone.Chart.Columns != nil { + columns := []component.InstanceDSL{} + for _, column := range clone.Chart.Columns { + id, has := mapping.Columns[column.Name] + if !has { + continue + } + + if _, has := excludes[id]; has { + continue + } + + columns = append(columns, column) + } + clone.Chart.Columns = columns + } + + return clone, nil +} + +// Clone layout for output +func (layout *LayoutDSL) Clone() (*LayoutDSL, error) { + new := LayoutDSL{} + bytes, err := jsoniter.Marshal(layout) + if err != nil { + return nil, err + } + err = jsoniter.Unmarshal(bytes, &new) + if err != nil { + return nil, err + } + return &new, nil } diff --git a/widgets/chart/compute.go b/widgets/chart/mapping.go similarity index 51% rename from widgets/chart/compute.go rename to widgets/chart/mapping.go index 0d4001a4..6eb2ce71 100644 --- a/widgets/chart/compute.go +++ b/widgets/chart/mapping.go @@ -5,6 +5,7 @@ import ( "github.com/yaoapp/yao/widgets/compute" "github.com/yaoapp/yao/widgets/field" + "github.com/yaoapp/yao/widgets/mapping" ) func (dsl *DSL) getField() func(string) (*field.ColumnDSL, string, string, error) { @@ -27,7 +28,8 @@ func (dsl *DSL) getFilter() func(string) (*field.FilterDSL, string, string, erro } } -func (dsl *DSL) computeMapping() error { +func (dsl *DSL) mapping() error { + if dsl.Computes == nil { dsl.Computes = &compute.Maps{ Filter: map[string][]compute.Unit{}, @@ -36,14 +38,32 @@ func (dsl *DSL) computeMapping() error { } } + if dsl.Mapping == nil { + dsl.Mapping = &mapping.Mapping{} + } + + if dsl.Mapping.Filters == nil { + dsl.Mapping.Filters = map[string]string{} + } + + if dsl.Mapping.Columns == nil { + dsl.Mapping.Columns = map[string]string{} + } + if dsl.Fields == nil { return nil } + // Mapping compute and id // Filter if dsl.Fields.Filter != nil && dsl.Layout.Filter != nil && dsl.Layout.Filter.Columns != nil { for _, inst := range dsl.Layout.Filter.Columns { if filter, has := dsl.Fields.Filter[inst.Name]; has { + + // Mapping ID + dsl.Mapping.Filters[filter.ID] = inst.Name + dsl.Mapping.Filters[inst.Name] = filter.ID + if filter.Edit != nil && filter.Edit.Compute != nil { bind := filter.FilterBind() if _, has := dsl.Computes.Filter[bind]; !has { @@ -58,6 +78,11 @@ func (dsl *DSL) computeMapping() error { if dsl.Fields.Chart != nil && dsl.Layout.Chart != nil && dsl.Layout.Chart.Columns != nil { for _, inst := range dsl.Layout.Chart.Columns { if field, has := dsl.Fields.Chart[inst.Name]; has { + + // Mapping ID + dsl.Mapping.Columns[field.ID] = inst.Name + dsl.Mapping.Columns[inst.Name] = field.ID + // View if field.View != nil && field.View.Compute != nil { bind := field.ViewBind() @@ -70,5 +95,61 @@ func (dsl *DSL) computeMapping() error { } } - return nil + // Mapping Actions + dsl.mappingActions() + + // Filters + err := dsl.Fields.Filter.CPropsMerge(dsl.CProps, func(name string, filter field.FilterDSL) (xpath string) { + return fmt.Sprintf("fields.filter.%s.edit.props", name) + }) + + if err != nil { + return err + } + + // Columns + return dsl.Fields.Chart.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) { + return fmt.Sprintf("fields.chart.%s.%s.props", name, kind) + }) + +} + +// Actions get the table actions +func (dsl *DSL) mappingActions() { + + if dsl.Mapping == nil { + dsl.Mapping = &mapping.Mapping{} + } + + if dsl.Mapping.Actions == nil { + dsl.Mapping.Actions = map[string]string{} + } + + // layout.operation.actions + if dsl.Layout != nil && + dsl.Layout.Operation != nil && + dsl.Layout.Operation.Actions != nil && + len(dsl.Layout.Operation.Actions) > 0 { + + for idx, action := range dsl.Layout.Operation.Actions { + xpath := fmt.Sprintf("layout.operation.actions[%d]", idx) + dsl.Mapping.Actions[action.ID] = xpath + dsl.Mapping.Actions[xpath] = action.ID + } + + } + + // layout.filter.actions + if dsl.Layout != nil && + dsl.Layout.Filter != nil && + dsl.Layout.Filter.Actions != nil && + len(dsl.Layout.Filter.Actions) > 0 { + + for idx, action := range dsl.Layout.Filter.Actions { + xpath := fmt.Sprintf("layout.filter.actions[%d]", idx) + dsl.Mapping.Actions[action.ID] = xpath + dsl.Mapping.Actions[xpath] = action.ID + } + } + } diff --git a/widgets/chart/process.go b/widgets/chart/process.go index d205d263..6179599d 100644 --- a/widgets/chart/process.go +++ b/widgets/chart/process.go @@ -5,6 +5,7 @@ import ( "github.com/yaoapp/gou" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/yao/widgets/app" ) // Export process @@ -18,7 +19,9 @@ func exportProcess() { func processXgen(process *gou.Process) interface{} { chart := MustGet(process) - setting, err := chart.Xgen() + data := process.ArgsMap(1, map[string]interface{}{}) + excludes := app.Permissions(process, "charts", chart.ID) + setting, err := chart.Xgen(data, excludes) if err != nil { exception.New(err.Error(), 500).Throw() } diff --git a/widgets/chart/process_test.go b/widgets/chart/process_test.go index 3c3b563c..b9f85449 100644 --- a/widgets/chart/process_test.go +++ b/widgets/chart/process_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" @@ -82,6 +83,41 @@ func TestProcessXgen(t *testing.T) { assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) } +func TestProcessXgenWithPermissions(t *testing.T) { + load(t) + session.Global().Set("__permissions", map[string]interface{}{ + "charts.dashboard": []string{ + "7f46a38d7ff3f1832375ff63cd412f41", // operation.actions[0] 跳转至大屏 + "09302a46b1b6f13a346deeea79b859dd", // filter.columns[0].时间区间 + "f11f01be1f77fe6563f8577806a46158", // 综合评分 + }, + }) + + args := []interface{}{"dashboard"} + res, err := gou.NewProcess("yao.chart.Xgen", args...).Exec() + if err != nil { + t.Fatal(err) + } + + data := any.Of(res).MapStr().Dot() + assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) + assert.NotEqual(t, "时间区间", data.Get("filter.columns[0].name")) + assert.Equal(t, nil, data.Get("operation.actions[0]")) + assert.Equal(t, nil, data.Get("fields.chart.综合评分")) + + session.Global().Set("__permissions", nil) + res, err = gou.NewProcess("yao.chart.Xgen", args...).Exec() + if err != nil { + t.Fatal(err) + } + + data = any.Of(res).MapStr().Dot() + assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "时间区间", data.Get("filter.columns[0].name")) + assert.NotEqual(t, nil, data.Get("operation.actions[0]")) + assert.NotEqual(t, nil, data.Get("fields.chart.综合评分")) +} + func load(t *testing.T) { prepare(t) err := Load(config.Conf) diff --git a/widgets/chart/types.go b/widgets/chart/types.go index afcaed3c..22389c14 100644 --- a/widgets/chart/types.go +++ b/widgets/chart/types.go @@ -6,6 +6,7 @@ import ( "github.com/yaoapp/yao/widgets/compute" "github.com/yaoapp/yao/widgets/field" "github.com/yaoapp/yao/widgets/hook" + "github.com/yaoapp/yao/widgets/mapping" ) // DSL the chart DSL @@ -18,6 +19,7 @@ type DSL struct { Config map[string]interface{} `json:"config,omitempty"` CProps field.CloudProps `json:"-"` compute.Computable + *mapping.Mapping } // ActionDSL the chart action DSL