[add] page builder page CURD

This commit is contained in:
Max 2023-10-05 19:07:26 +08:00
parent 1dedd7057b
commit b43f83aa61
8 changed files with 1070 additions and 53 deletions

View file

@ -78,6 +78,36 @@ var dsl = []byte(`
"process": "sui.Page.Tree",
"in": ["$param.id", "$param.template_id", "$param.route"],
"out": { "status": 200, "type": "application/json" }
},{
"path": "/:id/page/save/:template_id/*route",
"method": "POST",
"process": "sui.Page.Save",
"in": ["$param.id", "$param.template_id", "$param.route", ":context"],
"out": { "status": 200, "type": "application/json" }
},{
"path": "/:id/page/temp/:template_id/*route",
"method": "POST",
"process": "sui.Page.SaveTemp",
"in": ["$param.id", "$param.template_id", "$param.route", ":context"],
"out": { "status": 200, "type": "application/json" }
},{
"path": "/:id/page/create/:template_id/*route",
"method": "POST",
"process": "sui.Page.Create",
"in": ["$param.id", "$param.template_id", "$param.route", ":context"],
"out": { "status": 200, "type": "application/json" }
},{
"path": "/:id/page/exist/:template_id/*route",
"method": "GET",
"process": "sui.Page.Exist",
"in": ["$param.id", "$param.template_id", "$param.route"],
"out": { "status": 200, "type": "application/json" }
},{
"path": "/:id/page/remove/:template_id/*route",
"method": "POST",
"process": "sui.Page.Remove",
"in": ["$param.id", "$param.template_id", "$param.route"],
"out": { "status": 200, "type": "application/json" }
},
{

View file

@ -4,6 +4,8 @@ import (
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/sui/core"
@ -24,9 +26,14 @@ func init() {
"component.get": ComponentGet,
"component.find": ComponentFind,
"page.tree": PageTree,
"page.get": PageGet,
"page.asset": PageAsset,
"page.tree": PageTree,
"page.get": PageGet,
"page.save": PageSave,
"page.savetemp": PageSaveTemp,
"page.create": PageCreate,
"page.remove": PageRemove,
"page.exist": PageExist,
"page.asset": PageAsset,
"editor.render": EditorRender,
"editor.source": EditorSource,
@ -34,7 +41,6 @@ func init() {
}
// TemplateGet handle the get Template request
// Process sui.<ID>.templates
func TemplateGet(process *process.Process) interface{} {
process.ValidateArgNums(1)
@ -223,6 +229,164 @@ func PageGet(process *process.Process) interface{} {
return tree
}
// PageSave handle the find Template request
func PageSave(process *process.Process) interface{} {
process.ValidateArgNums(4)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
var page core.IPage
if tmpl.PageExist(route) {
page, err = tmpl.Page(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
} else {
page, err = tmpl.CreatePage(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
}
source, err := getSource(process)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
if source == nil {
exception.New("the source is required", 400).Throw()
}
err = page.Save(source)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return nil
}
// PageSaveTemp handle the find Template request
func PageSaveTemp(process *process.Process) interface{} {
process.ValidateArgNums(4)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
var page core.IPage
if tmpl.PageExist(route) {
page, err = tmpl.Page(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
} else {
page, err = tmpl.CreatePage(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
}
source, err := getSource(process)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
if source == nil {
exception.New("the source is required", 400).Throw()
}
if source.UID == "" {
exception.New("the source.uid is required", 400).Throw()
}
err = page.SaveTemp(source)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return nil
}
// PageCreate handle the find Template request
func PageCreate(process *process.Process) interface{} {
process.ValidateArgNums(3)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
page, err := tmpl.CreatePage(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
if len(process.Args) <= 3 {
return nil
}
source, err := getSource(process)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
if source == nil {
return nil
}
err = page.Save(source)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return nil
}
// PageRemove handle the find Template request
func PageRemove(process *process.Process) interface{} {
process.ValidateArgNums(3)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
err = tmpl.RemovePage(route)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return nil
}
// PageExist handle the find Template request
func PageExist(process *process.Process) interface{} {
process.ValidateArgNums(3)
sui := get(process)
templateID := process.ArgsString(1)
route := route(process, 2)
tmpl, err := sui.GetTemplate(templateID)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return tmpl.PageExist(route)
}
// PageAsset handle the find Template request
func PageAsset(process *process.Process) interface{} {
process.ValidateArgNums(3)
@ -357,3 +521,59 @@ func route(process *process.Process, i int) string {
}
return route
}
func getSource(process *process.Process) (*core.RequestSource, error) {
if process.NumOfArgs() < 4 {
return nil, nil
}
switch v := process.Args[3].(type) {
case *core.RequestSource:
return v, nil
case *gin.Context:
source := core.RequestSource{UID: v.GetHeader("Yao-Builder-Uid")}
err := v.Bind(&source)
if err != nil {
return nil, err
}
return &source, nil
case string:
if process.NumOfArgs() > 4 {
uid := process.ArgsString(3)
payload, err := jsoniter.Marshal(process.Args[4])
if err != nil {
return nil, err
}
source := core.RequestSource{UID: uid}
err = jsoniter.Unmarshal(payload, &source)
if err != nil {
return nil, err
}
return &source, nil
}
source := core.RequestSource{}
err := jsoniter.UnmarshalFromString(process.ArgsString(3), &source)
if err != nil {
return nil, err
}
return &source, nil
default:
payload, err := jsoniter.Marshal(process.Args[3])
if err != nil {
return nil, err
}
source := core.RequestSource{}
err = jsoniter.Unmarshal(payload, &source)
if err != nil {
return nil, err
}
return &source, nil
}
}

View file

@ -1,8 +1,12 @@
package api
import (
"bytes"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/yao/config"
@ -238,6 +242,233 @@ func TestPageGet(t *testing.T) {
}
}
func TestPageExist(t *testing.T) {
load(t)
defer clean()
// test demo
p, err := process.Of("sui.page.exist", "demo", "tech-blue", "/index/[invite]")
if err != nil {
t.Fatal(err)
}
res, err := p.Exec()
if err != nil {
t.Fatal(err)
}
assert.IsType(t, true, res)
assert.Equal(t, true, res.(bool))
p, err = process.Of("sui.page.exist", "demo", "tech-blue", "/index/[invite]/[invite]")
if err != nil {
t.Fatal(err)
}
res, err = p.Exec()
if err != nil {
t.Fatal(err)
}
assert.IsType(t, false, res)
assert.Equal(t, false, res.(bool))
}
func TestPageCreate(t *testing.T) {
load(t)
defer clean()
// test demo
p, err := process.Of("sui.page.create", "demo", "tech-blue", "/unit-test")
if err != nil {
t.Fatal(err)
}
res, err := p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
}
func TestPageCreateSaveThenRemove(t *testing.T) {
load(t)
defer clean()
// test demo
p, err := process.Of("sui.page.create", "demo", "tech-blue", "/unit-test", `{"uid":"unit-test", "needToSave":{"page":true}, "page":{"source":"<div>1</div>", "language":"html"}}`)
if err != nil {
t.Fatal(err)
}
res, err := p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
// test demo
p, err = process.Of("sui.page.remove", "demo", "tech-blue", "/unit-test")
if err != nil {
t.Fatal(err)
}
res, err = p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
}
func TestPageSaveThenRemove(t *testing.T) {
load(t)
defer clean()
// test demo
p, err := process.Of("sui.page.create", "demo", "tech-blue", "/unit-test", `{"uid":"unit-test", "needToSave":{"page":true}, "page":{"source":"<div>1</div>", "language":"html"}}`)
if err != nil {
t.Fatal(err)
}
res, err := p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
// test demo
p, err = process.Of("sui.page.SaveTemp", "demo", "tech-blue", "/unit-test", `{"uid":"unit-test", "needToSave":{"page":true}, "page":{"source":"<div>1</div>", "language":"html"}}`)
if err != nil {
t.Fatal(err)
}
res, err = p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
// test demo
p, err = process.Of("sui.page.Save", "demo", "tech-blue", "/unit-test", `{"uid":"unit-test", "needToSave":{"page":true}, "page":{"source":"<div>1</div>", "language":"html"}}`)
if err != nil {
t.Fatal(err)
}
res, err = p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
// test demo
p, err = process.Of("sui.page.remove", "demo", "tech-blue", "/unit-test")
if err != nil {
t.Fatal(err)
}
res, err = p.Exec()
if err != nil {
t.Fatal(err)
}
assert.Nil(t, res)
}
func TestGetSource(t *testing.T) {
// *core.RequestSource
var payload interface{} = &core.RequestSource{UID: "unit-test"}
args := []interface{}{"demo", "tech-blue", "/index/[invite]", payload}
p, err := process.Of("sui.page.Save", args...)
if err != nil {
t.Fatal(err)
}
src, err := getSource(p)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "unit-test", src.UID)
// String
args[3] = `{"uid":"unit-test-string"}`
p, err = process.Of("sui.page.Save", args...)
if err != nil {
t.Fatal(err)
}
src, err = getSource(p)
assert.Equal(t, "unit-test-string", src.UID)
// String & Payload
args[3] = "unit-test-string2"
newArgs := append(args, map[string]interface{}{
"page": map[string]interface{}{
"source": "<div>1</div>",
"language": "html",
}})
p, err = process.Of("sui.page.Save", newArgs...)
if err != nil {
t.Fatal(err)
}
src, err = getSource(p)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "unit-test-string2", src.UID)
// Default
args[3] = map[string]interface{}{
"uid": "unit-test-map",
"page": map[string]interface{}{
"source": "<div>1</div>",
"language": "html",
}}
p, err = process.Of("sui.page.Save", args...)
if err != nil {
t.Fatal(err)
}
src, err = getSource(p)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "unit-test-map", src.UID)
// Gin Context
requestBody := []byte(`{"page": {"source":"gin-context Test", "language":"html"} }`)
router := gin.Default()
router.POST("/unit-test", func(ctx *gin.Context) {
args[3] = ctx
p, err = process.Of("sui.page.Save", args...)
if err != nil {
t.Fatal(err)
}
src, err = getSource(p)
if err != nil {
t.Fatal(err)
}
if src.Page == nil {
t.Fatalf("Page is nil")
}
assert.Equal(t, "unit-test-gin-context", src.UID)
assert.Equal(t, "html", src.Page.Language)
assert.Equal(t, "gin-context Test", src.Page.Source)
})
req, err := http.NewRequest("POST", "/unit-test", bytes.NewBuffer(requestBody))
if err != nil {
t.Fatalf("Couldn't create request: %v\n", err)
return
}
req.Header.Set("Yao-Builder-Uid", "unit-test-gin-context")
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(httptest.NewRecorder(), req)
}
func TestPageAssetJS(t *testing.T) {
load(t)
@ -351,9 +582,9 @@ func TestEditorPageSource(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.IsType(t, core.ResponseSource{}, res)
assert.NotEmpty(t, res.(core.ResponseSource).Source)
assert.NotEmpty(t, res.(core.ResponseSource).Language)
assert.IsType(t, core.SourceData{}, res)
assert.NotEmpty(t, res.(core.SourceData).Source)
assert.NotEmpty(t, res.(core.SourceData).Language)
}
}

View file

@ -71,39 +71,39 @@ func (page *Page) EditorRender(request *Request) (*ResponseEditor, error) {
}
// EditorPageSource get the editor page source code
func (page *Page) EditorPageSource() ResponseSource {
return ResponseSource{
func (page *Page) EditorPageSource() SourceData {
return SourceData{
Source: page.Codes.HTML.Code,
Language: "html",
}
}
// EditorScriptSource get the editor script source code
func (page *Page) EditorScriptSource() ResponseSource {
func (page *Page) EditorScriptSource() SourceData {
if page.Codes.TS.Code != "" {
return ResponseSource{
return SourceData{
Source: page.Codes.TS.Code,
Language: "typescript",
}
}
return ResponseSource{
return SourceData{
Source: page.Codes.JS.Code,
Language: "javascript",
}
}
// EditorStyleSource get the editor style source code
func (page *Page) EditorStyleSource() ResponseSource {
return ResponseSource{
func (page *Page) EditorStyleSource() SourceData {
return SourceData{
Source: page.Codes.CSS.Code,
Language: "css",
}
}
// EditorDataSource get the editor data source code
func (page *Page) EditorDataSource() ResponseSource {
return ResponseSource{
func (page *Page) EditorDataSource() SourceData {
return SourceData{
Source: page.Codes.DATA.Code,
Language: "json",
}

View file

@ -15,6 +15,9 @@ type ITemplate interface {
Pages() ([]IPage, error)
PageTree(route string) ([]*PageTreeNode, error)
Page(route string) (IPage, error)
PageExist(route string) bool
CreatePage(route string) (IPage, error)
RemovePage(route string) error
GetPageFromAsset(asset string) (IPage, error)
Blocks() ([]IBlock, error)
@ -35,25 +38,18 @@ type IPage interface {
Load() error
Get() *Page
Save(request *RequestSource) error
SaveTemp(request *RequestSource) error
Remove() error
EditorRender(request *Request) (*ResponseEditor, error)
EditorPageSource() ResponseSource
EditorScriptSource() ResponseSource
EditorStyleSource() ResponseSource
EditorDataSource() ResponseSource
EditorPageSource() SourceData
EditorScriptSource() SourceData
EditorStyleSource() SourceData
EditorDataSource() SourceData
AssetScript() (*Asset, error)
AssetStyle() (*Asset, error)
// Render()
// Html()
// Script()
// Style()
// Data()
// Compile()
// Locale()
}
// IBlock is the interface for the block

View file

@ -85,6 +85,25 @@ type Request struct {
Locale string `json:"locale,omitempty"`
}
// RequestSource is the struct for the request
type RequestSource struct {
UID string `json:"uid"`
User string `json:"user,omitempty"`
Page *SourceData `json:"page,omitempty"`
Style *SourceData `json:"style,omitempty"`
Script *SourceData `json:"script,omitempty"`
Data *SourceData `json:"data,omitempty"`
Board *BoardSourceData `json:"board,omitempty"`
NeedToSave struct {
Page bool `json:"page,omitempty"`
Style bool `json:"style,omitempty"`
Script bool `json:"script,omitempty"`
Data bool `json:"data,omitempty"`
Board bool `json:"board,omitempty"`
Validate bool `json:"validate,omitempty"`
} `json:"needToSave,omitempty"`
}
// ResponseEditor is the struct for the response
type ResponseEditor struct {
HTML string `json:"html,omitempty"`
@ -95,10 +114,16 @@ type ResponseEditor struct {
Warnings []string `json:"warnings,omitempty"`
}
// ResponseSource is the struct for the response
type ResponseSource struct {
// SourceData is the struct for the response
type SourceData struct {
Source string `json:"source,omitempty"`
Language string `json:"lang,omitempty"`
Language string `json:"language,omitempty"`
}
// BoardSourceData is the struct for the response
type BoardSourceData struct {
HTML string `json:"html,omitempty"`
Style string `json:"style,omitempty"`
}
// SourceCodes is the struct for the page codes

View file

@ -17,7 +17,7 @@ func (tmpl *Template) Pages() ([]core.IPage, error) {
tmpl.local.fs.Walk(tmpl.Root, func(root, file string, isdir bool) error {
name := filepath.Base(file)
if isdir {
if strings.HasPrefix(name, "__") {
if strings.HasPrefix(name, "__") || name == ".tmp" {
return filepath.SkipDir
}
return nil
@ -56,7 +56,7 @@ func (tmpl *Template) PageTree(route string) ([]*core.PageTreeNode, error) {
relPath := file
if isdir {
if strings.HasPrefix(name, "__") {
if strings.HasPrefix(name, "__") || name == ".tmp" {
return filepath.SkipDir
}
@ -156,6 +156,101 @@ func (tmpl *Template) Page(route string) (core.IPage, error) {
return nil, fmt.Errorf("Page %s not found", route)
}
// PageExist check if the page exist
func (tmpl *Template) PageExist(route string) bool {
path := tmpl.getPagePath(route)
exts := []string{".sui", ".html", ".htm", ".page"}
for _, ext := range exts {
file := fmt.Sprintf("%s%s", path, ext)
if exist, _ := tmpl.local.fs.Exists(file); exist {
return true
}
}
return false
}
// RemovePage remove the page
func (tmpl *Template) RemovePage(route string) error {
if !tmpl.PageExist(route) {
return nil
}
path := filepath.Join(tmpl.Root, route)
name := filepath.Base(path) + ".*"
err := tmpl.local.fs.Walk(path, func(root, file string, isdir bool) error {
if isdir {
return nil
}
return tmpl.local.fs.Remove(file)
}, name)
if err != nil {
return err
}
dirs, err := tmpl.local.fs.ReadDir(path, false)
if err != nil {
return err
}
if len(dirs) == 0 {
return tmpl.local.fs.Remove(path)
}
return nil
}
// CreatePage create a new page
func (tmpl *Template) CreatePage(route string) (core.IPage, error) {
if tmpl.PageExist(route) {
return nil, fmt.Errorf("Page %s already exist", route)
}
// Create the page directory
name := tmpl.getPageBase(route)
return &Page{
tmpl: tmpl,
Page: &core.Page{
Route: route,
Path: filepath.Join(tmpl.Root, route),
Name: name,
Codes: core.SourceCodes{
HTML: core.Source{File: fmt.Sprintf("%s.html", name)},
CSS: core.Source{File: fmt.Sprintf("%s.css", name)},
JS: core.Source{File: fmt.Sprintf("%s.js", name)},
TS: core.Source{File: fmt.Sprintf("%s.ts", name)},
LESS: core.Source{File: fmt.Sprintf("%s.less", name)},
},
},
}, nil
}
// Remove remove the page
func (page *Page) Remove() error {
return page.tmpl.RemovePage(page.Route)
}
// GetPageFromAsset get the page from the asset
func (tmpl *Template) GetPageFromAsset(file string) (core.IPage, error) {
route := filepath.Dir(file)
name := tmpl.getPageBase(route)
return &Page{
tmpl: tmpl,
Page: &core.Page{
Route: route,
Path: filepath.Join(tmpl.Root, route),
Name: name,
Codes: core.SourceCodes{
CSS: core.Source{File: fmt.Sprintf("%s.css", name)},
JS: core.Source{File: fmt.Sprintf("%s.js", name)},
TS: core.Source{File: fmt.Sprintf("%s.ts", name)},
LESS: core.Source{File: fmt.Sprintf("%s.less", name)},
},
},
}, nil
}
func (tmpl *Template) getPageFrom(file string) (core.IPage, error) {
route := tmpl.getPageRoute(file)
return tmpl.getPage(route, file)
@ -255,24 +350,140 @@ func (page *Page) Load() error {
return nil
}
// GetPageFromAsset get the page from the asset
func (tmpl *Template) GetPageFromAsset(file string) (core.IPage, error) {
route := filepath.Dir(file)
name := tmpl.getPageBase(route)
return &Page{
tmpl: tmpl,
Page: &core.Page{
Route: route,
Path: filepath.Join(tmpl.Root, route),
Name: name,
Codes: core.SourceCodes{
CSS: core.Source{File: fmt.Sprintf("%s.css", name)},
JS: core.Source{File: fmt.Sprintf("%s.js", name)},
TS: core.Source{File: fmt.Sprintf("%s.ts", name)},
LESS: core.Source{File: fmt.Sprintf("%s.less", name)},
},
},
}, nil
// SaveTemp save page to the temp file
func (page *Page) SaveTemp(request *core.RequestSource) error {
tempPath := filepath.Join(page.Path, ".tmp", request.UID)
return page.save(tempPath, request)
}
// Save save page to the storage, if the page not exist, create it
func (page *Page) Save(request *core.RequestSource) error {
path := page.Path
err := page.save(path, request)
if err != nil {
return err
}
// Remove the temp file
tempPath := filepath.Join(page.Path, ".tmp", request.UID)
if exist, _ := page.tmpl.local.fs.Exists(tempPath); exist {
err = page.tmpl.local.fs.RemoveAll(tempPath)
if err != nil {
return err
}
dirs, err := page.tmpl.local.fs.ReadDir(filepath.Join(page.Path, ".tmp"), false)
if err != nil {
return err
}
if len(dirs) == 0 {
return page.tmpl.local.fs.Remove(filepath.Join(page.Path, ".tmp"))
}
}
return nil
}
// save page to the temp file
func (page *Page) save(path string, request *core.RequestSource) error {
if request.NeedToSave.Board {
err := page.saveBoard(path, request.Board)
if err != nil {
return err
}
}
if request.NeedToSave.Page {
err := page.savePage(path, request.Page)
if err != nil {
return err
}
}
if request.NeedToSave.Style {
err := page.saveStyle(path, request.Style)
if err != nil {
return err
}
}
if request.NeedToSave.Script {
err := page.saveScript(path, request.Script)
if err != nil {
return err
}
}
if request.NeedToSave.Data {
err := page.saveData(path, request.Data)
if err != nil {
return err
}
}
return nil
}
// saveBoard save the board to the storage
func (page *Page) saveBoard(path string, board *core.BoardSourceData) error {
htmlFile := filepath.Join(path, page.Codes.HTML.File)
_, err := page.tmpl.local.fs.WriteFile(htmlFile, []byte(board.HTML), 0644)
if err != nil {
return err
}
cssFile := filepath.Join(path, page.Codes.CSS.File)
_, err = page.tmpl.local.fs.WriteFile(cssFile, []byte(board.Style), 0644)
return err
}
func (page *Page) savePage(path string, src *core.SourceData) error {
if src.Language != "html" {
return fmt.Errorf("Page %s language not support", page.Route)
}
htmlFile := filepath.Join(path, page.Codes.HTML.File)
_, err := page.tmpl.local.fs.WriteFile(htmlFile, []byte(src.Source), 0644)
return err
}
func (page *Page) saveStyle(path string, src *core.SourceData) error {
if src.Language != "css" {
return fmt.Errorf("Page %s language not support", page.Route)
}
cssFile := filepath.Join(path, page.Codes.CSS.File)
_, err := page.tmpl.local.fs.WriteFile(cssFile, []byte(src.Source), 0644)
return err
}
func (page *Page) saveScript(path string, src *core.SourceData) error {
switch src.Language {
case "typescript":
tsFile := filepath.Join(path, page.Codes.TS.File)
_, err := page.tmpl.local.fs.WriteFile(tsFile, []byte(src.Source), 0644)
return err
case "javascript":
jsFile := filepath.Join(path, page.Codes.JS.File)
_, err := page.tmpl.local.fs.WriteFile(jsFile, []byte(src.Source), 0644)
return err
default:
return fmt.Errorf("Page %s language not support", page.Route)
}
}
func (page *Page) saveData(path string, src *core.SourceData) error {
if src.Language != "json" {
return fmt.Errorf("Page %s language not support", page.Route)
}
dataFile := filepath.Join(path, page.Codes.DATA.File)
_, err := page.tmpl.local.fs.WriteFile(dataFile, []byte(src.Source), 0644)
return err
}
// AssetScript get the script

View file

@ -4,6 +4,7 @@ import (
"path/filepath"
"testing"
jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/yao/sui/core"
)
@ -187,6 +188,309 @@ func TestPageRenderEditor(t *testing.T) {
assert.Equal(t, "@pages/index/index.css", res.Styles[4])
}
func TestPageSaveTempBoard(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": null,
"script": null,
"data": null,
"board": {
"html": "<div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\">Invite</a></div>\n<div id=\"i2j7\" cui:type=\"Card\">\n <h1>Card Instance</h1>\n <p>Card xx</p>\n <div> Table </div>\n</div>",
"style": "#i2j7 {\n color: #2c3e50;\n width: 100%;\n height: 300px;\n background: #d1c2d3;\n padding: .5em;\n}"
},
"needToSave": {
"page": false,
"style": false,
"script": false,
"data": false,
"board": true,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSaveTempPage(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": {
"source": "<div class=\"bg-purple-700 p-4 text-base\">\n <span class=\"text-white mr-2\">Home</span>\n <a href=\"/index/{{user.id}}\" class=\"text-white\">Invite</a>\n</div>\n\n<div cui:type=\"Card\">\n <h1>Card Instance</h1>\n <p>Card XYZ</p>\n</div>\n",
"language": "html"
},
"style": null,
"script": null,
"data": null,
"board": {
"html": "<body service=\"Index\" data-gjs-type=\"wrapper\" data-gjs-stylable=\"[&quot;background&quot;,&quot;background-color&quot;,&quot;background-image&quot;,&quot;background-repeat&quot;,&quot;background-attachment&quot;,&quot;background-position&quot;,&quot;background-size&quot;]\"><div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\" data-gjs-tagName=\"span\" data-gjs-type=\"text\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\" data-gjs-type=\"link\">Invite</a></div><div id=\"izaw\" data-gjs-type=\"Card\" data-gjs-style=\"\"><h1 data-gjs-tagName=\"h1\" data-gjs-type=\"text\">Card Instance</h1><p data-gjs-tagName=\"p\" data-gjs-type=\"text\">Card xx</p></div></body>",
"style": "#izaw{color:#2c3e50;width:100%;height:300px;background:#d1c2d3;padding:.5em;}"
},
"needToSave": {
"page": true,
"style": false,
"script": false,
"data": false,
"board": false,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSaveTempStyle(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": {
"source": "* { box-sizing: border-box; }\nbody { margin: 0; }\n#ihjf { color:#ffffff;width:100%;height:100px;background:#1c0d1a;padding:.5em;display:flex; }\n\n",
"language": "css"
},
"script": null,
"data": null,
"board": {
"html": "<body service=\"Index\" data-gjs-type=\"wrapper\" data-gjs-stylable=\"[&quot;background&quot;,&quot;background-color&quot;,&quot;background-image&quot;,&quot;background-repeat&quot;,&quot;background-attachment&quot;,&quot;background-position&quot;,&quot;background-size&quot;]\"><div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\" data-gjs-tagName=\"span\" data-gjs-type=\"text\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\" data-gjs-type=\"link\">Invite</a></div><div id=\"inhy\" data-gjs-type=\"Card\" data-gjs-style=\"\"><h1 data-gjs-tagName=\"h1\" data-gjs-type=\"text\">Card Instance</h1><p data-gjs-tagName=\"p\" data-gjs-type=\"text\">Card xx</p></div></body>",
"style": "#inhy{color:#2c3e50;width:100%;height:300px;background:#d1c2d3;padding:.5em;}"
},
"needToSave": {
"page": false,
"style": true,
"script": false,
"data": false,
"board": false,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSaveTempScriptJS(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": null,
"script": {
"source": "function Hello() {\n console.log(\"Hello World!\");\n}\n\nfunction Index() {\n return {\n title: \"Customers\",\n hello: \"world\",\n rows: [\n { name: \"John\", age: 30, city: \"New York\" },\n { name: \"Mary\", age: 20, city: \"Paris\" },\n { name: \"Peter\", age: 40, city: \"London\" },\n ],\n };\n}\n",
"language": "javascript"
},
"data": null,
"board": {
"html": "<body service=\"Index\" data-gjs-type=\"wrapper\" data-gjs-stylable=\"[&quot;background&quot;,&quot;background-color&quot;,&quot;background-image&quot;,&quot;background-repeat&quot;,&quot;background-attachment&quot;,&quot;background-position&quot;,&quot;background-size&quot;]\"><div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\" data-gjs-tagName=\"span\" data-gjs-type=\"text\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\" data-gjs-type=\"link\">Invite</a></div><div id=\"icqh\" data-gjs-type=\"Card\" data-gjs-style=\"\"><h1 data-gjs-tagName=\"h1\" data-gjs-type=\"text\">Card Instance</h1><p data-gjs-tagName=\"p\" data-gjs-type=\"text\">Card xx</p></div></body>",
"style": "#icqh{color:#2c3e50;width:100%;height:300px;background:#d1c2d3;padding:.5em;}"
},
"needToSave": {
"page": false,
"style": false,
"script": true,
"data": false,
"board": false,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSaveTempScriptTS(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": null,
"script": {
"source": "import \"@assets/dark/dark.css\";\nimport \"@assets/light/light.css\";\n\nimport { Hello } from \"@assets/main.js\";\n\nconst onPageLoad = (event: Event) => {\n console.log(\"Page Loaded 103\");\n};\n\nconst onPageReady = (event: Event) => {\n Hello(\"world\");\n Foo(\"Bar\");\n console.log(\"Page Ready\");\n return;\n};\n\nconst onData = (\n params: { [key: string]: string[] },\n query: { [key: string]: string[] }\n) => {\n console.log(\"Page Send Data Request\");\n};\n\nconst onDataSuccess = (data: { [key: string]: any }) => {\n console.log(\"Page Data Ready\");\n};\n\nconst onDataError = (data: { code: number; message: string }) => {\n console.log(\"Page Data Ready\");\n};\n\nconst onResize = (event: Event) => {\n console.log(\"Page Resize\");\n};\n\nconst onPageScroll = (event: Event) => {\n console.log(\"Page Scroll\");\n};\n\nfunction Foo(bar: string) {\n console.log(` + "`Foo ${bar}`" + `);\n}\n",
"language": "typescript"
},
"data": null,
"board": {
"html": "<body service=\"Index\" data-gjs-type=\"wrapper\" data-gjs-stylable=\"[&quot;background&quot;,&quot;background-color&quot;,&quot;background-image&quot;,&quot;background-repeat&quot;,&quot;background-attachment&quot;,&quot;background-position&quot;,&quot;background-size&quot;]\"><div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\" data-gjs-tagName=\"span\" data-gjs-type=\"text\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\" data-gjs-type=\"link\">Invite</a></div><div id=\"icqh\" data-gjs-type=\"Card\" data-gjs-style=\"\"><h1 data-gjs-tagName=\"h1\" data-gjs-type=\"text\">Card Instance</h1><p data-gjs-tagName=\"p\" data-gjs-type=\"text\">Card xx</p></div></body>",
"style": "#icqh{color:#2c3e50;width:100%;height:300px;background:#d1c2d3;padding:.5em;}"
},
"needToSave": {
"page": false,
"style": false,
"script": true,
"data": false,
"board": false,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSaveTempData(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": null,
"script": null,
"data": {
"source": "{\n \"title\": \"Home Page\",\n \"data\": { \"service\": \"Index\" },\n \"foo\": \"bar\",\n \"preview\": { \"params\": { \"id\": \"1\" } }\n}\n",
"language": "json"
},
"board": {
"html": "<body service=\"Index\" data-gjs-type=\"wrapper\" data-gjs-stylable=\"[&quot;background&quot;,&quot;background-color&quot;,&quot;background-image&quot;,&quot;background-repeat&quot;,&quot;background-attachment&quot;,&quot;background-position&quot;,&quot;background-size&quot;]\"><div class=\"bg-purple-700 p-4 text-base\"><span class=\"text-white mr-2\" data-gjs-tagName=\"span\" data-gjs-type=\"text\">Home</span><a href=\"/index/{{user.id}}\" class=\"text-white\" data-gjs-type=\"link\">Invite</a></div><div id=\"ipxs\" data-gjs-type=\"Card\" data-gjs-style=\"\"><h1 data-gjs-tagName=\"h1\" data-gjs-type=\"text\">Card Instance</h1><p data-gjs-tagName=\"p\" data-gjs-type=\"text\">Card xx</p></div></body>",
"style": "#ipxs{color:#2c3e50;width:100%;height:300px;background:#d1c2d3;padding:.5em;}"
},
"needToSave": {
"page": false,
"style": false,
"script": false,
"data": true,
"board": false,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.Page("/index")
if err != nil {
t.Fatalf("Page error: %v", err)
}
err = page.SaveTemp(req)
assert.Nil(t, err)
}
func TestPageSave(t *testing.T) {
tests := prepare(t)
defer clean()
tmpl, err := tests.Demo.GetTemplate("tech-blue")
if err != nil {
t.Fatalf("GetTemplate error: %v", err)
}
const payload = `{
"page": null,
"style": null,
"script": null,
"data": null,
"board": {
"html": "<div id=\"io71\">404 {{ $query.message || $post.message }}<br>Add IT</div>",
"style": ""
},
"needToSave": {
"page": false,
"style": false,
"script": false,
"data": false,
"board": true,
"validate": true
}
}`
req := &core.RequestSource{UID: "19e09e7e-9e19-44c1-bbab-2a55c51c9df3"}
jsoniter.Unmarshal([]byte(payload), &req)
page, err := tmpl.CreatePage("/unit-test")
if err != nil {
t.Fatalf("Page error: %v", err)
}
defer page.Remove()
err = page.SaveTemp(req)
if err != nil {
t.Fatalf("SaveTemp error: %v", err)
}
err = page.Save(req)
assert.Nil(t, err)
}
func TestPageGetPageFromAsset(t *testing.T) {
tests := prepare(t)