From e08df986d37725abbf75341ff5619020563058e0 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 18 Oct 2021 23:15:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BD=BD=E5=85=A5=E9=80=BB?= =?UTF-8?q?=E8=BE=91:=20Step=201/5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- api/api.go | 26 +++++++++ api/api_test.go | 24 ++++++++ app/app.go | 87 +++++++++++++++++++++++++++ engine/load.go | 100 +++++--------------------------- engine/watch_test.go | 2 +- flow/flow.go | 4 ++ model/model.go | 4 ++ plugin/plugin.go | 4 ++ share/utils.go | 61 +++++++++++++++++++ tests/apis/group/team.http.json | 8 +++ 11 files changed, 235 insertions(+), 87 deletions(-) create mode 100644 api/api.go create mode 100644 api/api_test.go create mode 100644 app/app.go create mode 100644 flow/flow.go create mode 100644 model/model.go create mode 100644 plugin/plugin.go create mode 100644 share/utils.go create mode 100644 tests/apis/group/team.http.json diff --git a/Makefile b/Makefile index da307b0f..5fc241f1 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ GOFILES := $(shell find . -name "*.go") VERSION := $(shell grep 'const VERSION =' share/vars.go |awk '{print $$4}' |sed 's/\"//g') # ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -TESTFOLDER := $(shell $(GO) list ./... | grep -E 'xiang$$|engine$$|table$$|user$$|xfs$$' | grep -v examples) +TESTFOLDER := $(shell $(GO) list ./... | grep -vE 'examples|tests*|config') TESTTAGS ?= "" # 运行单元测试 diff --git a/api/api.go b/api/api.go new file mode 100644 index 00000000..14ea19cb --- /dev/null +++ b/api/api.go @@ -0,0 +1,26 @@ +package api + +import ( + "github.com/yaoapp/gou" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/share" +) + +// Load 加载API +func Load(cfg config.Config) { + LoadFrom(cfg.RootAPI, "") +} + +// 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) + gou.LoadAPI(string(content), prefix+name) + }) +} diff --git a/api/api_test.go b/api/api_test.go new file mode 100644 index 00000000..01d78c13 --- /dev/null +++ b/api/api_test.go @@ -0,0 +1,24 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou" + "github.com/yaoapp/xiang/config" +) + +func TestLoad(t *testing.T) { + gou.APIs = make(map[string]*gou.API) + Load(config.Conf) + LoadFrom("not a path", "404.") + check(t) +} + +func check(t *testing.T) { + keys := []string{} + for key := range gou.APIs { + keys = append(keys, key) + } + assert.Equal(t, 3, len(keys)) +} diff --git a/app/app.go b/app/app.go new file mode 100644 index 00000000..1a390f78 --- /dev/null +++ b/app/app.go @@ -0,0 +1,87 @@ +package app + +import ( + "log" + "os" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/maps" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/data" + "github.com/yaoapp/xiang/share" + "github.com/yaoapp/xiang/xfs" +) + +// Load 加载应用信息 +func Load(cfg config.Config) { + Init(cfg) + LoadInfo(cfg.Root) +} + +// Init 应用初始化 +func Init(cfg config.Config) { + if _, err := os.Stat(cfg.RootUI); os.IsNotExist(err) { + err := os.MkdirAll(cfg.RootUI, os.ModePerm) + if err != nil { + log.Panicf("创建目录失败(%s) %s", cfg.RootUI, err) + } + } + + if _, err := os.Stat(cfg.RootDB); os.IsNotExist(err) { + err := os.MkdirAll(cfg.RootDB, os.ModePerm) + if err != nil { + log.Panicf("创建目录失败(%s) %s", cfg.RootDB, err) + } + } + + if _, err := os.Stat(cfg.RootData); os.IsNotExist(err) { + err := os.MkdirAll(cfg.RootData, os.ModePerm) + if err != nil { + log.Panicf("创建目录失败(%s) %s", cfg.RootData, err) + } + } +} + +// LoadInfo 应用信息 +func LoadInfo(root string) { + info := defaultInfo() + fs := xfs.New(root) + if fs.MustExists("/app.json") { + err := jsoniter.Unmarshal(fs.MustReadFile("/app.json"), &info) + if err != nil { + exception.New("解析应用失败 %s", 500, err).Throw() + } + } + + if fs.MustExists("/xiang/icons/icon.icns") { + info.Icons.Set("icns", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.icns"))) + } + + if fs.MustExists("/xiang/icons/icon.ico") { + info.Icons.Set("ico", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.ico"))) + } + + if fs.MustExists("/xiang/icons/icon.png") { + info.Icons.Set("png", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.png"))) + } + + share.App = info +} + +// defaultInfo 读取默认应用信息 +func defaultInfo() share.AppInfo { + info := share.AppInfo{ + Icons: maps.MakeSync(), + } + err := jsoniter.Unmarshal(data.MustAsset("xiang/data/app.json"), &info) + if err != nil { + exception.New("解析默认应用失败 %s", 500, err).Throw() + } + + info.Icons.Set("icns", xfs.Encode(data.MustAsset("xiang/data/icons/icon.icns"))) + info.Icons.Set("ico", xfs.Encode(data.MustAsset("xiang/data/icons/icon.ico"))) + info.Icons.Set("png", xfs.Encode(data.MustAsset("xiang/data/icons/icon.png"))) + + return info +} diff --git a/engine/load.go b/engine/load.go index 691e6dec..0ba079d9 100644 --- a/engine/load.go +++ b/engine/load.go @@ -7,25 +7,22 @@ import ( "path/filepath" "strings" - jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/maps" + "github.com/yaoapp/xiang/api" + "github.com/yaoapp/xiang/app" "github.com/yaoapp/xiang/config" - "github.com/yaoapp/xiang/data" "github.com/yaoapp/xiang/share" "github.com/yaoapp/xiang/table" - "github.com/yaoapp/xiang/xfs" "github.com/yaoapp/xun/capsule" ) // Load 根据配置加载 API, FLow, Model, Plugin func Load(cfg config.Config) { - - AppInit(cfg) + app.Load(cfg) // 加载应用信息 DBConnect(cfg.Database) - LoadAppInfo(cfg.Root) LoadEngine(cfg.Path) + api.Load(cfg) // 加载API LoadApp(share.AppRoot{ APIs: cfg.RootAPI, Flows: cfg.RootFLow, @@ -42,49 +39,6 @@ func Load(cfg config.Config) { gou.LoadCrypt(`{}`, "PASSWORD") } -// LoadAppInfo 读取应用信息 -func LoadAppInfo(root string) { - info := defaultAppInfo() - fs := xfs.New(root) - if fs.MustExists("/app.json") { - err := jsoniter.Unmarshal(fs.MustReadFile("/app.json"), &info) - if err != nil { - exception.New("解析应用失败 %s", 500, err).Throw() - } - } - - if fs.MustExists("/xiang/icons/icon.icns") { - info.Icons.Set("icns", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.icns"))) - } - - if fs.MustExists("/xiang/icons/icon.ico") { - info.Icons.Set("ico", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.ico"))) - } - - if fs.MustExists("/xiang/icons/icon.png") { - info.Icons.Set("png", xfs.Encode(fs.MustReadFile("/xiang/icons/icon.png"))) - } - - share.App = info -} - -// defaultAppInfo 读取默认应用信息 -func defaultAppInfo() share.AppInfo { - info := share.AppInfo{ - Icons: maps.MakeSync(), - } - err := jsoniter.Unmarshal(data.MustAsset("xiang/data/app.json"), &info) - if err != nil { - exception.New("解析默认应用失败 %s", 500, err).Throw() - } - - info.Icons.Set("icns", xfs.Encode(data.MustAsset("xiang/data/icons/icon.icns"))) - info.Icons.Set("ico", xfs.Encode(data.MustAsset("xiang/data/icons/icon.ico"))) - info.Icons.Set("png", xfs.Encode(data.MustAsset("xiang/data/icons/icon.png"))) - - return info -} - // Reload 根据配置重新加载 API, FLow, Model, Plugin func Reload(cfg config.Config) { gou.APIs = map[string]*gou.API{} @@ -111,31 +65,6 @@ func DBConnect(dbconfig config.DatabaseConfig) { } } -// AppInit 应用初始化 -func AppInit(cfg config.Config) { - - if _, err := os.Stat(cfg.RootUI); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootUI, os.ModePerm) - if err != nil { - log.Panicf("创建目录失败(%s) %s", cfg.RootUI, err) - } - } - - if _, err := os.Stat(cfg.RootDB); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootDB, os.ModePerm) - if err != nil { - log.Panicf("创建目录失败(%s) %s", cfg.RootDB, err) - } - } - - if _, err := os.Stat(cfg.RootData); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootData, os.ModePerm) - if err != nil { - log.Panicf("创建目录失败(%s) %s", cfg.RootData, err) - } - } -} - // LoadEngine 加载引擎的 API, Flow, Model 配置 func LoadEngine(from string) { @@ -186,7 +115,8 @@ 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.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 @@ -204,15 +134,15 @@ func LoadApp(app share.AppRoot) { } } - // 加载API - if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") { - root := strings.TrimPrefix(app.APIs, "fs://") - scripts := share.GetAppFilesFS(root, ".json") - for _, script := range scripts { - // 验证API 加载逻辑 - gou.LoadAPI(string(script.Content), script.Name) - } - } + // // 加载API + // if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") { + // root := strings.TrimPrefix(app.APIs, "fs://") + // scripts := share.GetAppFilesFS(root, ".json") + // for _, script := range scripts { + // // 验证API 加载逻辑 + // gou.LoadAPI(string(script.Content), script.Name) + // } + // } // 加载Flow if strings.HasPrefix(app.Flows, "fs://") || !strings.Contains(app.Flows, "://") { diff --git a/engine/watch_test.go b/engine/watch_test.go index 736ace3f..ed47a4d8 100644 --- a/engine/watch_test.go +++ b/engine/watch_test.go @@ -12,7 +12,7 @@ import ( ) func TestWatch(t *testing.T) { - root := path.Join(config.Conf.Source, "/app/flows") + root := path.Join(config.Conf.Source, "/tests/flows") assert.NotPanics(t, func() { go share.Watch(root, func(op string, file string) { log.Println(op, file) diff --git a/flow/flow.go b/flow/flow.go new file mode 100644 index 00000000..0dd4d809 --- /dev/null +++ b/flow/flow.go @@ -0,0 +1,4 @@ +package flow + +// Load 加载业务逻辑编排 +func Load(filepath string) {} diff --git a/model/model.go b/model/model.go new file mode 100644 index 00000000..ddbba5f1 --- /dev/null +++ b/model/model.go @@ -0,0 +1,4 @@ +package model + +// Load 加载数据模型 +func Load(filepath string) {} diff --git a/plugin/plugin.go b/plugin/plugin.go new file mode 100644 index 00000000..53d0557d --- /dev/null +++ b/plugin/plugin.go @@ -0,0 +1,4 @@ +package plugin + +// Load 加载应用插件 +func Load(filepath string) {} diff --git a/share/utils.go b/share/utils.go new file mode 100644 index 00000000..17e6fa98 --- /dev/null +++ b/share/utils.go @@ -0,0 +1,61 @@ +package share + +import ( + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/yaoapp/kun/exception" +) + +// Walk 遍历应用目录,读取文件列表 +func Walk(root string, typeName string, cb func(root, filename string)) { + root = strings.TrimPrefix(root, "fs://") + root = strings.TrimPrefix(root, "file://") + root = path.Join(root, "/") + filepath.Walk(root, func(filename string, info os.FileInfo, err error) error { + if err != nil { + exception.Err(err, 500).Throw() + return err + } + if strings.HasSuffix(filename, typeName) { + cb(root, filename) + } + return nil + }) +} + +// SpecName 解析名称 root: "/tests/apis" file: "/tests/apis/foo/bar.http.json" +func SpecName(root string, file string) string { + filename := strings.TrimPrefix(file, root+"/") // "foo/bar.http.json" + namer := strings.Split(filename, ".") // ["foo/bar", "http", "json"] + nametypes := strings.Split(namer[0], "/") // ["foo", "bar"] + name := strings.Join(nametypes, ".") // "foo.bar" + return name +} + +// ReadFile 读取文件 +func ReadFile(filename string) []byte { + file, err := os.Open(filename) + if err != nil { + exception.Err(err, 500).Throw() + } + defer file.Close() + content, err := ioutil.ReadAll(file) + if err != nil { + exception.Err(err, 500).Throw() + } + return content +} + +// DirNotExists 校验目录是否存在 +func DirNotExists(dir string) bool { + dir = strings.TrimPrefix(dir, "fs://") + dir = strings.TrimPrefix(dir, "file://") + if _, err := os.Stat(dir); os.IsNotExist(err) { + return true + } + return false +} diff --git a/tests/apis/group/team.http.json b/tests/apis/group/team.http.json new file mode 100644 index 00000000..ee8cc4b5 --- /dev/null +++ b/tests/apis/group/team.http.json @@ -0,0 +1,8 @@ +{ + "name": "厂商接口", + "version": "1.0.0", + "description": "厂商API", + "group": "manu", + "guard": "bearer-jwt", + "paths": [] +}