优化载入逻辑: Step 1/5
This commit is contained in:
parent
1cedbc97b3
commit
e08df986d3
11 changed files with 235 additions and 87 deletions
2
Makefile
2
Makefile
|
|
@ -6,7 +6,7 @@ GOFILES := $(shell find . -name "*.go")
|
||||||
VERSION := $(shell grep 'const VERSION =' share/vars.go |awk '{print $$4}' |sed 's/\"//g')
|
VERSION := $(shell grep 'const VERSION =' share/vars.go |awk '{print $$4}' |sed 's/\"//g')
|
||||||
|
|
||||||
# ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
# 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 ?= ""
|
TESTTAGS ?= ""
|
||||||
|
|
||||||
# 运行单元测试
|
# 运行单元测试
|
||||||
|
|
|
||||||
26
api/api.go
Normal file
26
api/api.go
Normal file
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
24
api/api_test.go
Normal file
24
api/api_test.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
87
app/app.go
Normal file
87
app/app.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
100
engine/load.go
100
engine/load.go
|
|
@ -7,25 +7,22 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/yaoapp/gou"
|
"github.com/yaoapp/gou"
|
||||||
"github.com/yaoapp/kun/exception"
|
"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/config"
|
||||||
"github.com/yaoapp/xiang/data"
|
|
||||||
"github.com/yaoapp/xiang/share"
|
"github.com/yaoapp/xiang/share"
|
||||||
"github.com/yaoapp/xiang/table"
|
"github.com/yaoapp/xiang/table"
|
||||||
"github.com/yaoapp/xiang/xfs"
|
|
||||||
"github.com/yaoapp/xun/capsule"
|
"github.com/yaoapp/xun/capsule"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load 根据配置加载 API, FLow, Model, Plugin
|
// Load 根据配置加载 API, FLow, Model, Plugin
|
||||||
func Load(cfg config.Config) {
|
func Load(cfg config.Config) {
|
||||||
|
app.Load(cfg) // 加载应用信息
|
||||||
AppInit(cfg)
|
|
||||||
DBConnect(cfg.Database)
|
DBConnect(cfg.Database)
|
||||||
LoadAppInfo(cfg.Root)
|
|
||||||
LoadEngine(cfg.Path)
|
LoadEngine(cfg.Path)
|
||||||
|
api.Load(cfg) // 加载API
|
||||||
LoadApp(share.AppRoot{
|
LoadApp(share.AppRoot{
|
||||||
APIs: cfg.RootAPI,
|
APIs: cfg.RootAPI,
|
||||||
Flows: cfg.RootFLow,
|
Flows: cfg.RootFLow,
|
||||||
|
|
@ -42,49 +39,6 @@ func Load(cfg config.Config) {
|
||||||
gou.LoadCrypt(`{}`, "PASSWORD")
|
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
|
// Reload 根据配置重新加载 API, FLow, Model, Plugin
|
||||||
func Reload(cfg config.Config) {
|
func Reload(cfg config.Config) {
|
||||||
gou.APIs = map[string]*gou.API{}
|
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 配置
|
// LoadEngine 加载引擎的 API, Flow, Model 配置
|
||||||
func LoadEngine(from string) {
|
func LoadEngine(from string) {
|
||||||
|
|
||||||
|
|
@ -186,7 +115,8 @@ func LoadApp(app share.AppRoot) {
|
||||||
|
|
||||||
// api string, flow string, model string, plugin string
|
// 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 {
|
for _, p := range paths {
|
||||||
if !strings.HasPrefix(p, "fs://") && strings.Contains(p, "://") {
|
if !strings.HasPrefix(p, "fs://") && strings.Contains(p, "://") {
|
||||||
continue
|
continue
|
||||||
|
|
@ -204,15 +134,15 @@ func LoadApp(app share.AppRoot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载API
|
// // 加载API
|
||||||
if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") {
|
// if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") {
|
||||||
root := strings.TrimPrefix(app.APIs, "fs://")
|
// root := strings.TrimPrefix(app.APIs, "fs://")
|
||||||
scripts := share.GetAppFilesFS(root, ".json")
|
// scripts := share.GetAppFilesFS(root, ".json")
|
||||||
for _, script := range scripts {
|
// for _, script := range scripts {
|
||||||
// 验证API 加载逻辑
|
// // 验证API 加载逻辑
|
||||||
gou.LoadAPI(string(script.Content), script.Name)
|
// gou.LoadAPI(string(script.Content), script.Name)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 加载Flow
|
// 加载Flow
|
||||||
if strings.HasPrefix(app.Flows, "fs://") || !strings.Contains(app.Flows, "://") {
|
if strings.HasPrefix(app.Flows, "fs://") || !strings.Contains(app.Flows, "://") {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWatch(t *testing.T) {
|
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() {
|
assert.NotPanics(t, func() {
|
||||||
go share.Watch(root, func(op string, file string) {
|
go share.Watch(root, func(op string, file string) {
|
||||||
log.Println(op, file)
|
log.Println(op, file)
|
||||||
|
|
|
||||||
4
flow/flow.go
Normal file
4
flow/flow.go
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package flow
|
||||||
|
|
||||||
|
// Load 加载业务逻辑编排
|
||||||
|
func Load(filepath string) {}
|
||||||
4
model/model.go
Normal file
4
model/model.go
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
// Load 加载数据模型
|
||||||
|
func Load(filepath string) {}
|
||||||
4
plugin/plugin.go
Normal file
4
plugin/plugin.go
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
// Load 加载应用插件
|
||||||
|
func Load(filepath string) {}
|
||||||
61
share/utils.go
Normal file
61
share/utils.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
8
tests/apis/group/team.http.json
Normal file
8
tests/apis/group/team.http.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "厂商接口",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "厂商API",
|
||||||
|
"group": "manu",
|
||||||
|
"guard": "bearer-jwt",
|
||||||
|
"paths": []
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue