diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index a9a7e660..dd8e226b 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -11,6 +11,7 @@ env: XIANG_MODE: debug XIANG_SOURCE: ${{ github.WORKSPACE }} XIANG_ROOT: ${{ github.WORKSPACE }}/tests + XIANG_ROOT_PLUGIN: ${{ HOME }}/data/gou-unit/plugins # 服务配置 XIANG_SERVICE_DEBUG: true diff --git a/engine/load.go b/engine/load.go index 9419e236..2f45b349 100644 --- a/engine/load.go +++ b/engine/load.go @@ -2,9 +2,6 @@ package engine import ( "fmt" - "log" - "os" - "path/filepath" "strings" "github.com/yaoapp/gou" @@ -25,23 +22,24 @@ func Load(cfg config.Config) { share.DBConnect(cfg.Database) // 创建数据库连接 app.Load(cfg) // 加载应用信息 - LoadEngine(cfg.Path) + model.Load(cfg) // 加载数据模型 model api.Load(cfg) // 加载业务接口 API flow.Load(cfg) // 加载业务逻辑 Flow plugin.Load(cfg) // 加载业务插件 plugin + table.Load(cfg) // 加载数据表格 table - LoadApp(share.AppRoot{ - APIs: cfg.RootAPI, - Flows: cfg.RootFLow, - Models: cfg.RootModel, - Plugins: cfg.RootPlugin, - Tables: cfg.RootTable, - Charts: cfg.RootChart, - Screens: cfg.RootScreen, - Data: cfg.RootData, - }) + // LoadApp(share.AppRoot{ + // APIs: cfg.RootAPI, + // Flows: cfg.RootFLow, + // Models: cfg.RootModel, + // Plugins: cfg.RootPlugin, + // Tables: cfg.RootTable, + // Charts: cfg.RootChart, + // Screens: cfg.RootScreen, + // Data: cfg.RootData, + // }) // 加密密钥函数 gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.Database.AESKey), "AES") @@ -113,7 +111,7 @@ func LoadEngine(from string) { for _, script := range scripts { switch script.Type { case "tables": - table.Load(string(script.Content), "xiang."+script.Name) + table.LoadTable(string(script.Content), "xiang."+script.Name) break } } @@ -125,23 +123,23 @@ func LoadApp(app share.AppRoot) { // api string, flow string, model string, plugin string // 创建应用目录 // paths := []string{app.APIs, app.Flows, app.Models, app.Plugins, app.Charts, app.Tables, app.Screens, app.Data} - paths := []string{app.Flows, app.Models, app.Plugins, app.Charts, app.Tables, app.Screens, app.Data} - for _, p := range paths { - if !strings.HasPrefix(p, "fs://") && strings.Contains(p, "://") { - continue - } - root, err := filepath.Abs(strings.TrimPrefix(p, "fs://")) - if err != nil { - log.Panicf("创建目录失败(%s) %s", root, err) - } + // paths := []string{app.Flows, app.Models, app.Plugins, app.Charts, app.Tables, app.Screens, app.Data} + // for _, p := range paths { + // if !strings.HasPrefix(p, "fs://") && strings.Contains(p, "://") { + // continue + // } + // root, err := filepath.Abs(strings.TrimPrefix(p, "fs://")) + // if err != nil { + // log.Panicf("创建目录失败(%s) %s", root, err) + // } - if _, err := os.Stat(root); os.IsNotExist(err) { - err := os.MkdirAll(root, os.ModePerm) - if err != nil { - log.Panicf("创建目录失败(%s) %s", root, err) - } - } - } + // if _, err := os.Stat(root); os.IsNotExist(err) { + // err := os.MkdirAll(root, os.ModePerm) + // if err != nil { + // log.Panicf("创建目录失败(%s) %s", root, err) + // } + // } + // } // // 加载API // if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") { @@ -181,13 +179,13 @@ func LoadApp(app share.AppRoot) { // } // 加载Table - if strings.HasPrefix(app.Tables, "fs://") || !strings.Contains(app.Tables, "://") { - root := strings.TrimPrefix(app.Tables, "fs://") - scripts := share.GetAppFilesFS(root, ".json") - for _, script := range scripts { - // 验证API 加载逻辑 - table.Load(string(script.Content), script.Name) - } - } + // if strings.HasPrefix(app.Tables, "fs://") || !strings.Contains(app.Tables, "://") { + // root := strings.TrimPrefix(app.Tables, "fs://") + // scripts := share.GetAppFilesFS(root, ".json") + // for _, script := range scripts { + // // 验证API 加载逻辑 + // table.Load(string(script.Content), script.Name) + // } + // } } diff --git a/plugin/plugin.go b/plugin/plugin.go index 08adfeaf..9a5ae67b 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -6,7 +6,7 @@ import ( "github.com/yaoapp/xiang/share" ) -// Load 加载数据模型 +// Load 加载业务插件 func Load(cfg config.Config) { LoadFrom(cfg.RootPlugin, "") } diff --git a/service/service.go b/service/service.go index f13a9777..973a1a7b 100644 --- a/service/service.go +++ b/service/service.go @@ -149,7 +149,7 @@ func watchEngine(from string) { } if op == "write" || op == "create" { script := share.GetFile(root, file) - table.Load(string(script.Content), "xiang."+script.Name) // Reload + table.LoadTable(string(script.Content), "xiang."+script.Name) // Reload api, has := gou.APIs["xiang.table"] if has { api.Reload() @@ -202,7 +202,7 @@ func watchAppTable(rootTable string) { if op == "write" || op == "create" { script := share.GetAppFile(root, file) - table.Load(string(script.Content), script.Name) // Reload + table.LoadTable(string(script.Content), script.Name) // Reload api, has := gou.APIs["xiang.table"] if has { api.Reload() diff --git a/engine/table_test.go b/table/process_test.go similarity index 90% rename from engine/table_test.go rename to table/process_test.go index fa2f26c4..c1596bc3 100644 --- a/engine/table_test.go +++ b/table/process_test.go @@ -1,4 +1,4 @@ -package engine +package table import ( "testing" @@ -7,10 +7,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" "github.com/yaoapp/kun/any" - "github.com/yaoapp/xiang/table" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/model" + "github.com/yaoapp/xiang/share" "github.com/yaoapp/xun/capsule" ) +func init() { + share.DBConnect(config.Conf.Database) + model.Load(config.Conf) + Load(config.Conf) +} func TestTableProcessSearch(t *testing.T) { args := []interface{}{ @@ -25,7 +32,7 @@ func TestTableProcessSearch(t *testing.T) { &gin.Context{}, } process := gou.NewProcess("xiang.table.Search", args...) - response := table.ProcessSearch(process) + response := ProcessSearch(process) assert.NotNil(t, response) res := any.Of(response).Map() assert.True(t, res.Has("data")) @@ -46,7 +53,7 @@ func TestTableProcessFind(t *testing.T) { &gin.Context{}, } process := gou.NewProcess("xiang.table.Find", args...) - response := table.ProcessFind(process) + response := ProcessFind(process) assert.NotNil(t, response) res := any.Of(response).Map() assert.Equal(t, any.Of(res.Get("id")).CInt(), 1) @@ -64,7 +71,7 @@ func TestTableProcessSave(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -86,7 +93,7 @@ func TestTableProcessDelete(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -96,7 +103,7 @@ func TestTableProcessDelete(t *testing.T) { id, } process = gou.NewProcess("xiang.table.Delete", args...) - response = table.ProcessDelete(process) + response = ProcessDelete(process) assert.Nil(t, response) // 清空数据 @@ -113,7 +120,7 @@ func TestTableProcessInsert(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Insert", args...) - response := table.ProcessInsert(process) + response := ProcessInsert(process) assert.Nil(t, response) // 清空数据 @@ -132,7 +139,7 @@ func TestTableProcessDeleteWhere(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -146,7 +153,7 @@ func TestTableProcessDeleteWhere(t *testing.T) { }, } process = gou.NewProcess("xiang.table.DeleteWhere", args...) - response = table.ProcessDeleteWhere(process) + response = ProcessDeleteWhere(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -168,7 +175,7 @@ func TestTableProcessDeleteIn(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -179,7 +186,7 @@ func TestTableProcessDeleteIn(t *testing.T) { "id", } process = gou.NewProcess("xiang.table.DeleteIn", args...) - response = table.ProcessDeleteIn(process) + response = ProcessDeleteIn(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -201,7 +208,7 @@ func TestTableProcessUpdateWhere(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -218,7 +225,7 @@ func TestTableProcessUpdateWhere(t *testing.T) { }, } process = gou.NewProcess("xiang.table.UpdateWhere", args...) - response = table.ProcessUpdateWhere(process) + response = ProcessUpdateWhere(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -240,7 +247,7 @@ func TestTableProcessUpdateIn(t *testing.T) { }, } process := gou.NewProcess("xiang.table.Save", args...) - response := table.ProcessSave(process) + response := ProcessSave(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -254,7 +261,7 @@ func TestTableProcessUpdateIn(t *testing.T) { }, } process = gou.NewProcess("xiang.table.UpdateIn", args...) - response = table.ProcessUpdateIn(process) + response = ProcessUpdateIn(process) assert.NotNil(t, response) assert.True(t, any.Of(response).IsInt()) @@ -267,7 +274,7 @@ func TestTableProcessUpdateIn(t *testing.T) { func TestTableProcessSetting(t *testing.T) { args := []interface{}{"service", ""} process := gou.NewProcess("xiang.table.Setting", args...) - response := table.ProcessSetting(process) + response := ProcessSetting(process) assert.NotNil(t, response) res := any.Of(response).Map() assert.Equal(t, res.Get("name"), "云服务库") @@ -283,7 +290,7 @@ func TestTableProcessSetting(t *testing.T) { func TestTableProcessSettingList(t *testing.T) { args := []interface{}{"service", "list"} process := gou.NewProcess("xiang.table.Setting", args...) - response := table.ProcessSetting(process) + response := ProcessSetting(process) assert.NotNil(t, response) res := any.Of(response).Map() assert.True(t, res.Has("actions")) @@ -294,7 +301,7 @@ func TestTableProcessSettingList(t *testing.T) { func TestTableProcessSettingListEdit(t *testing.T) { args := []interface{}{"service", "list, edit"} process := gou.NewProcess("xiang.table.Setting", args...) - response := table.ProcessSetting(process) + response := ProcessSetting(process) assert.NotNil(t, response) res := any.Of(response).MapStr().Dot() assert.True(t, res.Has("list.actions")) diff --git a/table/table.go b/table/table.go index 139aae3c..33e6eb4c 100644 --- a/table/table.go +++ b/table/table.go @@ -9,14 +9,34 @@ import ( "github.com/yaoapp/gou" "github.com/yaoapp/gou/helper" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Tables 已载入模型 var Tables = map[string]*Table{} -// Load 载入数据表格 -func Load(source string, name string) *Table { +// Load 加载数据表格 +func Load(cfg config.Config) { + LoadFrom(cfg.RootTable, "") +} + +// LoadFrom 从特定目录加载 +func LoadFrom(dir string, prefix string) { + + if share.DirNotExists(dir) { + return + } + + share.Walk(dir, ".json", func(root, filename string) { + name := share.SpecName(root, filename) + content := share.ReadFile(filename) + LoadTable(string(content), name) + }) +} + +// LoadTable 载入数据表格 +func LoadTable(source string, name string) *Table { var input io.Reader = nil if strings.HasPrefix(source, "file://") || strings.HasPrefix(source, "fs://") { filename := strings.TrimPrefix(source, "file://") @@ -61,7 +81,7 @@ func Select(name string) *Table { // Reload 更新数据表格配置 func (table *Table) Reload() *Table { - *table = *Load(table.Source, table.Name) + *table = *LoadTable(table.Source, table.Name) return table } diff --git a/table/table_test.go b/table/table_test.go new file mode 100644 index 00000000..6258d3e2 --- /dev/null +++ b/table/table_test.go @@ -0,0 +1,29 @@ +package table + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/model" + "github.com/yaoapp/xiang/share" +) + +func TestLoad(t *testing.T) { + share.DBConnect(config.Conf.Database) + model.Load(config.Conf) + + Tables = make(map[string]*Table) + Load(config.Conf) + LoadFrom("not a path", "404.") + check(t) +} + +func check(t *testing.T) { + keys := []string{} + for key := range Tables { + keys = append(keys, key) + } + assert.Equal(t, 2, len(keys)) +} diff --git a/tests/tables/foo/bar.tab.json b/tests/tables/foo/bar.tab.json new file mode 100644 index 00000000..0cd473dc --- /dev/null +++ b/tests/tables/foo/bar.tab.json @@ -0,0 +1,119 @@ +{ + "name": "菜单", + "version": "1.0.0", + "decription": "菜单设置", + "bind": { + "model": "service" + }, + "apis": {}, + "columns": {}, + "filters": {}, + "list": { + "primary": "id", + "layout": { + "columns": [ + { + "name": "名称", + "width": "240px" + }, + { + "name": "父节点" + }, + { + "name": "图标" + }, + { + "name": "状态" + }, + { + "name": "路由" + }, + { + "name": "排列" + }, + { + "name": "block布局" + }, + { + "name": "显示" + } + ], + "filters": [ + { + "name": "名称", + "width": 6 + } + ] + }, + "actions": { + "create": { + "type": "button", + "props": { + "label": "新建菜单", + "icon": "fas fa-plus" + } + }, + "view": {}, + "edit": {}, + "import": {}, + "update": {}, + "delete": {}, + "insert": {}, + "updateWhere": {}, + "deleteWhere": {}, + "updateSelect": {}, + "deleteSelect": {}, + "pagination": {}, + "setting": {} + } + }, + "edit": { + "primary": "id", + "layout": { + "fieldset": [ + { + "title": "基础信息", + "description": "", + "columns": [ + { + "name": "名称", + "width": 6 + }, + { + "name": "图标", + "width": 6 + }, + { + "name": "路由", + "width": 6 + }, + { + "name": "排列", + "width": 6 + }, + { + "name": "block布局", + "width": 6 + }, + { + "name": "显示", + "width": 6 + } + ] + } + ] + }, + "actions": { + "cancel": {}, + "save": { + "type": "button", + "props": { + "label": "保存" + } + }, + "delete": {} + } + }, + "insert": {}, + "view": {} +}