diff --git a/widgets/form/bind.go b/widgets/form/bind.go index 1930567f..82a02a48 100644 --- a/widgets/form/bind.go +++ b/widgets/form/bind.go @@ -91,7 +91,7 @@ func (dsl *DSL) bindTable() error { // Load table if _, has := table.Tables[id]; !has { - if err := table.LoadID(id, dsl.Root); err != nil { + if err := table.LoadID(id); err != nil { return err } } diff --git a/widgets/list/bind.go b/widgets/list/bind.go index c757b510..ff0bb65b 100644 --- a/widgets/list/bind.go +++ b/widgets/list/bind.go @@ -52,7 +52,7 @@ func (dsl *DSL) bindTable() error { // Load table if _, has := table.Tables[id]; !has { - if err := table.LoadID(id, dsl.Root); err != nil { + if err := table.LoadID(id); err != nil { return err } } diff --git a/widgets/table/bind.go b/widgets/table/bind.go index 52f7c4f7..08a2e6d7 100644 --- a/widgets/table/bind.go +++ b/widgets/table/bind.go @@ -64,7 +64,7 @@ func (dsl *DSL) bindTable() error { // Load table if _, has := Tables[id]; !has { - if err := LoadID(id, dsl.Root); err != nil { + if err := LoadID(id); err != nil { return err } } diff --git a/widgets/table/process.go b/widgets/table/process.go index 412f9feb..ca5eca73 100644 --- a/widgets/table/process.go +++ b/widgets/table/process.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/fs" "github.com/yaoapp/gou/model" gouProcess "github.com/yaoapp/gou/process" @@ -43,6 +44,10 @@ func exportProcess() { gouProcess.Register("yao.table.deletein", processDeleteIn) gouProcess.Register("yao.table.export", processExport) gouProcess.Register("yao.table.load", processLoad) + gouProcess.Register("yao.table.reload", processReload) + gouProcess.Register("yao.table.unload", processUnload) + gouProcess.Register("yao.table.read", processRead) + gouProcess.Register("yao.table.exists", processExists) } func processXgen(process *gouProcess.Process) interface{} { @@ -307,9 +312,21 @@ func processExport(process *gouProcess.Process) interface{} { return filename } -// processLoad yao.table.Load (:file) +// processLoad yao.table.Load table_name file func processLoad(process *gouProcess.Process) interface{} { process.ValidateArgNums(1) + // Load from source + if process.NumOfArgs() >= 3 { + id := process.ArgsString(0) + source := process.ArgsString(2) + _, err := LoadSourceSync([]byte(source), id) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return nil + } + + // Load from file file := process.ArgsString(0) if file == "" { exception.New("file is required", 400).Throw() @@ -318,3 +335,38 @@ func processLoad(process *gouProcess.Process) interface{} { file = strings.TrimPrefix(file, string(os.PathSeparator)) return LoadFileSync("tables", file) } + +// processReload yao.table.Reload table_name +func processReload(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + tab := MustGet(process) // 0 + _, err := tab.Reload() + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return nil +} + +func processUnload(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + Unload(process.ArgsString(0)) + return nil +} + +// processRead yao.table.Read table_name +func processRead(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + tab := MustGet(process) // 0 + source := map[string]interface{}{} + err := application.Parse(tab.file, tab.Read(), &source) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return source +} + +// processExists yao.table.Exists table_name +func processExists(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + return Exists(process.ArgsString(0)) +} diff --git a/widgets/table/process_test.go b/widgets/table/process_test.go index 28d724b4..d40d52df 100644 --- a/widgets/table/process_test.go +++ b/widgets/table/process_test.go @@ -15,7 +15,6 @@ import ( "github.com/yaoapp/kun/any" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/helper" - q "github.com/yaoapp/yao/query" "github.com/yaoapp/yao/test" ) @@ -578,13 +577,62 @@ func TestProcessExport(t *testing.T) { assert.Greater(t, size, 1000) } -func load(t *testing.T) { +func TestProcessLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) - err := Load(config.Conf) - if err != nil { - t.Fatal(err) - } - q.Load(config.Conf) + + source := `{ + "name": "Pet Admin Bind Model And Form", + "action": { + "bind": { "model": "pet", "option": { "form": "pet" } }, + "search": { + "guard": "-", + "process": "scripts.pet.Search", + "default": [null, 1, 5] + } + } + } + ` + args := []interface{}{"dynamic.pet", "/tables/dynamic/pet.tab.yao", source} + + // Load + assert.NotPanics(t, func() { + process.New("yao.table.Load", args...).Run() + }) + tab := MustGet("dynamic.pet") + assert.Equal(t, "Pet Admin Bind Model And Form", tab.Name) + assert.Equal(t, "pet", tab.Action.Bind.Model) + + // Exist + res := process.New("yao.table.Exists", "dynamic.pet").Run() + assert.True(t, res.(bool)) + + // Reload + assert.NotPanics(t, func() { + process.New("yao.table.Reload", "dynamic.pet").Run() + }) + tab = MustGet("dynamic.pet") + assert.Equal(t, "Pet Admin Bind Model And Form", tab.Name) + assert.Equal(t, "pet", tab.Action.Bind.Model) + + // Unload + assert.NotPanics(t, func() { + process.New("yao.table.Unload", "dynamic.pet").Run() + }) + res = process.New("yao.table.Exists", "dynamic.pet").Run() + assert.False(t, res.(bool)) +} + +func TestProcessRead(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + + res := process.New("yao.table.Read", "pet").Run() + assert.NotNil(t, res) + assert.Equal(t, "::Pet Admin", res.(map[string]interface{})["name"]) } func testData(t *testing.T) { diff --git a/widgets/table/table.go b/widgets/table/table.go index d0ba7236..b74f22a6 100644 --- a/widgets/table/table.go +++ b/widgets/table/table.go @@ -125,8 +125,13 @@ func Load(cfg config.Config) error { return err } +// Unload unload the table +func Unload(id string) { + delete(Tables, id) +} + // LoadID load table dsl by id -func LoadID(id string, root string) error { +func LoadID(id string) error { file := filepath.Join("tables", share.File(id, ".tab.yao")) if exists, _ := application.App.Exists(file); exists { @@ -162,31 +167,51 @@ func LoadFile(root string, file string) error { return err } - dsl := &DSL{ID: id} - err = application.Parse(file, data, dsl) - if err != nil { - return fmt.Errorf("[%s] %s", id, err.Error()) - } - - err = dsl.parse(id, root) + _, err = load(data, id, file) if err != nil { return err } - - Tables[id] = dsl return nil } -// parse parse table dsl source -func (dsl *DSL) parse(id string, root string) error { +// LoadSourceSync load table dsl by source +func LoadSourceSync(source []byte, id string) (*DSL, error) { + lock.Lock() + defer lock.Unlock() + return LoadSource(source, id) +} + +// LoadSource load table dsl by source +func LoadSource(source []byte, id string) (*DSL, error) { + file := filepath.Join("tables", share.File(id, ".tab.yao")) + return load(source, id, file) +} + +// LoadSource load table dsl by source +func load(source []byte, id string, file string) (*DSL, error) { + dsl := &DSL{ID: id, source: source, file: file} + err := application.Parse(file, source, dsl) + if err != nil { + return nil, fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id) + if err != nil { + return nil, err + } + + Tables[id] = dsl + return dsl, nil +} + +// parse parse table dsl source +func (dsl *DSL) parse(id string) error { - dsl.Root = root // remove next version if dsl.Action == nil { dsl.Action = &ActionDSL{} } dsl.Action.SetDefaultProcess() - if dsl.Layout == nil { dsl.Layout = &LayoutDSL{ Header: &HeaderLayoutDSL{ @@ -382,3 +407,19 @@ func (dsl *DSL) Actions() []component.ActionsExport { return res } + +// Reload reload the table +func (dsl *DSL) Reload() (*DSL, error) { + return LoadSourceSync(dsl.source, dsl.ID) +} + +// Read read the source +func (dsl *DSL) Read() []byte { + return dsl.source +} + +// Exists check the table exists +func Exists(id string) bool { + _, has := Tables[id] + return has +} diff --git a/widgets/table/table_test.go b/widgets/table/table_test.go index 89acbb8a..219e5518 100644 --- a/widgets/table/table_test.go +++ b/widgets/table/table_test.go @@ -1,7 +1,6 @@ package table import ( - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -29,18 +28,76 @@ func TestLoad(t *testing.T) { } func TestLoadID(t *testing.T) { - test.Prepare(t, config.Conf) defer test.Clean() prepare(t) - - err := LoadID("pet", filepath.Join(config.Conf.Root)) + err := LoadID("pet") if err != nil { t.Fatal(err) } } -func prepare(t *testing.T, language ...string) { +func TestLoadSourceSync(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + source := []byte(`{ + "name": "Pet Admin Bind Model And Form", + "action": { + "bind": { "model": "pet", "option": { "form": "pet" } }, + "search": { + "guard": "-", + "process": "scripts.pet.Search", + "default": [null, 1, 5] + } + } + } + `) + + tab, err := LoadSourceSync(source, `dynamic.pet`) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Pet Admin Bind Model And Form", tab.Name) + assert.Equal(t, "pet", tab.Action.Bind.Model) + assert.True(t, Exists("dynamic.pet")) + + // Reload + tab, err = tab.Reload() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Pet Admin Bind Model And Form", tab.Name) + assert.Equal(t, "pet", tab.Action.Bind.Model) + assert.True(t, Exists("dynamic.pet")) + + // Unload + Unload("dynamic.pet") + assert.False(t, Exists("dynamic.pet")) +} + +func TestRead(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + tab := MustGet("pet") + assert.NotNil(t, tab) + + // Read + source := tab.Read() + if source == nil { + t.Fatal("Read Error") + } + + tab, err := LoadSourceSync(source, `dynamic.pet`) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "::Pet Admin", tab.Name) +} + +func prepare(t *testing.T) { // test.Prepare(t, config.Conf) // defer test.Clean() diff --git a/widgets/table/types.go b/widgets/table/types.go index 3374b6cf..d7b74065 100644 --- a/widgets/table/types.go +++ b/widgets/table/types.go @@ -11,7 +11,7 @@ import ( // DSL the table DSL type DSL struct { - Root string `json:"-"` + // Root string `json:"-"` ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Action *ActionDSL `json:"action"` @@ -19,6 +19,8 @@ type DSL struct { Fields *FieldsDSL `json:"fields"` Config map[string]interface{} `json:"config,omitempty"` CProps field.CloudProps `json:"-"` + file string `json:"-"` + source []byte `json:"-"` compute.Computable *mapping.Mapping }