separate mark into packages

This commit is contained in:
Egor Kovetskiy 2019-04-08 22:44:27 +03:00
parent e27dd44bfa
commit ada6f828ff
5 changed files with 265 additions and 159 deletions

174
main.go
View File

@ -11,8 +11,10 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/kovetskiy/godocs" "github.com/kovetskiy/godocs"
"github.com/kovetskiy/lorg" "github.com/kovetskiy/lorg"
"github.com/kovetskiy/mark/pkg/confluence"
"github.com/kovetskiy/mark/pkg/mark"
"github.com/reconquest/colorgful" "github.com/reconquest/colorgful"
"github.com/reconquest/ser-go" "github.com/reconquest/karma-go"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
"github.com/zazab/zhash" "github.com/zazab/zhash"
) )
@ -87,24 +89,6 @@ Options:
` `
) )
type PageInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Version struct {
Number int64 `json:"number"`
} `json:"version"`
Ancestors []struct {
Id string `json:"id"`
Title string `json:"title"`
} `json:"ancestors"`
Links struct {
Full string `json:"webui"`
} `json:"_links"`
}
var ( var (
logger = lorg.NewLog() logger = lorg.NewLog()
) )
@ -151,7 +135,7 @@ func main() {
logger.Fatal(err) logger.Fatal(err)
} }
meta, err := extractMeta(markdownData) meta, err := mark.ExtractMeta(markdownData)
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
@ -168,7 +152,7 @@ func main() {
logger.Fatal(err) logger.Fatal(err)
} }
api := NewAPI(creds.BaseURL, creds.Username, creds.Password) api := confluence.NewAPI(creds.BaseURL, creds.Username, creds.Password)
if creds.PageID != "" && meta != nil { if creds.PageID != "" && meta != nil {
logger.Warningf( logger.Warningf(
@ -187,7 +171,7 @@ func main() {
) )
} }
var target *PageInfo var target *confluence.PageInfo
if meta != nil { if meta != nil {
page, err := resolvePage(api, meta) page, err := resolvePage(api, meta)
@ -201,7 +185,7 @@ func main() {
logger.Fatalf("URL should provide 'pageId' GET-parameter") logger.Fatalf("URL should provide 'pageId' GET-parameter")
} }
page, err := api.getPageByID(creds.PageID) page, err := api.GetPageByID(creds.PageID)
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
@ -209,7 +193,7 @@ func main() {
target = page target = page
} }
err = api.updatePage( err = api.UpdatePage(
target, target,
MacroLayout{meta.Layout, [][]byte{htmlData}}.Render(), MacroLayout{meta.Layout, [][]byte{htmlData}}.Render(),
) )
@ -224,9 +208,13 @@ func main() {
creds.Username, creds.Username,
) )
err := api.setPagePermissions(target, RestrictionEdit, []Restriction{ err := api.SetPagePermissions(
{User: creds.Username}, target,
}) confluence.RestrictionEdit,
[]confluence.Restriction{
{User: creds.Username},
},
)
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
@ -284,10 +272,13 @@ func compileMarkdown(markdown []byte) []byte {
return html return html
} }
func resolvePage(api *API, meta *Meta) (*PageInfo, error) { func resolvePage(
page, err := api.findPage(meta.Space, meta.Title) api *confluence.API,
meta *mark.Meta,
) (*confluence.PageInfo, error) {
page, err := api.FindPage(meta.Space, meta.Title)
if err != nil { if err != nil {
return nil, ser.Errorf( return nil, karma.Format(
err, err,
"error during finding page '%s': %s", "error during finding page '%s': %s",
meta.Title, meta.Title,
@ -300,7 +291,7 @@ func resolvePage(api *API, meta *Meta) (*PageInfo, error) {
} }
if len(ancestry) > 0 { if len(ancestry) > 0 {
page, err := validateAncestry( page, err := mark.ValidateAncestry(
api, api,
meta.Space, meta.Space,
ancestry, ancestry,
@ -325,13 +316,13 @@ func resolvePage(api *API, meta *Meta) (*PageInfo, error) {
) )
} }
parent, err := ensureAncestry( parent, err := mark.EnsureAncestry(
api, api,
meta.Space, meta.Space,
meta.Parents, meta.Parents,
) )
if err != nil { if err != nil {
return nil, ser.Errorf( return nil, karma.Format(
err, err,
"can't create ancestry tree: %s; error: %s", "can't create ancestry tree: %s; error: %s",
strings.Join(meta.Parents, ` > `), strings.Join(meta.Parents, ` > `),
@ -352,9 +343,9 @@ func resolvePage(api *API, meta *Meta) (*PageInfo, error) {
) )
if page == nil { if page == nil {
page, err := api.createPage(meta.Space, parent, meta.Title, ``) page, err := api.CreatePage(meta.Space, parent, meta.Title, ``)
if err != nil { if err != nil {
return nil, ser.Errorf( return nil, karma.Format(
err, err,
"can't create page '%s': %s", "can't create page '%s': %s",
meta.Title, meta.Title,
@ -367,117 +358,6 @@ func resolvePage(api *API, meta *Meta) (*PageInfo, error) {
return page, nil return page, nil
} }
func ensureAncestry(
api *API,
space string,
ancestry []string,
) (*PageInfo, error) {
var parent *PageInfo
rest := ancestry
for i, title := range ancestry {
page, err := api.findPage(space, title)
if err != nil {
return nil, ser.Errorf(
err,
`error during finding parent page with title '%s': %s`,
title,
)
}
if page == nil {
break
}
logger.Tracef("parent page '%s' exists: %s", title, page.Links.Full)
rest = ancestry[i:]
parent = page
}
if parent != nil {
rest = rest[1:]
} else {
page, err := api.findRootPage(space)
if err != nil {
return nil, ser.Errorf(
err,
"can't find root page for space '%s': %s", space,
)
}
parent = page
}
if len(rest) == 0 {
return parent, nil
}
logger.Debugf(
"empty pages under '%s' to be created: %s",
parent.Title,
strings.Join(rest, ` > `),
)
for _, title := range rest {
page, err := api.createPage(space, parent, title, ``)
if err != nil {
return nil, ser.Errorf(
err,
`error during creating parent page with title '%s': %s`,
title,
)
}
parent = page
}
return parent, nil
}
func validateAncestry(
api *API,
space string,
ancestry []string,
) (*PageInfo, error) {
page, err := api.findPage(space, ancestry[len(ancestry)-1])
if err != nil {
return nil, err
}
if page == nil {
return nil, nil
}
if len(page.Ancestors) < 1 {
return nil, fmt.Errorf(`page '%s' has no parents`, page.Title)
}
if len(page.Ancestors) < len(ancestry) {
return nil, fmt.Errorf(
"page '%s' has fewer parents than specified: %s",
page.Title,
strings.Join(ancestry, ` > `),
)
}
// skipping root article title
for i, ancestor := range page.Ancestors[1:len(ancestry)] {
if ancestor.Title != ancestry[i] {
return nil, fmt.Errorf(
"broken ancestry tree; expected tree: %s; "+
"encountered '%s' at position of '%s'",
strings.Join(ancestry, ` > `),
ancestor.Title,
ancestry[i],
)
}
}
return page, nil
}
func getConfig(path string) (zhash.Hash, error) { func getConfig(path string) (zhash.Hash, error) {
configData := map[string]interface{}{} configData := map[string]interface{}{}
_, err := toml.DecodeFile(path, &configData) _, err := toml.DecodeFile(path, &configData)
@ -486,7 +366,7 @@ func getConfig(path string) (zhash.Hash, error) {
return zhash.NewHash(), err return zhash.NewHash(), err
} }
return zhash.NewHash(), ser.Errorf( return zhash.NewHash(), karma.Format(
err, err,
"can't decode toml file: %s", "can't decode toml file: %s",
) )

View File

@ -1,4 +1,4 @@
package main package confluence
import ( import (
"fmt" "fmt"
@ -27,6 +27,24 @@ type API struct {
json *gopencils.Resource json *gopencils.Resource
} }
type PageInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Version struct {
Number int64 `json:"number"`
} `json:"version"`
Ancestors []struct {
Id string `json:"id"`
Title string `json:"title"`
} `json:"ancestors"`
Links struct {
Full string `json:"webui"`
} `json:"_links"`
}
func NewAPI(baseURL string, username string, password string) *API { func NewAPI(baseURL string, username string, password string) *API {
auth := &gopencils.BasicAuth{username, password} auth := &gopencils.BasicAuth{username, password}
@ -40,8 +58,8 @@ func NewAPI(baseURL string, username string, password string) *API {
} }
} }
func (api *API) findRootPage(space string) (*PageInfo, error) { func (api *API) FindRootPage(space string) (*PageInfo, error) {
page, err := api.findPage(space, ``) page, err := api.FindPage(space, ``)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
`can't obtain first page from space '%s': %s`, `can't obtain first page from space '%s': %s`,
@ -64,8 +82,7 @@ func (api *API) findRootPage(space string) (*PageInfo, error) {
}, nil }, nil
} }
func (api *API) findPage(space string, title string) (*PageInfo, error) { func (api *API) FindPage(space string, title string) (*PageInfo, error) {
result := struct { result := struct {
Results []PageInfo `json:"results"` Results []PageInfo `json:"results"`
}{} }{}
@ -105,8 +122,7 @@ func (api *API) findPage(space string, title string) (*PageInfo, error) {
return &result.Results[0], nil return &result.Results[0], nil
} }
func (api *API) getPageByID(pageID string) (*PageInfo, error) { func (api *API) GetPageByID(pageID string) (*PageInfo, error) {
request, err := api.rest.Res( request, err := api.rest.Res(
"content/"+pageID, &PageInfo{}, "content/"+pageID, &PageInfo{},
).Get(map[string]string{"expand": "ancestors,version"}) ).Get(map[string]string{"expand": "ancestors,version"})
@ -136,7 +152,7 @@ func (api *API) getPageByID(pageID string) (*PageInfo, error) {
return request.Response.(*PageInfo), nil return request.Response.(*PageInfo), nil
} }
func (api *API) createPage( func (api *API) CreatePage(
space string, space string,
parent *PageInfo, parent *PageInfo,
title string, title string,
@ -183,7 +199,7 @@ func (api *API) createPage(
return request.Response.(*PageInfo), nil return request.Response.(*PageInfo), nil
} }
func (api *API) updatePage( func (api *API) UpdatePage(
page *PageInfo, newContent string, page *PageInfo, newContent string,
) error { ) error {
nextPageVersion := page.Version.Number + 1 nextPageVersion := page.Version.Number + 1
@ -238,7 +254,7 @@ func (api *API) updatePage(
return nil return nil
} }
func (api *API) setPagePermissions( func (api *API) SetPagePermissions(
page *PageInfo, page *PageInfo,
operation RestrictionOperation, operation RestrictionOperation,
restrictions []Restriction, restrictions []Restriction,

120
pkg/mark/ancestry.go Normal file
View File

@ -0,0 +1,120 @@
package mark
import (
"fmt"
"strings"
"github.com/kovetskiy/mark/pkg/confluence"
"github.com/reconquest/karma-go"
)
func EnsureAncestry(
api *confluence.API,
space string,
ancestry []string,
) (*confluence.PageInfo, error) {
var parent *confluence.PageInfo
rest := ancestry
for i, title := range ancestry {
page, err := api.FindPage(space, title)
if err != nil {
return nil, karma.Format(
err,
`error during finding parent page with title '%s': %s`,
title,
)
}
if page == nil {
break
}
logger.Tracef("parent page '%s' exists: %s", title, page.Links.Full)
rest = ancestry[i:]
parent = page
}
if parent != nil {
rest = rest[1:]
} else {
page, err := api.FindRootPage(space)
if err != nil {
return nil, karma.Format(
err,
"can't find root page for space '%s': %s", space,
)
}
parent = page
}
if len(rest) == 0 {
return parent, nil
}
logger.Debugf(
"empty pages under '%s' to be created: %s",
parent.Title,
strings.Join(rest, ` > `),
)
for _, title := range rest {
page, err := api.CreatePage(space, parent, title, ``)
if err != nil {
return nil, karma.Format(
err,
`error during creating parent page with title '%s': %s`,
title,
)
}
parent = page
}
return parent, nil
}
func ValidateAncestry(
api *confluence.API,
space string,
ancestry []string,
) (*confluence.PageInfo, error) {
page, err := api.FindPage(space, ancestry[len(ancestry)-1])
if err != nil {
return nil, err
}
if page == nil {
return nil, nil
}
if len(page.Ancestors) < 1 {
return nil, fmt.Errorf(`page '%s' has no parents`, page.Title)
}
if len(page.Ancestors) < len(ancestry) {
return nil, fmt.Errorf(
"page '%s' has fewer parents than specified: %s",
page.Title,
strings.Join(ancestry, ` > `),
)
}
// skipping root article title
for i, ancestor := range page.Ancestors[1:len(ancestry)] {
if ancestor.Title != ancestry[i] {
return nil, fmt.Errorf(
"broken ancestry tree; expected tree: %s; "+
"encountered '%s' at position of '%s'",
strings.Join(ancestry, ` > `),
ancestor.Title,
ancestry[i],
)
}
}
return page, nil
}

90
pkg/mark/mark.go Normal file
View File

@ -0,0 +1,90 @@
package mark
import (
"strings"
"github.com/kovetskiy/lorg"
"github.com/kovetskiy/mark/pkg/confluence"
"github.com/reconquest/karma-go"
)
var (
logger lorg.Logger = lorg.NewDiscarder()
)
func SetLogger(log lorg.Logger) {
logger = log
}
func ResolvePage(
api *confluence.API,
meta *Meta,
) (*confluence.PageInfo, error) {
page, err := api.FindPage(meta.Space, meta.Title)
if err != nil {
return nil, karma.Format(
err,
"error during finding page '%s': %s",
meta.Title,
)
}
ancestry := meta.Parents
if page != nil {
ancestry = append(ancestry, page.Title)
}
if len(ancestry) > 0 {
page, err := ValidateAncestry(
api,
meta.Space,
ancestry,
)
if err != nil {
return nil, err
}
if page == nil {
logger.Warningf(
"page '%s' is not found ",
meta.Parents[len(ancestry)-1],
)
}
path := meta.Parents
path = append(path, meta.Title)
logger.Debugf(
"resolving page path: ??? > %s",
strings.Join(path, ` > `),
)
}
parent, err := EnsureAncestry(
api,
meta.Space,
meta.Parents,
)
if err != nil {
return nil, karma.Format(
err,
"can't create ancestry tree: %s; error: %s",
strings.Join(meta.Parents, ` > `),
)
}
titles := []string{}
for _, page := range parent.Ancestors {
titles = append(titles, page.Title)
}
titles = append(titles, parent.Title)
logger.Infof(
"page will be stored under path: %s > %s",
strings.Join(titles, ` > `),
meta.Title,
)
return page, nil
}

View File

@ -1,4 +1,4 @@
package main package mark
import ( import (
"bufio" "bufio"
@ -22,7 +22,7 @@ type Meta struct {
Layout string Layout string
} }
func extractMeta(data []byte) (*Meta, error) { func ExtractMeta(data []byte) (*Meta, error) {
headerPattern := regexp.MustCompile(`\[\]:\s*#\s*\(([^:]+):\s*(.*)\)`) headerPattern := regexp.MustCompile(`\[\]:\s*#\s*\(([^:]+):\s*(.*)\)`)
var meta *Meta var meta *Meta