From 4830c554417c38c1d62868dc2afd84ae1bca94b6 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 21 Sep 2022 00:03:04 +0800 Subject: [PATCH] [add] app & login widget --- .github/workflows/pr-test.yml | 14 +++ .github/workflows/unit-test.yml | 14 +++ Makefile | 30 ++++- lang/lang.go | 3 + share/utils.go | 5 + widgets/app/app.go | 193 ++++++++++++++++++++++++++++++++ widgets/app/app_test.go | 161 ++++++++++++++++++++++++++ widgets/app/types.go | 25 +++++ widgets/login/login.go | 133 ++++++++++++++++++++++ widgets/login/login_test.go | 124 ++++++++++++++++++++ widgets/login/types.go | 24 ++++ widgets/widgets.go | 24 ++++ widgets/widgets_test.go | 13 +++ 13 files changed, 762 insertions(+), 1 deletion(-) create mode 100644 widgets/app/app.go create mode 100644 widgets/app/app_test.go create mode 100644 widgets/app/types.go create mode 100644 widgets/login/login.go create mode 100644 widgets/login/login_test.go create mode 100644 widgets/login/types.go create mode 100644 widgets/widgets.go create mode 100644 widgets/widgets_test.go diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index f5ddb517..f5983e75 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -38,6 +38,8 @@ env: MONGO_TEST_USER: "root" MONGO_TEST_PASS: "123456" + DEMO_APP: ${{ github.WORKSPACE }}/../app + jobs: UnitTest: runs-on: ubuntu-latest @@ -130,12 +132,19 @@ jobs: ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8 path: v8go + - name: Checkout Demo App + uses: actions/checkout@v2 + with: + repository: yaoapp/demo-app + path: app + - name: Move Kun, Xun, Gou, V8Go run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ + mv app ../ ls -l . ls -l ../ @@ -218,6 +227,11 @@ jobs: run: | make test + - name: Run widgets test + run: | + echo "YAO_ROOT=$DEMO_APP" >> $GITHUB_ENV + make test-widgets + - name: Codecov Report uses: codecov/codecov-action@v2 with: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 37917776..65f100d4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -42,6 +42,8 @@ env: MONGO_TEST_USER: "root" MONGO_TEST_PASS: "123456" + DEMO_APP: ${{ github.WORKSPACE }}/../app + jobs: unit-test: runs-on: ubuntu-latest @@ -87,12 +89,19 @@ jobs: ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8 path: v8go + - name: Checkout Demo App + uses: actions/checkout@v2 + with: + repository: yaoapp/demo-app + path: app + - name: Move Kun, Xun, Gou, V8Go run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ + mv app ../ ls -l . ls -l ../ @@ -173,6 +182,11 @@ jobs: run: | make test + - name: Run widgets test + run: | + echo "YAO_ROOT=$DEMO_APP" >> $GITHUB_ENV + make test-widgets + - name: Codecov Report uses: codecov/codecov-action@v2 with: diff --git a/Makefile b/Makefile index abcea79b..791b696f 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,11 @@ VERSION := $(shell grep 'const VERSION =' share/const.go |awk '{print $$4}' |sed COMMIT := $(shell git log | head -n 1 | awk '{print substr($$2, 0, 12)}') # ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -TESTFOLDER := $(shell $(GO) list ./... | grep -vE 'examples|tests*|config') +TESTFOLDER := $(shell $(GO) list ./... | grep -vE 'examples|tests*|config|widgets') TESTTAGS ?= "" +TESTWIDGETS := $(shell $(GO) list ./widgets/...) + # Unit Test .PHONY: test test: @@ -37,6 +39,32 @@ test: fi; \ done +# Unit Test +.PHONY: test-widgets +test-widgets: + echo "mode: count" > coverage.out + for d in $(TESTWIDGETS); do \ + $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out -coverpkg=$$(echo $$d | sed "s/\/test$$//g") $$d > tmp.out; \ + cat tmp.out; \ + if grep -q "^--- FAIL" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + elif grep -q "build failed" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + elif grep -q "setup failed" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + elif grep -q "runtime error" tmp.out; then \ + rm tmp.out; \ + exit 1; \ + fi; \ + if [ -f profile.out ]; then \ + cat profile.out | grep -v "mode:" >> coverage.out; \ + rm profile.out; \ + fi; \ + done + .PHONY: fmt fmt: $(GOFMT) -w $(GOFILES) diff --git a/lang/lang.go b/lang/lang.go index ddf950f1..382861c9 100644 --- a/lang/lang.go +++ b/lang/lang.go @@ -15,6 +15,7 @@ func init() { lang.RegisterWidget("kanban", "page") lang.RegisterWidget("screen", "page") lang.RegisterWidget("pages", "page") + lang.RegisterWidget("logins", "login") } // Load language packs @@ -25,6 +26,8 @@ func Load(cfg config.Config) error { return err } + // Set default + lang.Pick("default").AsDefault() name := os.Getenv("YAO_LANG") if name != "" { if _, has := lang.Dicts[name]; !has { diff --git a/share/utils.go b/share/utils.go index 989bc203..1a6f9d7c 100644 --- a/share/utils.go +++ b/share/utils.go @@ -30,6 +30,11 @@ func Walk(root string, typeName string, cb func(root, filename string)) error { return err } +// ID parse unique name root: "/tests/apis" file: "/tests/apis/foo/bar.http.json" +func ID(root string, file string) string { + return SpecName(root, file) +} + // 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" diff --git a/widgets/app/app.go b/widgets/app/app.go new file mode 100644 index 00000000..a311bb76 --- /dev/null +++ b/widgets/app/app.go @@ -0,0 +1,193 @@ +package app + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou" + "github.com/yaoapp/gou/lang" + "github.com/yaoapp/kun/exception" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/widgets/login" +) + +// +// API: +// GET /api/__yao/app/setting -> Default process: yao.app.Xgen +// GET /api/__yao/app/menu -> Default process: yao.app.Menu +// +// Process: +// yao.app.Setting Return the App DSL +// yao.app.Xgen Return the Xgen setting ( merge app & login ) +// yao.app.Menu Return the menu list +// + +// Setting the application setting +var Setting *DSL + +// Load the app DSL +func Load(cfg config.Config) error { + + file := filepath.Join(cfg.Root, "app.json") + file, err := filepath.Abs(file) + if err != nil { + return err + } + + data, err := ioutil.ReadFile(file) + if err != nil { + return err + } + + dsl := &DSL{} + err = jsoniter.Unmarshal(data, dsl) + if err != nil { + return nil + } + + // Apply a language pack + if lang.Default != nil { + lang.Default.Apply(dsl) + } + + Setting = dsl + return nil +} + +// exportAPI export login api +func exportAPI() error { + + if Setting == nil { + return fmt.Errorf("the app does not init") + } + + http := gou.HTTP{ + Name: "Widget App API", + Description: "Widget App API", + Version: share.VERSION, + Guard: "bearer-jwt", + Group: "__yao/app", + Paths: []gou.Path{}, + } + + process := "yao.app.Xgen" + if Setting.Optional.Setting != "" { + process = Setting.Optional.Setting + } + + path := gou.Path{ + Label: "App Setting", + Description: "App Setting", + Guard: "-", + Path: "/setting", + Method: "GET", + Process: process, + In: []string{}, + Out: gou.Out{Status: 200, Type: "application/json"}, + } + http.Paths = append(http.Paths, path) + + process = "yao.app.Menu" + args := []string{} + if Setting.Menu.Process != "" { + process = Setting.Menu.Process + args = Setting.Menu.Args + } + + path = gou.Path{ + Label: "App Menu", + Description: "App Menu", + Path: "/menu", + Method: "GET", + Process: process, + In: args, + Out: gou.Out{Status: 200, Type: "application/json"}, + } + http.Paths = append(http.Paths, path) + + // api source + source, err := jsoniter.Marshal(http) + if err != nil { + return err + } + + // load apis + _, err = gou.LoadAPIReturn(string(source), "widgets.app") + return err +} + +// Export export login api +func Export() error { + exportProcess() + return exportAPI() +} + +func exportProcess() { + gou.RegisterProcessHandler("yao.app.setting", processSetting) + gou.RegisterProcessHandler("yao.app.xgen", processXgen) + gou.AliasProcess("flows.xiang.menu", "yao.app.menu") +} + +func processSetting(process *gou.Process) interface{} { + if Setting == nil { + exception.New("the app does not init", 500).Throw() + return nil + } + return *Setting +} + +func processXgen(process *gou.Process) interface{} { + + if Setting == nil { + exception.New("the app does not init", 500).Throw() + } + + mode := os.Getenv("YAO_ENV") + if mode == "" { + mode = "production" + } + + xgenLogin := map[string]map[string]interface{}{ + "entry": {"admin": "/x/Welcome"}, + } + + if admin, has := login.Logins["admin"]; has { + xgenLogin["entry"]["admin"] = admin.Layout.Entry + xgenLogin["admin"] = map[string]interface{}{ + "captcha": "/api/__yao/login/admin/captcha", + "login": "/api/__yao/login/admin", + } + } + + if user, has := login.Logins["user"]; has { + xgenLogin["entry"]["user"] = user.Layout.Entry + xgenLogin["user"] = map[string]interface{}{ + "captcha": "/api/__yao/login/user/captcha", + "login": "/api/__yao/login/user", + } + } + + xgenSetting := map[string]interface{}{ + "name": Setting.Name, + "description": Setting.Description, + "mode": mode, + "apiPrefix": "__yao", + "token": "localStorage", + "optional": Setting.Optional, + "login": xgenLogin, + } + + return xgenSetting +} + +// Lang for applying a language pack +func (dsl *DSL) Lang(trans func(widget string, inst string, value *string) bool) { + widget := "app" + trans(widget, "app", &dsl.Name) + trans(widget, "app", &dsl.Short) + trans(widget, "app", &dsl.Description) +} diff --git a/widgets/app/app_test.go b/widgets/app/app_test.go new file mode 100644 index 00000000..3491b6e2 --- /dev/null +++ b/widgets/app/app_test.go @@ -0,0 +1,161 @@ +package app + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/lang" + "github.com/yaoapp/yao/widgets/login" +) + +func TestLoad(t *testing.T) { + + lang.Load(config.Conf) + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "Demo Application", Setting.Name) + assert.Equal(t, "Demo", Setting.Short) + assert.Equal(t, "Another yao application", Setting.Description) + assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, "flows.app.menu", Setting.Menu.Process) + assert.Equal(t, true, Setting.Optional.HideNotification) + assert.Equal(t, false, Setting.Optional.HideSetting) +} + +func TestLoadHK(t *testing.T) { + + os.Setenv("YAO_LANG", "zh-hk") + lang.Load(config.Conf) + + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "示例應用", Setting.Name) + assert.Equal(t, "演示", Setting.Short) + assert.Equal(t, "又一個YAO應用", Setting.Description) + assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, "flows.app.menu", Setting.Menu.Process) + assert.Equal(t, true, Setting.Optional.HideNotification) + assert.Equal(t, false, Setting.Optional.HideSetting) +} + +func TestLoadCN(t *testing.T) { + + os.Setenv("YAO_LANG", "zh-cn") + lang.Load(config.Conf) + + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "示例应用", Setting.Name) + assert.Equal(t, "演示", Setting.Short) + assert.Equal(t, "又一个 YAO 应用", Setting.Description) + assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, "flows.app.menu", Setting.Menu.Process) + assert.Equal(t, true, Setting.Optional.HideNotification) + assert.Equal(t, false, Setting.Optional.HideSetting) +} + +func TestExport(t *testing.T) { + + err := login.Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + err = Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + err = Export() + if err != nil { + t.Fatal(err) + } + + api, has := gou.APIs["widgets.app"] + assert.True(t, has) + assert.Equal(t, 2, len(api.HTTP.Paths)) + + _, has = gou.ThirdHandlers["yao.app.setting"] + assert.True(t, has) + + _, has = gou.ThirdHandlers["yao.app.xgen"] + assert.True(t, has) + + _, has = gou.ThirdHandlers["yao.app.menu"] + assert.True(t, has) +} + +func TestProcessSetting(t *testing.T) { + loadApp(t) + res, err := gou.NewProcess("yao.app.Setting").Exec() + if err != nil { + t.Fatal(err) + } + + setting, ok := res.(DSL) + assert.True(t, ok) + assert.Equal(t, "Demo Application", setting.Name) + assert.Equal(t, "Demo", setting.Short) + assert.Equal(t, "Another yao application", setting.Description) + assert.Equal(t, []string{"demo"}, setting.Menu.Args) + assert.Equal(t, "flows.app.menu", setting.Menu.Process) + assert.Equal(t, true, setting.Optional.HideNotification) + assert.Equal(t, false, setting.Optional.HideSetting) +} + +func TestProcessXgen(t *testing.T) { + loadApp(t) + res, err := gou.NewProcess("yao.app.Xgen").Exec() + if err != nil { + t.Fatal(err) + } + + xgen := any.Of(res).MapStr().Dot() + assert.Equal(t, "__yao", xgen.Get("apiPrefix")) + assert.Equal(t, "Another yao application", xgen.Get("description")) + assert.Equal(t, "/api/__yao/login/admin/captcha", xgen.Get("login.admin.captcha")) + assert.Equal(t, "/api/__yao/login/admin", xgen.Get("login.admin.login")) + assert.Equal(t, "/x/Table/pet", xgen.Get("login.entry.admin")) + assert.Equal(t, "/x/Table/dash", xgen.Get("login.entry.user")) + assert.Equal(t, "/api/__yao/login/user/captcha", xgen.Get("login.user.captcha")) + assert.Equal(t, "/api/__yao/login/user", xgen.Get("login.user.login")) + assert.Equal(t, os.Getenv("YAO_ENV"), xgen.Get("mode")) + assert.Equal(t, "Demo Application", xgen.Get("name")) + assert.Equal(t, true, xgen.Get("optional.hideNotification")) + assert.Equal(t, "localStorage", xgen.Get("token")) +} + +func loadApp(t *testing.T) { + + os.Setenv("YAO_LANG", "") + lang.Load(config.Conf) + + err := login.Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + err = Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + err = Export() + if err != nil { + t.Fatal(err) + } + +} diff --git a/widgets/app/types.go b/widgets/app/types.go new file mode 100644 index 00000000..8fe1997a --- /dev/null +++ b/widgets/app/types.go @@ -0,0 +1,25 @@ +package app + +// DSL the app DSL +type DSL struct { + Name string `json:"name,omitempty"` + Short string `json:"short,omitempty"` + Version string `json:"version,omitempty"` + Description string `json:"description,omitempty"` + Theme string `json:"theme,omitempty"` + Menu MenuDSL `json:"menu,omitempty"` + Optional OptionalDSL `json:"optional,omitempty"` +} + +// MenuDSL the menu DSL +type MenuDSL struct { + Process string `json:"process,omitempty"` + Args []string `json:"args,omitempty"` +} + +// OptionalDSL the Optional DSL +type OptionalDSL struct { + HideNotification bool `json:"hideNotification,omitempty"` + HideSetting bool `json:"hideSetting,omitempty"` + Setting string `json:"setting,omitempty"` // custom setting process +} diff --git a/widgets/login/login.go b/widgets/login/login.go new file mode 100644 index 00000000..55697efb --- /dev/null +++ b/widgets/login/login.go @@ -0,0 +1,133 @@ +package login + +import ( + "fmt" + "path/filepath" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou" + "github.com/yaoapp/gou/lang" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/share" +) + +// +// API: +// GET /api/__yao/login/:id/captcha -> Default process: yao.utils.Captcha :query +// POST /api/__yao/login/:id -> Default process: yao.user.Login :payload +// + +// Logins the loaded login widgets +var Logins map[string]*DSL = map[string]*DSL{} + +// Load load task +func Load(cfg config.Config) error { + var root = filepath.Join(cfg.Root, "logins") + return LoadFrom(root, "") +} + +// LoadFrom load from dir +func LoadFrom(dir string, prefix string) error { + + if share.DirNotExists(dir) { + return fmt.Errorf("%s does not exists", dir) + } + + messages := []string{} + err := share.Walk(dir, ".json", func(root, filename string) { + id := prefix + share.ID(root, filename) + data := share.ReadFile(filename) + dsl := &DSL{ID: id} + err := jsoniter.Unmarshal(data, dsl) + if err != nil { + messages = append(messages, err.Error()) + } + + // Apply a language pack + if lang.Default != nil { + lang.Default.Apply(dsl) + } + + Logins[id] = dsl + }) + + return err +} + +// Export export login api +func Export() error { + return exportAPI() +} + +// exportAPI export login api +func exportAPI() error { + + http := gou.HTTP{ + Name: "Widget Login API", + Description: "Widget Login API", + Version: share.VERSION, + Guard: "bearer-jwt", + Group: "__yao/login", + Paths: []gou.Path{}, + } + + for _, dsl := range Logins { + + // login action + process := "yao.user.Login" + args := []string{":payload"} + if dsl.Action.Process != "" { + process = dsl.Action.Process + args = dsl.Action.Args + } + path := gou.Path{ + Label: fmt.Sprintf("%s login", dsl.ID), + Description: fmt.Sprintf("%s login", dsl.ID), + Guard: "-", + Path: fmt.Sprintf("/%s", dsl.ID), + Method: "POST", + Process: process, + In: args, + Out: gou.Out{Status: 200, Type: "application/json"}, + } + http.Paths = append(http.Paths, path) + + // captcha + process = "yao.utils.Captcha" + args = []string{":query"} + if dsl.Layout.Captcha != "" { + process = dsl.Layout.Captcha + } + + path = gou.Path{ + Label: fmt.Sprintf("%s captcha", dsl.ID), + Description: fmt.Sprintf("%s captcha", dsl.ID), + Guard: "-", + Path: fmt.Sprintf("/%s/captcha", dsl.ID), + Method: "GET", + Process: process, + In: args, + Out: gou.Out{Status: 200, Type: "application/json"}, + } + http.Paths = append(http.Paths, path) + + } + + // api source + source, err := jsoniter.Marshal(http) + if err != nil { + return err + } + + // load apis + _, err = gou.LoadAPIReturn(string(source), "widgets.login") + return err +} + +// Lang for applying a language pack +func (dsl *DSL) Lang(trans func(widget string, inst string, value *string) bool) { + widget := "login" + trans(widget, dsl.ID, &dsl.Name) + trans(widget, dsl.ID, &dsl.Layout.Slogan) + trans(widget, dsl.ID, &dsl.Layout.Site) +} diff --git a/widgets/login/login_test.go b/widgets/login/login_test.go new file mode 100644 index 00000000..b00f90be --- /dev/null +++ b/widgets/login/login_test.go @@ -0,0 +1,124 @@ +package login + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/yaoapp/gou" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/lang" +) + +func TestLoad(t *testing.T) { + + lang.Load(config.Conf) + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, 2, len(Logins)) + + assert.Equal(t, "admin", Logins["admin"].ID) + assert.Equal(t, "Admin Login", Logins["admin"].Name) + assert.Equal(t, "yao.user.login", Logins["admin"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["admin"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["admin"].Layout.Captcha) + assert.Equal(t, "/images/admin-cover.png", Logins["admin"].Layout.Cover) + assert.Equal(t, "/x/Table/pet", Logins["admin"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com", Logins["admin"].Layout.Site) + assert.Equal(t, "Make Your Dream With Yao App Engine", Logins["admin"].Layout.Slogan) + + assert.Equal(t, "user", Logins["user"].ID) + assert.Equal(t, "User Login", Logins["user"].Name) + assert.Equal(t, "scripts.user.Login", Logins["user"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["user"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["user"].Layout.Captcha) + assert.Equal(t, "/images/user-cover.png", Logins["user"].Layout.Cover) + assert.Equal(t, "/x/Table/dash", Logins["user"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com/docs", Logins["user"].Layout.Site) + assert.Equal(t, "Make Your Dream With Yao App Engine", Logins["user"].Layout.Slogan) +} + +func TestLoadHK(t *testing.T) { + + os.Setenv("YAO_LANG", "zh-hk") + lang.Load(config.Conf) + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, 2, len(Logins)) + + assert.Equal(t, "admin", Logins["admin"].ID) + assert.Equal(t, "管理員登錄", Logins["admin"].Name) + assert.Equal(t, "yao.user.login", Logins["admin"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["admin"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["admin"].Layout.Captcha) + assert.Equal(t, "/images/admin-cover.png", Logins["admin"].Layout.Cover) + assert.Equal(t, "/x/Table/pet", Logins["admin"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com", Logins["admin"].Layout.Site) + assert.Equal(t, "夢想讓我們與眾不同", Logins["admin"].Layout.Slogan) + + assert.Equal(t, "user", Logins["user"].ID) + assert.Equal(t, "用戶登錄", Logins["user"].Name) + assert.Equal(t, "scripts.user.Login", Logins["user"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["user"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["user"].Layout.Captcha) + assert.Equal(t, "/images/user-cover.png", Logins["user"].Layout.Cover) + assert.Equal(t, "/x/Table/dash", Logins["user"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com/docs", Logins["user"].Layout.Site) + assert.Equal(t, "夢想讓我們與眾不同", Logins["user"].Layout.Slogan) +} + +func TestLoadCN(t *testing.T) { + + os.Setenv("YAO_LANG", "zh-cn") + lang.Load(config.Conf) + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, 2, len(Logins)) + + assert.Equal(t, "admin", Logins["admin"].ID) + assert.Equal(t, "管理员登录", Logins["admin"].Name) + assert.Equal(t, "yao.user.login", Logins["admin"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["admin"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["admin"].Layout.Captcha) + assert.Equal(t, "/images/admin-cover.png", Logins["admin"].Layout.Cover) + assert.Equal(t, "/x/Table/pet", Logins["admin"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com", Logins["admin"].Layout.Site) + assert.Equal(t, "梦想让我们与众不同", Logins["admin"].Layout.Slogan) + + assert.Equal(t, "user", Logins["user"].ID) + assert.Equal(t, "用户登录", Logins["user"].Name) + assert.Equal(t, "scripts.user.Login", Logins["user"].Action.Process) + assert.Equal(t, []string{":payload"}, Logins["user"].Action.Args) + assert.Equal(t, "yao.utils.Captcha", Logins["user"].Layout.Captcha) + assert.Equal(t, "/images/user-cover.png", Logins["user"].Layout.Cover) + assert.Equal(t, "/x/Table/dash", Logins["user"].Layout.Entry) + assert.Equal(t, "https://yaoapps.com/docs", Logins["user"].Layout.Site) + assert.Equal(t, "梦想让我们与众不同", Logins["user"].Layout.Slogan) +} + +func TestExport(t *testing.T) { + + err := Load(config.Conf) + if err != nil { + t.Fatal(err) + } + + err = Export() + if err != nil { + t.Fatal(err) + } + + api, has := gou.APIs["widgets.login"] + assert.True(t, has) + assert.Equal(t, 4, len(api.HTTP.Paths)) +} diff --git a/widgets/login/types.go b/widgets/login/types.go new file mode 100644 index 00000000..3194c716 --- /dev/null +++ b/widgets/login/types.go @@ -0,0 +1,24 @@ +package login + +// DSL the app DSL +type DSL struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Action ActionDSL `json:"action,omitempty"` + Layout LayoutDSL `json:"layout,omitempty"` +} + +// ActionDSL the login action DSL +type ActionDSL struct { + Process string `json:"process,omitempty"` + Args []string `json:"args,omitempty"` +} + +// LayoutDSL the login page layoutDSL +type LayoutDSL struct { + Entry string `json:"entry,omitempty"` + Captcha string `json:"captcha,omitempty"` + Cover string `json:"cover,omitempty"` + Slogan string `json:"slogan,omitempty"` + Site string `json:"site,omitempty"` +} diff --git a/widgets/widgets.go b/widgets/widgets.go new file mode 100644 index 00000000..e7465231 --- /dev/null +++ b/widgets/widgets.go @@ -0,0 +1,24 @@ +package widgets + +import ( + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/widgets/app" + "github.com/yaoapp/yao/widgets/login" +) + +// Load the widgets +func Load(cfg config.Config) error { + + err := login.Load(cfg) + if err != nil { + return err + } + + // app login + err = app.Load(cfg) + if err != nil { + return err + } + + return nil +} diff --git a/widgets/widgets_test.go b/widgets/widgets_test.go new file mode 100644 index 00000000..f992f3cd --- /dev/null +++ b/widgets/widgets_test.go @@ -0,0 +1,13 @@ +package widgets + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/yao/config" +) + +func TestLoad(t *testing.T) { + err := Load(config.Conf) + assert.Nil(t, err) +}