diff --git a/sui/api/api.go b/sui/api/api.go index a66e1e6a..e81787f3 100644 --- a/sui/api/api.go +++ b/sui/api/api.go @@ -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" } }, { diff --git a/sui/api/process.go b/sui/api/process.go index 670fa7f8..7d6de448 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -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..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 + } +} diff --git a/sui/api/process_test.go b/sui/api/process_test.go index 5a7cfc55..5e6b1a33 100644 --- a/sui/api/process_test.go +++ b/sui/api/process_test.go @@ -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":"
1
", "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":"
1
", "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":"
1
", "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":"
1
", "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": "
1
", + "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": "
1
", + "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) } } diff --git a/sui/core/editor.go b/sui/core/editor.go index 4e9df67a..caeefb9a 100644 --- a/sui/core/editor.go +++ b/sui/core/editor.go @@ -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", } diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 816a3c38..ee80f9ad 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -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 diff --git a/sui/core/types.go b/sui/core/types.go index 963f8211..9af11103 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -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 diff --git a/sui/storages/local/page.go b/sui/storages/local/page.go index b68705f8..b6f7d30e 100644 --- a/sui/storages/local/page.go +++ b/sui/storages/local/page.go @@ -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 diff --git a/sui/storages/local/page_test.go b/sui/storages/local/page_test.go index 302630c8..30d60792 100644 --- a/sui/storages/local/page_test.go +++ b/sui/storages/local/page_test.go @@ -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": "
HomeInvite
\n
\n

Card Instance

\n

Card xx

\n
Table
\n
", + "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": "
\n Home\n Invite\n
\n\n
\n

Card Instance

\n

Card XYZ

\n
\n", + "language": "html" + }, + "style": null, + "script": null, + "data": null, + "board": { + "html": "
HomeInvite

Card Instance

Card xx

", + "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": "
HomeInvite

Card Instance

Card xx

", + "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": "
HomeInvite

Card Instance

Card xx

", + "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": "
HomeInvite

Card Instance

Card xx

", + "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": "
HomeInvite

Card Instance

Card xx

", + "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": "
404 {{ $query.message || $post.message }}
Add IT
", + "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)