[add] app & login widget
This commit is contained in:
parent
95f7cc026d
commit
4830c55441
13 changed files with 762 additions and 1 deletions
14
.github/workflows/pr-test.yml
vendored
14
.github/workflows/pr-test.yml
vendored
|
|
@ -38,6 +38,8 @@ env:
|
||||||
MONGO_TEST_USER: "root"
|
MONGO_TEST_USER: "root"
|
||||||
MONGO_TEST_PASS: "123456"
|
MONGO_TEST_PASS: "123456"
|
||||||
|
|
||||||
|
DEMO_APP: ${{ github.WORKSPACE }}/../app
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
UnitTest:
|
UnitTest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -130,12 +132,19 @@ jobs:
|
||||||
ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8
|
ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8
|
||||||
path: v8go
|
path: v8go
|
||||||
|
|
||||||
|
- name: Checkout Demo App
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: yaoapp/demo-app
|
||||||
|
path: app
|
||||||
|
|
||||||
- name: Move Kun, Xun, Gou, V8Go
|
- name: Move Kun, Xun, Gou, V8Go
|
||||||
run: |
|
run: |
|
||||||
mv kun ../
|
mv kun ../
|
||||||
mv xun ../
|
mv xun ../
|
||||||
mv gou ../
|
mv gou ../
|
||||||
mv v8go ../
|
mv v8go ../
|
||||||
|
mv app ../
|
||||||
ls -l .
|
ls -l .
|
||||||
ls -l ../
|
ls -l ../
|
||||||
|
|
||||||
|
|
@ -218,6 +227,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
make test
|
make test
|
||||||
|
|
||||||
|
- name: Run widgets test
|
||||||
|
run: |
|
||||||
|
echo "YAO_ROOT=$DEMO_APP" >> $GITHUB_ENV
|
||||||
|
make test-widgets
|
||||||
|
|
||||||
- name: Codecov Report
|
- name: Codecov Report
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
14
.github/workflows/unit-test.yml
vendored
14
.github/workflows/unit-test.yml
vendored
|
|
@ -42,6 +42,8 @@ env:
|
||||||
MONGO_TEST_USER: "root"
|
MONGO_TEST_USER: "root"
|
||||||
MONGO_TEST_PASS: "123456"
|
MONGO_TEST_PASS: "123456"
|
||||||
|
|
||||||
|
DEMO_APP: ${{ github.WORKSPACE }}/../app
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unit-test:
|
unit-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -87,12 +89,19 @@ jobs:
|
||||||
ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8
|
ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8
|
||||||
path: v8go
|
path: v8go
|
||||||
|
|
||||||
|
- name: Checkout Demo App
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: yaoapp/demo-app
|
||||||
|
path: app
|
||||||
|
|
||||||
- name: Move Kun, Xun, Gou, V8Go
|
- name: Move Kun, Xun, Gou, V8Go
|
||||||
run: |
|
run: |
|
||||||
mv kun ../
|
mv kun ../
|
||||||
mv xun ../
|
mv xun ../
|
||||||
mv gou ../
|
mv gou ../
|
||||||
mv v8go ../
|
mv v8go ../
|
||||||
|
mv app ../
|
||||||
ls -l .
|
ls -l .
|
||||||
ls -l ../
|
ls -l ../
|
||||||
|
|
||||||
|
|
@ -173,6 +182,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
make test
|
make test
|
||||||
|
|
||||||
|
- name: Run widgets test
|
||||||
|
run: |
|
||||||
|
echo "YAO_ROOT=$DEMO_APP" >> $GITHUB_ENV
|
||||||
|
make test-widgets
|
||||||
|
|
||||||
- name: Codecov Report
|
- name: Codecov Report
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
30
Makefile
30
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)}')
|
COMMIT := $(shell git log | head -n 1 | awk '{print substr($$2, 0, 12)}')
|
||||||
|
|
||||||
# ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
# 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 ?= ""
|
TESTTAGS ?= ""
|
||||||
|
|
||||||
|
TESTWIDGETS := $(shell $(GO) list ./widgets/...)
|
||||||
|
|
||||||
# Unit Test
|
# Unit Test
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
|
|
@ -37,6 +39,32 @@ test:
|
||||||
fi; \
|
fi; \
|
||||||
done
|
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
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
$(GOFMT) -w $(GOFILES)
|
$(GOFMT) -w $(GOFILES)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ func init() {
|
||||||
lang.RegisterWidget("kanban", "page")
|
lang.RegisterWidget("kanban", "page")
|
||||||
lang.RegisterWidget("screen", "page")
|
lang.RegisterWidget("screen", "page")
|
||||||
lang.RegisterWidget("pages", "page")
|
lang.RegisterWidget("pages", "page")
|
||||||
|
lang.RegisterWidget("logins", "login")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load language packs
|
// Load language packs
|
||||||
|
|
@ -25,6 +26,8 @@ func Load(cfg config.Config) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set default
|
||||||
|
lang.Pick("default").AsDefault()
|
||||||
name := os.Getenv("YAO_LANG")
|
name := os.Getenv("YAO_LANG")
|
||||||
if name != "" {
|
if name != "" {
|
||||||
if _, has := lang.Dicts[name]; !has {
|
if _, has := lang.Dicts[name]; !has {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ func Walk(root string, typeName string, cb func(root, filename string)) error {
|
||||||
return err
|
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"
|
// SpecName 解析名称 root: "/tests/apis" file: "/tests/apis/foo/bar.http.json"
|
||||||
func SpecName(root string, file string) string {
|
func SpecName(root string, file string) string {
|
||||||
filename := strings.TrimPrefix(file, root+"/") // "foo/bar.http.json"
|
filename := strings.TrimPrefix(file, root+"/") // "foo/bar.http.json"
|
||||||
|
|
|
||||||
193
widgets/app/app.go
Normal file
193
widgets/app/app.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
161
widgets/app/app_test.go
Normal file
161
widgets/app/app_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
widgets/app/types.go
Normal file
25
widgets/app/types.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
133
widgets/login/login.go
Normal file
133
widgets/login/login.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
124
widgets/login/login_test.go
Normal file
124
widgets/login/login_test.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
24
widgets/login/types.go
Normal file
24
widgets/login/types.go
Normal file
|
|
@ -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"`
|
||||||
|
}
|
||||||
24
widgets/widgets.go
Normal file
24
widgets/widgets.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
13
widgets/widgets_test.go
Normal file
13
widgets/widgets_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue