Merge pull request #770 from trheyi/main
Securely update add yao.component.GetOptions and deprecated SelectOptions
This commit is contained in:
commit
8a8070414f
6 changed files with 516 additions and 98 deletions
|
|
@ -105,7 +105,7 @@ func (dsl *DSL) Parse() {
|
||||||
for key, val := range BackendOnlyProps[t] {
|
for key, val := range BackendOnlyProps[t] {
|
||||||
if dsl.Props.Has(key) {
|
if dsl.Props.Has(key) {
|
||||||
for k, v := range val {
|
for k, v := range val {
|
||||||
dsl.Props[k] = v
|
dsl.Props[k] = dsl.copy(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,3 +127,30 @@ func (dsl *DSL) Clone() *DSL {
|
||||||
}
|
}
|
||||||
return &new
|
return &new
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy the component properties
|
||||||
|
func (dsl *DSL) copy(v interface{}) interface{} {
|
||||||
|
var res interface{} = nil
|
||||||
|
switch v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
// Clone the map
|
||||||
|
new := map[string]interface{}{}
|
||||||
|
for k1, v1 := range v.(map[string]interface{}) {
|
||||||
|
new[k1] = v1
|
||||||
|
}
|
||||||
|
res = new
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
// Clone the array
|
||||||
|
new := []interface{}{}
|
||||||
|
for _, v1 := range v.([]interface{}) {
|
||||||
|
new = append(new, dsl.copy(v1))
|
||||||
|
}
|
||||||
|
res = new
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,120 +2,292 @@ package component
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/yaoapp/gou/model"
|
"github.com/yaoapp/gou/model"
|
||||||
"github.com/yaoapp/gou/process"
|
"github.com/yaoapp/gou/process"
|
||||||
"github.com/yaoapp/kun/any"
|
"github.com/yaoapp/gou/query"
|
||||||
"github.com/yaoapp/kun/exception"
|
"github.com/yaoapp/kun/exception"
|
||||||
"github.com/yaoapp/kun/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var varRe = regexp.MustCompile(`\[\[\s*\$([A-Za-z0-9_\-]+)\s*\]\]`)
|
||||||
|
|
||||||
|
// QueryProp query prop
|
||||||
|
type QueryProp struct {
|
||||||
|
Engine string `json:"engine"`
|
||||||
|
From string `json:"from"`
|
||||||
|
LabelField string `json:"labelField,omitempty"`
|
||||||
|
ValueField string `json:"valueField,omitempty"`
|
||||||
|
IconField string `json:"iconField,omitempty"`
|
||||||
|
LabelFormat string `json:"labelFormat,omitempty"`
|
||||||
|
ValueFormat string `json:"valueFormat,omitempty"`
|
||||||
|
IconFormat string `json:"iconFormat,omitempty"`
|
||||||
|
Wheres []map[string]interface{} `json:"wheres,omitempty"`
|
||||||
|
param model.QueryParam
|
||||||
|
dsl map[string]interface{}
|
||||||
|
props map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option select option
|
||||||
|
type Option struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
Icon string `json:"icon,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Export process
|
// Export process
|
||||||
func exportProcess() {
|
func exportProcess() {
|
||||||
process.Register("yao.component.getoptions", processGetOptions)
|
process.Register("yao.component.getoptions", processGetOptions)
|
||||||
process.Register("yao.component.selectoptions", processSelectOptions)
|
process.Register("yao.component.selectoptions", processSelectOptions) // Deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
// processGetOptions get options
|
// processGetOptions get options
|
||||||
func processGetOptions(process *process.Process) interface{} {
|
func processGetOptions(process *process.Process) interface{} {
|
||||||
utils.Dump(process.Args)
|
|
||||||
return []map[string]interface{}{
|
process.ValidateArgNums(2)
|
||||||
{"label": "Option 1", "value": "1"},
|
params := process.ArgsMap(0, map[string]interface{}{})
|
||||||
{"label": "Option 2", "value": "2"},
|
props := process.ArgsMap(1, map[string]interface{}{})
|
||||||
|
|
||||||
|
// Paser props
|
||||||
|
p, err := parseOptionsProps(params, props)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query the data
|
||||||
|
options := []Option{}
|
||||||
|
if p.Engine != "" {
|
||||||
|
engine, err := query.Select(p.Engine)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
qb, err := engine.Load(p.dsl)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the data
|
||||||
|
data := qb.Get(nil)
|
||||||
|
for _, row := range data {
|
||||||
|
p.format(&options, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query param
|
||||||
|
m := model.Select(p.From)
|
||||||
|
data, err := m.Get(p.param)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 500).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the data
|
||||||
|
for _, row := range data {
|
||||||
|
p.format(&options, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func processSelectOptions(process *process.Process) interface{} {
|
// parseOptionsProps parse options props
|
||||||
process.ValidateArgNums(1)
|
func parseOptionsProps(query, props map[string]interface{}) (*QueryProp, error) {
|
||||||
query := process.ArgsMap(0, map[string]interface{}{})
|
if props["query"] == nil {
|
||||||
if !query.Has("model") {
|
exception.New("props.query is required", 400).Throw()
|
||||||
exception.New("query.model required", 400).Throw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modelName, ok := query.Get("model").(string)
|
// Read props
|
||||||
if !ok {
|
if v, ok := props["query"].(map[string]interface{}); ok {
|
||||||
exception.New("query.model must be a string", 400).Throw()
|
props = v
|
||||||
}
|
}
|
||||||
|
|
||||||
m := model.Select(modelName)
|
// Read query condition
|
||||||
|
var keywords string = ""
|
||||||
valueField := query.Get("value")
|
var selected interface{} = nil
|
||||||
if valueField == nil {
|
if v, ok := query["keywords"].(string); ok {
|
||||||
valueField = "id"
|
keywords = v
|
||||||
}
|
}
|
||||||
value, ok := valueField.(string)
|
if v, ok := query["selected"]; ok {
|
||||||
if !ok {
|
selected = v
|
||||||
exception.New("query.value must be a string", 400).Throw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
labelField := query.Get("label")
|
raw, err := jsoniter.Marshal(props)
|
||||||
if labelField == nil {
|
if err != nil {
|
||||||
labelField = "name"
|
return nil, err
|
||||||
}
|
|
||||||
label, ok := labelField.(string)
|
|
||||||
if !ok {
|
|
||||||
exception.New("query.label must be a string", 400).Throw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
limit := 500
|
qprops := QueryProp{}
|
||||||
if query.Get("limit") != nil {
|
err = jsoniter.Unmarshal(raw, &qprops)
|
||||||
v := any.Of(query.Get("limit"))
|
if err != nil {
|
||||||
if v.IsInt() || v.IsString() {
|
return nil, err
|
||||||
limit = v.CInt()
|
}
|
||||||
|
|
||||||
|
qprops.props = props
|
||||||
|
err = qprops.parse(keywords, selected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &qprops, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// format format the option
|
||||||
|
func (q *QueryProp) format(options *[]Option, row map[string]interface{}) {
|
||||||
|
label := row[q.LabelField]
|
||||||
|
value := row[q.ValueField]
|
||||||
|
option := Option{Label: fmt.Sprintf("%v", label), Value: value}
|
||||||
|
if q.IconField != "" {
|
||||||
|
option.Icon = fmt.Sprintf("%v", row[q.IconField])
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.LabelFormat != "" {
|
||||||
|
option.Label = q.replaceString(q.LabelFormat, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.ValueFormat != "" {
|
||||||
|
option.Value = q.replaceString(q.ValueFormat, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.IconField != "" && q.IconFormat != "" {
|
||||||
|
option.Icon = q.replaceString(q.IconFormat, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the option
|
||||||
|
*options = append(*options, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryProp) parse(keywords string, selected interface{}) error {
|
||||||
|
if q.Wheres == nil {
|
||||||
|
q.Wheres = []map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the query param required fields
|
||||||
|
if q.Engine == "" {
|
||||||
|
if q.From == "" {
|
||||||
|
return fmt.Errorf("props.from is required")
|
||||||
|
}
|
||||||
|
if q.LabelField == "" {
|
||||||
|
return fmt.Errorf("props.labelField is required")
|
||||||
|
}
|
||||||
|
if q.ValueField == "" {
|
||||||
|
return fmt.Errorf("props.valueField is required")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wheres := []model.QueryWhere{}
|
// Parse wheres
|
||||||
switch input := query.Get("wheres").(type) {
|
wheres := []map[string]interface{}{}
|
||||||
case string:
|
for _, where := range q.Wheres {
|
||||||
where := model.QueryWhere{}
|
if q.replaceWhere(where, map[string]interface{}{"KEYWORDS": keywords, "SELECTED": selected}) {
|
||||||
err := jsoniter.Unmarshal([]byte(input), &where)
|
|
||||||
if err != nil {
|
|
||||||
exception.New("query.wheres error %s", 400, err.Error()).Throw()
|
|
||||||
}
|
|
||||||
wheres = append(wheres, where)
|
|
||||||
break
|
|
||||||
|
|
||||||
case []string:
|
|
||||||
for _, data := range input {
|
|
||||||
where := model.QueryWhere{}
|
|
||||||
err := jsoniter.Unmarshal([]byte(data), &where)
|
|
||||||
if err != nil {
|
|
||||||
exception.New("query.wheres error %s", 400, err.Error()).Throw()
|
|
||||||
}
|
|
||||||
wheres = append(wheres, where)
|
wheres = append(wheres, where)
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if data, ok := query.Get("wheres").(string); ok {
|
// Update the props
|
||||||
data = strings.TrimSpace(data)
|
props := map[string]interface{}{}
|
||||||
if strings.HasPrefix(data, "{") && strings.HasSuffix(data, "}") {
|
for key, value := range q.props {
|
||||||
data = fmt.Sprintf("[%s]", data)
|
props[key] = value
|
||||||
|
}
|
||||||
|
props["wheres"] = wheres
|
||||||
|
|
||||||
|
// Return the query dsl, if the engine is set
|
||||||
|
if q.Engine != "" {
|
||||||
|
|
||||||
|
if q.LabelField == "" {
|
||||||
|
q.LabelField = "label"
|
||||||
}
|
}
|
||||||
err := jsoniter.Unmarshal([]byte(data), &wheres)
|
|
||||||
if err != nil {
|
if q.ValueField == "" {
|
||||||
exception.New("query.wheres error %s", 400, err.Error()).Throw()
|
q.ValueField = "value"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if q.IconField == "" {
|
||||||
|
q.IconField = "icon"
|
||||||
|
}
|
||||||
|
|
||||||
|
q.dsl = props
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := m.Get(model.QueryParam{
|
// Parse the query param from the props
|
||||||
Select: []interface{}{valueField, labelField},
|
q.param = model.QueryParam{
|
||||||
Wheres: wheres,
|
Model: q.From,
|
||||||
Limit: limit,
|
Select: []interface{}{q.LabelField, q.ValueField},
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if q.IconField != "" {
|
||||||
|
q.param.Select = append(q.param.Select, q.IconField)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := jsoniter.Marshal(props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exception.New("%s", 500, err.Error()).Throw()
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
err = jsoniter.Unmarshal(raw, &q.param)
|
||||||
for _, row := range rows {
|
if err != nil {
|
||||||
res = append(res, map[string]interface{}{
|
return err
|
||||||
"label": row.Get(label),
|
|
||||||
"value": row.Get(value),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return res
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryProp) replaceString(format string, data map[string]interface{}) string {
|
||||||
|
if data == nil {
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := varRe.FindAllStringSubmatch(format, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
for _, match := range matches {
|
||||||
|
name := match[1]
|
||||||
|
orignal := match[0]
|
||||||
|
if val, ok := data[name]; ok {
|
||||||
|
format = strings.ReplaceAll(format, orignal, fmt.Sprintf("%v", val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace replace the query where condition
|
||||||
|
// return true if the where condition is effective, otherwise return false
|
||||||
|
func (q *QueryProp) replaceWhere(where map[string]interface{}, data map[string]interface{}) bool {
|
||||||
|
if where == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range where {
|
||||||
|
if v, ok := value.(string); ok {
|
||||||
|
matches := varRe.FindAllStringSubmatch(v, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
name := matches[0][1]
|
||||||
|
if val, ok := data[name]; ok {
|
||||||
|
|
||||||
|
// Check if the value is empty
|
||||||
|
if val == nil || val == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the value
|
||||||
|
where[key] = val
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: please use processGetOptions instead
|
||||||
|
// This function may cause security issue, please use processGetOptions instead
|
||||||
|
// It will be removed when the v0.10.4 released
|
||||||
|
func processSelectOptions(process *process.Process) interface{} {
|
||||||
|
message := "process yao.component.SelectOptions is deprecated, please use yao.component.GetOptions instead"
|
||||||
|
exception.New(message, 400).Throw()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
225
widgets/component/process_test.go
Normal file
225
widgets/component/process_test.go
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
package component
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/yaoapp/gou/process"
|
||||||
|
"github.com/yaoapp/yao/config"
|
||||||
|
"github.com/yaoapp/yao/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessGetOptions(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
props := prepare(t)
|
||||||
|
|
||||||
|
name := "yao.component.GetOptions"
|
||||||
|
for _, queryParam := range props {
|
||||||
|
|
||||||
|
args := []interface{}{
|
||||||
|
map[string]interface{}{},
|
||||||
|
map[string]interface{}{"query": queryParam},
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := process.Of(name, args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer p.Release()
|
||||||
|
res, ok := p.Value().([]Option)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Result is not []Option")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) != 8 {
|
||||||
|
t.Fatal("Result length is not 8")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Category cat 1-active-1", res[0].Label)
|
||||||
|
assert.Equal(t, "1", res[0].Value)
|
||||||
|
assert.Equal(t, "active-1", res[0].Icon)
|
||||||
|
|
||||||
|
// With KEYWORDS
|
||||||
|
args = []interface{}{
|
||||||
|
map[string]interface{}{"keywords": "dog"},
|
||||||
|
map[string]interface{}{"query": queryParam},
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = process.Of(name, args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, ok = p.Value().([]Option)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Result is not []Option")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) != 2 {
|
||||||
|
t.Fatal("Result length is not 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Category dog 7-active-7", res[0].Label)
|
||||||
|
assert.Equal(t, "7", res[0].Value)
|
||||||
|
assert.Equal(t, "active-7", res[0].Icon)
|
||||||
|
|
||||||
|
// With SELECTED
|
||||||
|
args = []interface{}{
|
||||||
|
map[string]interface{}{"selected": []interface{}{1}},
|
||||||
|
map[string]interface{}{"query": queryParam},
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = process.Of(name, args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
res, ok = p.Value().([]Option)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Result is not []Option")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) != 1 {
|
||||||
|
t.Fatal("Result length is not 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Category cat 1-active-1", res[0].Label)
|
||||||
|
assert.Equal(t, "1", res[0].Value)
|
||||||
|
assert.Equal(t, "active-1", res[0].Icon)
|
||||||
|
|
||||||
|
// With KEYWORDS and SELECTED
|
||||||
|
args = []interface{}{
|
||||||
|
map[string]interface{}{"keywords": "dog", "selected": []interface{}{1, 2}},
|
||||||
|
map[string]interface{}{"query": queryParam},
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = process.Of(name, args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, ok = p.Value().([]Option)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Result is not []Option")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) != 4 {
|
||||||
|
t.Fatal("Result length is not 4")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Category cat 1-active-1", res[0].Label)
|
||||||
|
assert.Equal(t, "1", res[0].Value)
|
||||||
|
assert.Equal(t, "active-1", res[0].Icon)
|
||||||
|
assert.Equal(t, "Category dog 7-active-7", res[2].Label)
|
||||||
|
assert.Equal(t, "7", res[2].Value)
|
||||||
|
assert.Equal(t, "active-7", res[2].Icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessSelectOptions(t *testing.T) {
|
||||||
|
|
||||||
|
name := "yao.component.SelectOptions"
|
||||||
|
|
||||||
|
p, err := process.Of(name, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Execute()
|
||||||
|
assert.Contains(t, err.Error(), "process yao.component.SelectOptions is deprecated, please use yao.component.GetOptions instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepare(t *testing.T) map[string]map[string]interface{} {
|
||||||
|
exportProcess()
|
||||||
|
|
||||||
|
// Prepare data for testing
|
||||||
|
err := process.New("models.category.Migrate", true).Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = process.New("models.category.Insert",
|
||||||
|
[]string{"name", "status"},
|
||||||
|
[][]interface{}{
|
||||||
|
{"Category cat 1", "active-1"},
|
||||||
|
{"Category cat 2", "active-2"},
|
||||||
|
{"Category cat 3", "active-3"},
|
||||||
|
{"Category cat 4", "active-4"},
|
||||||
|
{"Category cat 5", "active-5"},
|
||||||
|
{"Category cat 6", "active-6"},
|
||||||
|
{"Category dog 7", "active-7"},
|
||||||
|
{"Category dog 8", "active-8"},
|
||||||
|
}).Execute()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
queryParam := map[string]interface{}{}
|
||||||
|
queryDSL := map[string]interface{}{}
|
||||||
|
err = jsoniter.Unmarshal([]byte(`{
|
||||||
|
"labelField": "name",
|
||||||
|
"valueField": "id",
|
||||||
|
"iconField": "status",
|
||||||
|
"from": "category",
|
||||||
|
"wheres": [
|
||||||
|
{ "column": "name", "value": "[[ $KEYWORDS ]]", "op": "match" },
|
||||||
|
{
|
||||||
|
"method": "orwhere",
|
||||||
|
"column": "id",
|
||||||
|
"op": "in",
|
||||||
|
"value": "[[ $SELECTED ]]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"limit": 20,
|
||||||
|
"labelFormat": "[[ $name ]]-[[ $status ]]",
|
||||||
|
"valueFormat": "[[ $id ]]",
|
||||||
|
"iconFormat": "[[ $status ]]"
|
||||||
|
}`), &queryParam)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jsoniter.Unmarshal([]byte(`{
|
||||||
|
"engine": "query-test",
|
||||||
|
"select": ["name as label", "id as value", "status as icon"],
|
||||||
|
"from": "category",
|
||||||
|
"wheres": [
|
||||||
|
{ "field": "name", "match": "[[ $KEYWORDS ]]" },
|
||||||
|
{ "or": true, "field":"id", "in":"[[ $SELECTED ]]" }
|
||||||
|
],
|
||||||
|
"limit": 20,
|
||||||
|
"labelFormat": "[[ $label ]]-[[ $icon ]]",
|
||||||
|
"valueFormat": "[[ $value ]]",
|
||||||
|
"iconFormat": "[[ $icon ]]"
|
||||||
|
}`), &queryDSL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]map[string]interface{}{
|
||||||
|
"queryParam": queryParam,
|
||||||
|
"queryDSL": queryDSL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/yaoapp/yao/flow"
|
"github.com/yaoapp/yao/flow"
|
||||||
"github.com/yaoapp/yao/i18n"
|
"github.com/yaoapp/yao/i18n"
|
||||||
"github.com/yaoapp/yao/test"
|
"github.com/yaoapp/yao/test"
|
||||||
|
"github.com/yaoapp/yao/widgets/component"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoad(t *testing.T) {
|
func TestLoad(t *testing.T) {
|
||||||
|
|
@ -40,4 +41,6 @@ func prepare(t *testing.T, language ...string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.Export()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package dashboard
|
package dashboard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -10,9 +9,9 @@ import (
|
||||||
"github.com/yaoapp/gou/process"
|
"github.com/yaoapp/gou/process"
|
||||||
"github.com/yaoapp/gou/session"
|
"github.com/yaoapp/gou/session"
|
||||||
"github.com/yaoapp/kun/any"
|
"github.com/yaoapp/kun/any"
|
||||||
"github.com/yaoapp/kun/maps"
|
|
||||||
"github.com/yaoapp/yao/config"
|
"github.com/yaoapp/yao/config"
|
||||||
"github.com/yaoapp/yao/test"
|
"github.com/yaoapp/yao/test"
|
||||||
|
"github.com/yaoapp/yao/widgets/component"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProcessData(t *testing.T) {
|
func TestProcessData(t *testing.T) {
|
||||||
|
|
@ -33,12 +32,12 @@ func TestProcessComponent(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
prepare(t)
|
prepare(t)
|
||||||
|
testData(t)
|
||||||
|
|
||||||
args := []interface{}{
|
args := []interface{}{
|
||||||
"workspace",
|
"workspace",
|
||||||
"fields.filter.状态.edit.props.xProps",
|
"fields.filter.状态.edit.props.xProps",
|
||||||
"remote",
|
"remote",
|
||||||
map[string]interface{}{"select": []string{"name", "status"}, "limit": 2},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := process.New("yao.dashboard.Component", args...).Exec()
|
res, err := process.New("yao.dashboard.Component", args...).Exec()
|
||||||
|
|
@ -46,15 +45,13 @@ func TestProcessComponent(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(res)
|
pets, ok := res.([]component.Option)
|
||||||
|
|
||||||
pets, ok := res.([]maps.MapStr)
|
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, 2, len(pets))
|
assert.Equal(t, 2, len(pets))
|
||||||
assert.Equal(t, "Cookie", pets[0]["name"])
|
assert.Equal(t, "Cookie", pets[0].Label)
|
||||||
assert.Equal(t, "checked", pets[0]["status"])
|
assert.Equal(t, "checked", pets[0].Value)
|
||||||
assert.Equal(t, "Baby", pets[1]["name"])
|
assert.Equal(t, "Baby", pets[1].Label)
|
||||||
assert.Equal(t, "checked", pets[1]["status"])
|
assert.Equal(t, "checked", pets[1].Value)
|
||||||
|
|
||||||
args = []interface{}{
|
args = []interface{}{
|
||||||
"workspace",
|
"workspace",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/yaoapp/yao/config"
|
"github.com/yaoapp/yao/config"
|
||||||
"github.com/yaoapp/yao/helper"
|
"github.com/yaoapp/yao/helper"
|
||||||
"github.com/yaoapp/yao/test"
|
"github.com/yaoapp/yao/test"
|
||||||
|
"github.com/yaoapp/yao/widgets/component"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProcessSearch(t *testing.T) {
|
func TestProcessSearch(t *testing.T) {
|
||||||
|
|
@ -364,13 +365,6 @@ func TestProcessComponent(t *testing.T) {
|
||||||
"pet",
|
"pet",
|
||||||
"fields.filter.状态.edit.props.xProps",
|
"fields.filter.状态.edit.props.xProps",
|
||||||
"remote",
|
"remote",
|
||||||
map[string]interface{}{
|
|
||||||
"model": "pet",
|
|
||||||
"label": "name",
|
|
||||||
"value": "status",
|
|
||||||
"wheres[]": `{"column":"id","op":"ge","value":0}`,
|
|
||||||
"limit": "2",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := process.New("yao.table.Component", args...).Exec()
|
res, err := process.New("yao.table.Component", args...).Exec()
|
||||||
|
|
@ -378,13 +372,13 @@ func TestProcessComponent(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pets, ok := res.([]map[string]interface{})
|
pets, ok := res.([]component.Option)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, 2, len(pets))
|
assert.Equal(t, 2, len(pets))
|
||||||
assert.Equal(t, "Cookie", pets[0]["label"])
|
assert.Equal(t, "Cookie", pets[0].Label)
|
||||||
assert.Equal(t, "checked", pets[0]["value"])
|
assert.Equal(t, "checked", pets[0].Value)
|
||||||
assert.Equal(t, "Baby", pets[1]["label"])
|
assert.Equal(t, "Baby", pets[1].Label)
|
||||||
assert.Equal(t, "checked", pets[1]["value"])
|
assert.Equal(t, "checked", pets[1].Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcessComponentError(t *testing.T) {
|
func TestProcessComponentError(t *testing.T) {
|
||||||
|
|
@ -525,7 +519,7 @@ func TestProcessXgenWithPermissions(t *testing.T) {
|
||||||
session.Global().Set("__permissions", map[string]interface{}{
|
session.Global().Set("__permissions", map[string]interface{}{
|
||||||
"tables.pet": []string{
|
"tables.pet": []string{
|
||||||
"8ca9bdf0fa2cbc8f1018f8566ed6ab5e", // fields.table.消费金额
|
"8ca9bdf0fa2cbc8f1018f8566ed6ab5e", // fields.table.消费金额
|
||||||
"f03f1ae60c46dd6cdeda87b919a51d7e", // fields.filter.状态
|
"c5b1f06582e1dff3ac6d16822fdadd54", // fields.filter.状态
|
||||||
"b1483ade34cd51261817558114e74e3f", // filter.actions[0] 添加宠物
|
"b1483ade34cd51261817558114e74e3f", // filter.actions[0] 添加宠物
|
||||||
"e6a67850312980e8372e550c5b361097", // operation.actions[0] 查看
|
"e6a67850312980e8372e550c5b361097", // operation.actions[0] 查看
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue