diff --git a/sui/core/data.go b/sui/core/data.go index 1e2f5d0c8..862c41745 100644 --- a/sui/core/data.go +++ b/sui/core/data.go @@ -1,9 +1,12 @@ package core import ( + "fmt" "strings" jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou/process" + "github.com/yaoapp/kun/any" ) // Data get the data @@ -22,7 +25,186 @@ func (page *Page) Data(request *Request) (map[string]interface{}, map[string]int return nil, setting, nil } -// Render render for the html -func (page *Page) Render(html string, data map[string]interface{}, warnings []string) (string, error) { - return html, nil +// Exec get the data +func (page *Page) Exec(request *Request) (map[string]interface{}, error) { + + if page.Codes.DATA.Code == "" { + return map[string]interface{}{}, nil + } + + data := map[string]interface{}{} + err := jsoniter.UnmarshalFromString(page.Codes.DATA.Code, &data) + if err != nil { + return nil, err + } + + err = page.execMap(request, data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (page *Page) execMap(r *Request, m map[string]interface{}) error { + + for key, value := range m { + + if strings.HasPrefix(key, "$") { + res, err := page.call(r, value) + if err != nil { + return err + } + newKey := key[1:] + m[newKey] = res + delete(m, key) + continue + } + + res, err := page.execValue(r, value) + if err != nil { + return err + } + m[key] = res + + } + + return nil +} + +func (page *Page) execValue(r *Request, value interface{}) (interface{}, error) { + switch v := value.(type) { + case string: + if strings.HasPrefix(v, "$") { + return page.call(r, strings.TrimLeft(v, "$")) + } + return v, nil + + case []interface{}: + for i, item := range v { + res, err := page.execValue(r, item) + if err != nil { + return nil, err + } + v[i] = res + } + return v, nil + + case []string: + interfaceSlice := make([]interface{}, len(v)) + for i, item := range v { + interfaceSlice[i] = item + } + return page.execValue(r, interfaceSlice) + + case map[string]interface{}: + + if _, ok := v["process"].(string); ok { + if call, _ := v["__exec"].(bool); call { + res, err := page.call(r, v) + if err != nil { + return nil, err + } + return res, nil + } + } + + err := page.execMap(r, v) + if err != nil { + return nil, err + } + return v, nil + + default: + return v, nil + } +} + +func (page *Page) call(r *Request, p interface{}) (interface{}, error) { + + processName := "" + processArgs := []interface{}{r} + switch v := p.(type) { + case string: + processName = v + break + + case map[string]interface{}: + if name, ok := v["process"].(string); ok { + processName = name + } + + if args, ok := v["args"].([]interface{}); ok { + args, err := page.parseArgs(r, args) + if err != nil { + return nil, err + } + processArgs = append(args, processArgs...) + } + } + + if processName == "" { + return nil, fmt.Errorf("process name is empty") + } + + process, err := process.Of(processName, processArgs...) + if err != nil { + return nil, err + } + + return process.Exec() +} + +func (page *Page) parseArgs(r *Request, args []interface{}) ([]interface{}, error) { + + data := any.MapOf(map[string]interface{}{ + "param": r.Params, + "query": r.Query, + "payload": map[string]interface{}{}, + "header": r.Headers, + "theme": r.Theme, + "locale": r.Locale, + }).Dot() + + for i, arg := range args { + switch v := arg.(type) { + + case string: + if strings.HasPrefix(v, "$") { + key := strings.TrimLeft(v, "$") + args[i] = key + if data.Has(key) { + v := data.Get(key) + if strings.HasPrefix(key, "query.") || strings.HasPrefix(key, "header.") { + switch arg := v.(type) { + case []interface{}: + if len(arg) == 1 { + args[i] = arg[0] + } + case []string: + if len(arg) == 1 { + args[i] = arg[0] + } + } + } + } + } + + case []interface{}: + res, err := page.parseArgs(r, v) + if err != nil { + return nil, err + } + args[i] = res + + case map[string]interface{}: + res, err := page.parseArgs(r, []interface{}{v}) + if err != nil { + return nil, err + } + args[i] = res[0] + } + } + + return args, nil } diff --git a/sui/core/data_test.go b/sui/core/data_test.go new file mode 100644 index 000000000..a39d78786 --- /dev/null +++ b/sui/core/data_test.go @@ -0,0 +1,113 @@ +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" +) + +func TestPageExec(t *testing.T) { + prepare(t) + defer clean() + + page := testPage(t) + request := &Request{ + Query: map[string][]string{"show": {"yes"}}, + Locale: "zh-CN", + Theme: "dark", + } + + data, err := page.Exec(request) + if err != nil { + t.Fatalf("Exec error: %v", err) + } + + assert.NotEmpty(t, data) + + res := any.Of(data).Map().Dot() + assert.Equal(t, "yes", res.Get("array[3][0].query")) + assert.Equal(t, "Article Search", res.Get("articles.data[0].description")) +} + +func prepare(t *testing.T) { + test.Prepare(t, config.Conf, "YAO_TEST_BUILDER_APPLICATION") +} + +func clean() { + test.Clean() +} + +func testPage(t *testing.T) *Page { + + page := &Page{ + Name: "test", + Route: "test", + Codes: SourceCodes{ + HTML: Source{ + File: "test.html", + Code: `
+
For
+
+
{{ idx }} {{ article.title }}
+
{{ article.desc }}
+
{{ article.type == "article" ? "article" : "others"}}
+
article
+
image
+
others
+
+
IF
+
+
{{ articles.length > 0 }} articles.length 大于 0
+
+
showImage
+
noImage
+
others
+ +
Bind
+
+
{{ input.data }}
+
+ +
+
+ +
+
+
`, + }, + DATA: Source{ + File: "test.json", + Code: `{ + "$articles": "scripts.article.Search", + "$showImage": { + "process": "scripts.article.ShowImage", + "args": ["$query.show"] + }, + "array": [ + "item-1", + "$scripts.article.Setting", + {"$images": "scripts.article.Images"}, + {"process": "scripts.article.Thumbs", "args": ["$query.show"], "__exec": true } + ], + "input": { "data": "hello world" } + } + `, + }, + }, + } + + return page +} diff --git a/sui/core/render.go b/sui/core/render.go new file mode 100644 index 000000000..63845ad76 --- /dev/null +++ b/sui/core/render.go @@ -0,0 +1,6 @@ +package core + +// Render render for the html +func (page *Page) Render(html string, data map[string]interface{}, warnings []string) (string, error) { + return html, nil +} diff --git a/test/utils.go b/test/utils.go index b19078c9c..066eb0eb0 100644 --- a/test/utils.go +++ b/test/utils.go @@ -196,7 +196,7 @@ func loadConnector(t *testing.T, cfg config.Config) { } func loadScript(t *testing.T, cfg config.Config) { - exts := []string{"*.js"} + exts := []string{"*.js", "*.ts"} err := application.App.Walk("scripts", func(root, file string, isdir bool) error { if isdir { return nil