[add] widget list permission support

This commit is contained in:
Max 2022-12-13 23:30:47 +08:00
parent 30473739ac
commit 72b9a8b62d
9 changed files with 155 additions and 35 deletions

View file

@ -232,10 +232,10 @@ func (dsl *DSL) Xgen(data map[string]interface{}, excludes map[string]bool) (map
return nil, err
}
// Hooks
onChange := map[string]interface{}{}
onChange := map[string]interface{}{} // Hooks
setting["fields"] = fields
setting["config"] = dsl.Config
for _, cProp := range dsl.CProps {
err := cProp.Replace(setting, func(cProp component.CloudPropsDSL) interface{} {

View file

@ -110,6 +110,18 @@ func exportAPI() error {
}
http.Paths = append(http.Paths, path)
// POST /api/__yao/list/:id/component/:xpath/:method -> Default process: yao.list.Component $param.id $param.xpath $param.method :payload
path = gou.Path{
Label: "Component",
Description: "Component",
Path: "/:id/component/:xpath/:method",
Method: "POST",
Process: "yao.list.Component",
In: []string{"$param.id", "$param.xpath", "$param.method", ":payload"},
Out: gou.Out{Status: 200, Type: "application/json"},
}
http.Paths = append(http.Paths, path)
// POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.list.Upload $param.id $param.xpath $param.method $file.file
path = gou.Path{
Label: "Upload",

View file

@ -109,6 +109,13 @@ func (fields *FieldsDSL) Xgen(layout *LayoutDSL) (map[string]interface{}, error)
path := fmt.Sprintf("layout.columns[%d]", i)
messages = append(messages, fmt.Sprintf("fields.list.%s not found, checking %s", f.Name, path))
}
if field.Edit != nil && field.Edit.Props != nil {
if _, has := field.Edit.Props["$on:change"]; has {
delete(field.Edit.Props, "$on:change")
}
}
lists[name] = field.Map()
}
}

View file

@ -3,6 +3,8 @@ package list
import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou"
"github.com/yaoapp/yao/widgets/component"
"github.com/yaoapp/yao/widgets/mapping"
"github.com/yaoapp/yao/widgets/table"
)
@ -119,17 +121,42 @@ func (layout *LayoutDSL) BindTable(tab *table.DSL, listID string, fields *Fields
}
// 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
// layout.list.columns
columns := []component.InstanceDSL{}
if clone.List != nil && clone.List.Columns != nil {
for _, column := range clone.List.Columns {
id, has := mapping.Columns[column.Name]
if !has {
continue
}
if _, has := excludes[id]; has {
continue
}
columns = append(columns, column)
}
clone.List.Columns = columns
}
return res, nil
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
}

View file

@ -143,10 +143,10 @@ func LoadData(data []byte, id string, root string) error {
return fmt.Errorf("[List] LoadData Bind %s %s", id, err.Error())
}
// Parse
err = dsl.Parse()
// Mapping
err = dsl.mapping()
if err != nil {
return fmt.Errorf("[List] LoadData Parse %s %s", id, err.Error())
return fmt.Errorf("[List] LoadData Mapping %s %s", id, err.Error())
}
// Validate
@ -187,23 +187,8 @@ func MustGet(list interface{}) *DSL {
return t
}
// Parse Layout
func (dsl *DSL) Parse() error {
// ComputeFields
err := dsl.computeMapping()
if err != nil {
return err
}
// Columns
return dsl.Fields.List.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) {
return fmt.Sprintf("fields.list.%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) {
if dsl.Layout == nil {
dsl.Layout = &LayoutDSL{List: &ViewLayoutDSL{}}
@ -213,12 +198,12 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) {
dsl.Layout.List = &ViewLayoutDSL{}
}
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(dsl.Layout)
fields, err := dsl.Fields.Xgen(layout)
if err != nil {
return nil, err
}
@ -228,6 +213,18 @@ 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
}
onChange := map[string]interface{}{} // Hooks
setting["fields"] = fields
setting["config"] = dsl.Config
for _, cProp := range dsl.CProps {
@ -242,11 +239,22 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) {
"params": cProp.Query,
}
})
if err != nil {
return nil, err
}
}
// hooks
if cProp.Name == "on:change" {
field := strings.TrimPrefix(cProp.Xpath, "fields.list.")
field = strings.TrimSuffix(field, ".edit.props")
onChange[field] = map[string]interface{}{
"api": fmt.Sprintf("/api/__yao/list/%s%s", dsl.ID, cProp.Path()),
"params": cProp.Query,
}
}
}
setting["hooks"] = map[string]interface{}{"onChange": onChange}
setting["name"] = dsl.Name
return setting, nil
}

View file

@ -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) {
@ -17,7 +18,7 @@ func (dsl *DSL) getField() func(string) (*field.ColumnDSL, string, string, error
}
}
func (dsl *DSL) computeMapping() error {
func (dsl *DSL) mapping() error {
if dsl.Computes == nil {
dsl.Computes = &compute.Maps{
Filter: map[string][]compute.Unit{},
@ -26,16 +27,33 @@ 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
if dsl.Fields.List != nil && dsl.Layout.List != nil {
for _, inst := range dsl.Layout.List.Columns {
if field, has := dsl.Fields.List[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()
@ -57,5 +75,8 @@ func (dsl *DSL) computeMapping() error {
}
}
return nil
// Columns
return dsl.Fields.List.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) {
return fmt.Sprintf("fields.list.%s.%s.props", name, kind)
})
}

View file

@ -10,6 +10,7 @@ import (
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/widgets/app"
)
// Export process
@ -25,7 +26,9 @@ func exportProcess() {
func processXgen(process *gou.Process) interface{} {
list := MustGet(process)
setting, err := list.Xgen()
data := process.ArgsMap(1, map[string]interface{}{})
excludes := app.Permissions(process, "lists", list.ID)
setting, err := list.Xgen(data, excludes)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
@ -143,7 +146,7 @@ func processUpload(process *gou.Process) interface{} {
func processSetting(process *gou.Process) interface{} {
list := MustGet(process)
process.Args = append(process.Args, process.Args[0]) // listle name
process.Args = append(process.Args, process.Args[0]) // list name
return list.Action.Setting.MustExec(process)
}

View file

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/yao/config"
q "github.com/yaoapp/yao/query"
@ -23,6 +24,8 @@ func TestProcessSetting(t *testing.T) {
}
data := any.Of(res).MapStr().Dot()
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("父类")+".edit.props.xProps/remote", data.Get("fields.list.父类.edit.props.xProps.remote.api"))
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("名称")+".edit.props/on%3Achange", data.Get("hooks.onChange.名称.api"))
assert.Equal(t, "开发者自定义", data.Get("hooks.onChange.名称.params.extra"))
}
func TestProcessXgen(t *testing.T) {
@ -36,6 +39,43 @@ func TestProcessXgen(t *testing.T) {
}
data := any.Of(res).MapStr().Dot()
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("父类")+".edit.props.xProps/remote", data.Get("fields.list.父类.edit.props.xProps.remote.api"))
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("名称")+".edit.props/on%3Achange", data.Get("hooks.onChange.名称.api"))
assert.Equal(t, "开发者自定义", data.Get("hooks.onChange.名称.params.extra"))
}
func TestProcessXgenWithPermissions(t *testing.T) {
load(t)
clear(t)
testData(t)
session.Global().Set("__permissions", map[string]interface{}{
"lists.category": []string{
"a189b2bf0dd9b29f6628b386e501397f", // fields.list.库存预警
},
})
args := []interface{}{"category"}
res, err := gou.NewProcess("yao.list.Xgen", args...).Exec()
if err != nil {
t.Fatal(err)
}
data := any.Of(res).MapStr().Dot()
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("父类")+".edit.props.xProps/remote", data.Get("fields.list.父类.edit.props.xProps.remote.api"))
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("名称")+".edit.props/on%3Achange", data.Get("hooks.onChange.名称.api"))
assert.Equal(t, "开发者自定义", data.Get("hooks.onChange.名称.params.extra"))
assert.False(t, data.Has("fields.list.库存预警"))
session.Global().Set("__permissions", nil)
res, err = gou.NewProcess("yao.list.Xgen", args...).Exec()
if err != nil {
t.Fatal(err)
}
data = any.Of(res).MapStr().Dot()
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("父类")+".edit.props.xProps/remote", data.Get("fields.list.父类.edit.props.xProps.remote.api"))
assert.Equal(t, "/api/__yao/list/category/component/fields.list."+url.QueryEscape("名称")+".edit.props/on%3Achange", data.Get("hooks.onChange.名称.api"))
assert.Equal(t, "开发者自定义", data.Get("hooks.onChange.名称.params.extra"))
assert.True(t, data.Has("fields.list.库存预警"))
}
func load(t *testing.T) {

View file

@ -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 list DSL
@ -19,6 +20,7 @@ type DSL struct {
Config map[string]interface{} `json:"config,omitempty"`
CProps field.CloudProps `json:"-"`
compute.Computable
*mapping.Mapping
}
// ActionDSL the list action DSL