[add] sui page fetch data
This commit is contained in:
parent
1baaede53f
commit
eaf17e2b27
4 changed files with 305 additions and 4 deletions
188
sui/core/data.go
188
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
|
||||
}
|
||||
|
|
|
|||
113
sui/core/data_test.go
Normal file
113
sui/core/data_test.go
Normal file
|
|
@ -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: `<div class="p-10">
|
||||
<div>For</div>
|
||||
<div s:for="articles" s:for-item="article" s:for-index="idx">
|
||||
<div>{{ idx }} {{ article.title }}</div>
|
||||
<div>{{ article.desc }}</div>
|
||||
<div>{{ article.type == "article" ? "article" : "others"}}</div>
|
||||
<div s:if="article.type == 'article'">article</div>
|
||||
<div s:elif="article.type == 'image'">image</div>
|
||||
<div s:else="">others</div>
|
||||
</div>
|
||||
<div class="mt-10">IF</div>
|
||||
<div s:if="articles.length > 0">
|
||||
<div>{{ articles.length > 0 }} articles.length 大于 0</div>
|
||||
</div>
|
||||
<div s:if="showImage == 'yes'">showImage</div>
|
||||
<div s:elif="showImage == 'no'">noImage</div>
|
||||
<div s:else="">others</div>
|
||||
|
||||
<div class="mt-10">Bind</div>
|
||||
<div>
|
||||
<div class="w-200">{{ input.data }}</div>
|
||||
<div class="mt-5">
|
||||
<input
|
||||
class="w-200 p-2 bg-purple-900 text-white"
|
||||
type="text"
|
||||
s:bind="input.data"
|
||||
placeholder="数据双向绑定"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<input
|
||||
type="button"
|
||||
value="修改"
|
||||
class="text-blue-600 p-2"
|
||||
s:click="changeInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
},
|
||||
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
|
||||
}
|
||||
6
sui/core/render.go
Normal file
6
sui/core/render.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue