mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-23 21:32:41 +08:00
Support for relative links (#33)
* Support for relative links Fixes #25 * Error logging fixes * Better regexp
This commit is contained in:
parent
bcf2acb39f
commit
63fe97beaa
2
go.mod
2
go.mod
@ -15,6 +15,6 @@ require (
|
||||
github.com/reconquest/pkg v0.0.0-20201028091908-8e9a5e0226ef
|
||||
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4
|
||||
github.com/russross/blackfriday v1.5.2
|
||||
github.com/stretchr/testify v1.5.1 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -11,8 +11,6 @@ github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwn
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/kovetskiy/gopencils v0.0.0-20201103141120-610929377f9b h1:+PnJcuiUVcU3ixOvpvhyjswKPkxBVN+a2CaFCNNBfvw=
|
||||
github.com/kovetskiy/gopencils v0.0.0-20201103141120-610929377f9b/go.mod h1:rn9YsgK4kxBDPZn+hOwSmg6MdtWfF2ejC3tvgDjWyBM=
|
||||
github.com/kovetskiy/gopencils v0.0.0-20201105104258-2a0bfdd710fb h1:e8UwTXL3Nauw5T847OMHZRhAfQy4ntgQ7PUwgZ2ct4w=
|
||||
github.com/kovetskiy/gopencils v0.0.0-20201105104258-2a0bfdd710fb/go.mod h1:rn9YsgK4kxBDPZn+hOwSmg6MdtWfF2ejC3tvgDjWyBM=
|
||||
github.com/kovetskiy/ko v0.0.0-20190324102900-26b8dd0988bf h1:4QsqgCcPoqDB91dcp4GffoV6TjwfVURaWpjKWFi0ae0=
|
||||
|
6
main.go
6
main.go
@ -213,6 +213,12 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
links, err := mark.ResolveRelativeLinks(api, markdown, ".")
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to resolve relative links")
|
||||
}
|
||||
markdown = mark.ReplaceRelativeLinks(markdown, links)
|
||||
|
||||
if dryRun {
|
||||
compileOnly = true
|
||||
|
||||
|
@ -26,7 +26,8 @@ type API struct {
|
||||
|
||||
// it's deprecated accordingly to Atlassian documentation,
|
||||
// but it's only way to set permissions
|
||||
json *gopencils.Resource
|
||||
json *gopencils.Resource
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
@ -87,8 +88,9 @@ func NewAPI(baseURL string, username string, password string) *API {
|
||||
}
|
||||
|
||||
return &API{
|
||||
rest: rest,
|
||||
json: json,
|
||||
rest: rest,
|
||||
json: json,
|
||||
BaseURL: strings.TrimSuffix(baseURL, "/"),
|
||||
}
|
||||
}
|
||||
|
||||
|
105
pkg/mark/link.go
Normal file
105
pkg/mark/link.go
Normal file
@ -0,0 +1,105 @@
|
||||
package mark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/kovetskiy/mark/pkg/confluence"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
MDLink string
|
||||
Link string
|
||||
}
|
||||
|
||||
// ResolveRelativeLinks finds links in the markdown, and replaces one pointing
|
||||
// to other Markdowns either by own link (if not created to Confluence yet) or
|
||||
// witn actual Confluence link
|
||||
func ResolveRelativeLinks(
|
||||
api *confluence.API,
|
||||
markdown []byte,
|
||||
base string,
|
||||
) (links []Link, collectedErrors error) {
|
||||
currentMarkdownMetadata, onlyMarkdown, err := ExtractMeta(markdown)
|
||||
if err != nil {
|
||||
return links, fmt.Errorf("unable to get metadata from handled markdown file. Error %w", err)
|
||||
}
|
||||
|
||||
currentPageLinkString, collectedErrors := getConfluenceLink(api, currentMarkdownMetadata.Space, currentMarkdownMetadata.Title, collectedErrors)
|
||||
|
||||
submatchall := collectLinksFromMarkdown(string(onlyMarkdown))
|
||||
|
||||
for _, element := range submatchall {
|
||||
link := Link{
|
||||
MDLink: element[1],
|
||||
Link: currentPageLinkString,
|
||||
}
|
||||
// If link points to markdown like target, we build link for that in Confluence
|
||||
if len(element[2]) > 0 {
|
||||
possibleMDFile := element[2]
|
||||
filepath := filepath.Join(base, possibleMDFile)
|
||||
if _, err := os.Stat(filepath); err == nil {
|
||||
linkMarkdown, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
collectedErrors = fmt.Errorf("%w\n unable to read markdown file "+filepath, collectedErrors)
|
||||
continue
|
||||
}
|
||||
// This helps to determine if found link points to file that's not markdown
|
||||
// or have mark required metadata
|
||||
meta, _, err := ExtractMeta(linkMarkdown)
|
||||
if err != nil {
|
||||
collectedErrors = fmt.Errorf("%w\n unable to get metadata from markdown file "+filepath, collectedErrors)
|
||||
continue
|
||||
}
|
||||
|
||||
link.Link, collectedErrors = getConfluenceLink(api, meta.Space, meta.Title, collectedErrors)
|
||||
}
|
||||
}
|
||||
|
||||
if len(element[3]) > 0 {
|
||||
link.Link = currentPageLinkString + "#" + element[2]
|
||||
}
|
||||
|
||||
links = append(links, link)
|
||||
}
|
||||
return links, collectedErrors
|
||||
}
|
||||
|
||||
// ReplaceRelativeLinks replaces relative links between md files (in same
|
||||
// directory structure) with links working in Confluence
|
||||
func ReplaceRelativeLinks(markdown []byte, links []Link) []byte {
|
||||
for _, link := range links {
|
||||
markdown = bytes.ReplaceAll(
|
||||
markdown,
|
||||
[]byte(fmt.Sprintf("](%s)", link.MDLink)),
|
||||
[]byte(fmt.Sprintf("](%s)", link.Link)),
|
||||
)
|
||||
}
|
||||
return markdown
|
||||
}
|
||||
|
||||
// collectLinksFromMarkdown collects all links from given markdown file
|
||||
// (including images and external links)
|
||||
func collectLinksFromMarkdown(markdown string) [][]string {
|
||||
re := regexp.MustCompile("\\[[^\\]]+\\]\\((([^\\)#]+)?#?([^\\)]+)?)\\)")
|
||||
return re.FindAllStringSubmatch(markdown, -1)
|
||||
}
|
||||
|
||||
// getConfluenceLink build (to be) link for Conflunce, and tries to verify from API if there's real link available
|
||||
func getConfluenceLink(api *confluence.API, space, title string, collectedErrors error) (string, error) {
|
||||
link := fmt.Sprintf("%s/display/%s/%s", api.BaseURL, space, url.QueryEscape(title))
|
||||
confluencePage, err := api.FindPage(space, title)
|
||||
if err != nil {
|
||||
collectedErrors = fmt.Errorf("%w\n "+err.Error(), collectedErrors)
|
||||
} else if confluencePage != nil {
|
||||
// Needs baseURL, as REST api response URL doesn't contain subpath ir confluence is server from that
|
||||
link = api.BaseURL + confluencePage.Links.Full
|
||||
}
|
||||
|
||||
return link, collectedErrors
|
||||
}
|
33
pkg/mark/link_test.go
Normal file
33
pkg/mark/link_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
package mark
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLinkFind(t *testing.T) {
|
||||
markdown := `
|
||||
[example1](../path/to/example.md#second-heading)
|
||||
[example2](../path/to/example.md)
|
||||
[example3](#heading-in-document)
|
||||
[Text link that should be put as attachment](../path/to/example.txt)
|
||||
[Image link that should be put as attachment](../path/to/example.png)
|
||||
`
|
||||
|
||||
links := collectLinksFromMarkdown(markdown)
|
||||
|
||||
assert.Equal(t, "../path/to/example.md#second-heading", links[0][1])
|
||||
assert.Equal(t, "../path/to/example.md", links[0][2])
|
||||
assert.Equal(t, "second-heading", links[0][3])
|
||||
|
||||
assert.Equal(t, "../path/to/example.md", links[1][1])
|
||||
assert.Equal(t, "../path/to/example.md", links[1][2])
|
||||
assert.Equal(t, "", links[1][3])
|
||||
|
||||
assert.Equal(t, "#heading-in-document", links[2][1])
|
||||
assert.Equal(t, "", links[2][2])
|
||||
assert.Equal(t, "heading-in-document", links[2][3])
|
||||
|
||||
assert.Equal(t, len(links), 5)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user