From 9d1dea4073c90356e469c9d7b93ba394ac706189 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 15 Nov 2022 20:05:49 +0800 Subject: [PATCH] [add] form widget upload api and process --- widgets/form/action.go | 9 ++++++++ widgets/form/api.go | 14 +++++++++++++ widgets/form/form.go | 5 +++++ widgets/form/form_test.go | 7 +++++++ widgets/form/process.go | 30 +++++++++++++++++++++++++++ widgets/form/process_test.go | 39 +++++++++++++++++++++++++++++++++++ widgets/form/types.go | 1 + widgets/table/api.go | 4 ++-- widgets/table/process_test.go | 3 +++ 9 files changed, 110 insertions(+), 2 deletions(-) diff --git a/widgets/form/action.go b/widgets/form/action.go index 6c7449f6..e529e7ff 100644 --- a/widgets/form/action.go +++ b/widgets/form/action.go @@ -22,6 +22,11 @@ var processActionDefaults = map[string]*action.Process{ Guard: "bearer-jwt", Default: []interface{}{nil, nil, nil}, }, + "Upload": { + Name: "yao.form.Upload", + Guard: "bearer-jwt", + Default: []interface{}{nil, nil, nil}, + }, "Find": { Name: "yao.form.Find", Guard: "bearer-jwt", @@ -60,6 +65,10 @@ func (act *ActionDSL) SetDefaultProcess() { Merge(processActionDefaults["Component"]). SetHandler(processHandler) + act.Upload = action.ProcessOf(act.Upload). + Merge(processActionDefaults["Upload"]). + SetHandler(processHandler) + act.Find = action.ProcessOf(act.Find). WithBefore(act.BeforeFind).WithAfter(act.AfterFind). Merge(processActionDefaults["Find"]). diff --git a/widgets/form/api.go b/widgets/form/api.go index 953450ba..daade514 100644 --- a/widgets/form/api.go +++ b/widgets/form/api.go @@ -51,6 +51,8 @@ func (form *DSL) getAction(path string) (*action.Process, error) { return form.Action.Setting, nil case "/api/__yao/form/:id/component/:xpath/:method": return form.Action.Component, nil + case "/api/__yao/form/:id/upload/:xpath/:method": + return form.Action.Upload, nil case "/api/__yao/form/:id/find/:primary": return form.Action.Find, nil case "/api/__yao/form/:id/save": @@ -114,6 +116,18 @@ func exportAPI() error { } http.Paths = append(http.Paths, path) + // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.form.Upload $param.id $param.xpath $param.method $file.file + path = gou.Path{ + Label: "Upload", + Description: "Upload", + Path: "/:id/upload/:xpath/:method", + Method: "POST", + Process: "yao.form.Upload", + In: []string{"$param.id", "$param.xpath", "$param.method", "$file.file"}, + Out: gou.Out{Status: 200, Type: "application/json"}, + } + http.Paths = append(http.Paths, path) + // POST /api/__yao/form/:id/save -> Default process: yao.form.Save $param.id :payload path = gou.Path{ Label: "Save", diff --git a/widgets/form/form.go b/widgets/form/form.go index bcc183ba..bb8b0147 100644 --- a/widgets/form/form.go +++ b/widgets/form/form.go @@ -240,6 +240,11 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { setting["config"] = dsl.Config for _, cProp := range dsl.CProps { err := cProp.Replace(setting, func(cProp component.CloudPropsDSL) interface{} { + + if cProp.Type == "Upload" { + return fmt.Sprintf("/api/__yao/form/%s%s", dsl.ID, cProp.UploadPath()) + } + return map[string]interface{}{ "api": fmt.Sprintf("/api/__yao/form/%s%s", dsl.ID, cProp.Path()), "params": cProp.Query, diff --git a/widgets/form/form_test.go b/widgets/form/form_test.go index 127d4b2c..5a1d25c9 100644 --- a/widgets/form/form_test.go +++ b/widgets/form/form_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/fs" "github.com/yaoapp/yao/i18n" "github.com/yaoapp/yao/model" "github.com/yaoapp/yao/runtime" @@ -36,6 +37,12 @@ func prepare(t *testing.T, language ...string) { i18n.Load(config.Conf) share.DBConnect(config.Conf.DB) // removed later + // load fs + err = fs.Load(config.Conf) + if err != nil { + t.Fatal(err) + } + // load scripts err = script.Load(config.Conf) if err != nil { diff --git a/widgets/form/process.go b/widgets/form/process.go index e279728c..3efa0c90 100644 --- a/widgets/form/process.go +++ b/widgets/form/process.go @@ -12,6 +12,7 @@ func exportProcess() { gou.RegisterProcessHandler("yao.form.setting", processSetting) gou.RegisterProcessHandler("yao.form.xgen", processXgen) gou.RegisterProcessHandler("yao.form.component", processComponent) + gou.RegisterProcessHandler("yao.form.upload", processUpload) gou.RegisterProcessHandler("yao.form.find", processFind) gou.RegisterProcessHandler("yao.form.save", processSave) gou.RegisterProcessHandler("yao.form.create", processCreate) @@ -59,6 +60,35 @@ func processComponent(process *gou.Process) interface{} { return res } +func processUpload(process *gou.Process) interface{} { + + process.ValidateArgNums(4) + form := MustGet(process) + xpath := process.ArgsString(1) + method := process.ArgsString(2) + key := fmt.Sprintf("%s.$%s", xpath, method) + + // get cloud props + cProp, has := form.CProps[key] + if !has { + exception.New("%s does not exist", 400, key).Throw() + } + + // $file.file + tmpfile, ok := process.Args[3].(gou.UploadFile) + if !ok { + exception.New("parameters error: %v", 400, process.Args[3]).Throw() + } + + // execute upload + res, err := cProp.ExecUpload(process, tmpfile) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + + return res +} + func processSetting(process *gou.Process) interface{} { form := MustGet(process) process.Args = append(process.Args, process.Args[0]) // formle name diff --git a/widgets/form/process_test.go b/widgets/form/process_test.go index 7bc61052..6bc6290f 100644 --- a/widgets/form/process_test.go +++ b/widgets/form/process_test.go @@ -3,6 +3,7 @@ package form import ( "fmt" "net/url" + "os" "testing" "github.com/stretchr/testify/assert" @@ -156,6 +157,27 @@ func TestProcessComponent(t *testing.T) { assert.Equal(t, "checked", pets[1]["status"]) } +func TestProcessUpload(t *testing.T) { + load(t) + clear(t) + testData(t) + args := []interface{}{ + "pet", + "fields.form.相关图片.edit.props", + "api", + gou.UploadFile{TempFile: tempFile(t)}, + } + + res, err := gou.NewProcess("yao.form.Upload", args...).Exec() + if err != nil { + t.Fatal(err) + } + + file, ok := res.(string) + assert.True(t, ok) + assert.NotEmpty(t, file) +} + func TestProcessComponentError(t *testing.T) { load(t) clear(t) @@ -182,6 +204,7 @@ func TestProcessSetting(t *testing.T) { data := any.Of(res).MapStr().Dot() assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/form/pet/upload/fields.form."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.form.相关图片.edit.props.api")) } func TestProcessXgen(t *testing.T) { @@ -196,6 +219,7 @@ func TestProcessXgen(t *testing.T) { data := any.Of(res).MapStr().Dot() assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/form/pet/upload/fields.form."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.form.相关图片.edit.props.api")) } func load(t *testing.T) { @@ -222,6 +246,21 @@ func testData(t *testing.T) { } } +func tempFile(t *testing.T) string { + file, err := os.CreateTemp("", "unit-test") + if err != nil { + t.Fatal(err) + } + defer file.Close() + + _, err = file.Write([]byte("HELLO")) + if err != nil { + t.Fatal(err) + } + + return file.Name() +} + func clear(t *testing.T) { for _, m := range gou.Models { err := m.DropTable() diff --git a/widgets/form/types.go b/widgets/form/types.go index 3f114248..4ecd6130 100644 --- a/widgets/form/types.go +++ b/widgets/form/types.go @@ -26,6 +26,7 @@ type ActionDSL struct { Bind *BindActionDSL `json:"bind,omitempty"` Setting *action.Process `json:"setting,omitempty"` Component *action.Process `json:"component,omitempty"` + Upload *action.Process `json:"upload,omitempty"` Find *action.Process `json:"find,omitempty"` Save *action.Process `json:"save,omitempty"` Update *action.Process `json:"update,omitempty"` diff --git a/widgets/table/api.go b/widgets/table/api.go index 759304e4..e09e6c66 100644 --- a/widgets/table/api.go +++ b/widgets/table/api.go @@ -154,10 +154,10 @@ func exportAPI() error { } http.Paths = append(http.Paths, path) - // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.table.Component $param.id $param.xpath $param.method $file.file + // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.table.Upload $param.id $param.xpath $param.method $file.file path = gou.Path{ Label: "Upload", - Description: "Component", + Description: "Upload", Path: "/:id/upload/:xpath/:method", Method: "POST", Process: "yao.table.Upload", diff --git a/widgets/table/process_test.go b/widgets/table/process_test.go index 3a9f25fe..214dc870 100644 --- a/widgets/table/process_test.go +++ b/widgets/table/process_test.go @@ -383,6 +383,7 @@ func TestProcessSetting(t *testing.T) { assert.Equal(t, "查看详情2", data.Get("header.preset.import.actions[1].title")) assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/upload/fields.table."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.table.相关图片.edit.props.api")) } func TestProcessXgen(t *testing.T) { @@ -401,6 +402,8 @@ func TestProcessXgen(t *testing.T) { assert.Equal(t, "查看详情2", data.Get("header.preset.import.actions[1].title")) assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/upload/fields.table."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.table.相关图片.edit.props.api")) + } func load(t *testing.T) {