Skip to content

Commit

Permalink
writing files/folders fixed, empty nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
g-pavlov committed Oct 1, 2020
1 parent 1f2457c commit 29982ce
Show file tree
Hide file tree
Showing 17 changed files with 625 additions and 197 deletions.
15 changes: 15 additions & 0 deletions pkg/api/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ func (n *Node) GetRootNode() *Node {
return nil
}

// Peers returns the peer nodes of the node
func (n *Node) Peers() []*Node {
var parent *Node
if parent = n.Parent(); parent == nil {
return nil
}
peers := []*Node{}
for _, node := range parent.Nodes {
if node != n {
peers = append(peers, node)
}
}
return peers
}

// FindNodeByContentSource traverses up and then all around the
// tree paths in the node's documentation strcuture, looking for
// a node that has contentSource path nodeContentSource
Expand Down
81 changes: 69 additions & 12 deletions pkg/hugo/fswriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package hugo

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/gardener/docforge/pkg/markdown"
"gopkg.in/yaml.v3"

"github.com/gardener/docforge/pkg/api"
"github.com/gardener/docforge/pkg/writers"
)

Expand All @@ -19,27 +22,81 @@ type FSWriter struct {
// Write implements writers#Write and will rename files that match the
// list in hugo#FSWriter.IndexFileNames to _index.md on first match, first
// renamed basis to serve as section files.
func (w *FSWriter) Write(name, path string, docBlob []byte) error {
func (w *FSWriter) Write(name, path string, docBlob []byte, node *api.Node) error {
if node != nil {
if docBlob == nil && node.Properties != nil && node.Properties["frontmatter"] != nil {
var (
err error
b []byte
_docBlob []byte
)
if b, err = yaml.Marshal(node.Properties["frontmatter"]); err != nil {
return err
}
_name := "_index"
if _docBlob, err = markdown.InsertFrontMatter(b, []byte("")); err != nil {
return err
}
if err := w.Writer.Write(_name, filepath.Join(path, name), _docBlob, node); err != nil {
return err
}
}

if strings.HasSuffix(name, ".md") {
// If there's still no section file at this path, assess if the file
// is a good candiate to become Hugo section file
if _, err := os.Stat(filepath.Join(path, "_index.md")); os.IsNotExist(err) {
if hasIndex([]*api.Node{node}) {
name = "_index"
}
peerNodes := node.Peers()
if peerNodes != nil {
if ns := indexNodes(append([]*api.Node{node}, peerNodes...)); len(ns) > 0 {
names := []string{}
for _, n := range ns {
names = append(names, n.Name)
}
return fmt.Errorf("Multiple peer nodes with property index: true detected: %s", strings.Join(names, ","))
}
}
// if IndexFileNames has values and index file has not been
// identified, try to figure out index file out from node names.
if name != "_index" && len(w.IndexFileNames) > 0 && !hasIndex(peerNodes) {
for _, s := range w.IndexFileNames {
if strings.ToLower(strings.TrimSuffix(name, ".md")) == s {
if strings.ToLower(name) == s {
fmt.Printf("Renaming %s -> _index.md\n", filepath.Join(path, name))
name = "_index.md"
name = "_index"
}
}
} else {
// TODO: see if we can generate _index.md section files when it's all done
}

}

if err := w.Writer.Write(name, path, docBlob); err != nil {
if err := w.Writer.Write(name, path, docBlob, node); err != nil {
return err
}

return nil
}

func hasIndex(nodes []*api.Node) bool {
for _, n := range nodes {
if n.Properties != nil {
index := n.Properties["index"]
if isIndex, ok := index.(bool); ok {
return isIndex
}
}
}
return false
}

func indexNodes(nodes []*api.Node) []*api.Node {
indexNodes := []*api.Node{}
for _, n := range nodes {
if n.Properties != nil {
index := n.Properties["index"]
if isIndex, ok := index.(bool); ok {
if isIndex {
indexNodes = append(indexNodes, n)
}
}
}
}
return indexNodes
}
126 changes: 126 additions & 0 deletions pkg/hugo/fswriter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package hugo

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"

"github.com/gardener/docforge/pkg/api"
"github.com/gardener/docforge/pkg/writers"
"github.com/google/uuid"
)

func TestWrite(t *testing.T) {
testCases := []struct {
name string
path string
docBlob []byte
node *api.Node
wantErr error
wantFileName string
wantContent string
mutate func(writer *FSWriter)
}{
{
name: "test.md",
path: "a/b",
docBlob: []byte("# Test"),
wantErr: nil,
wantFileName: `test.md`,
wantContent: `# Test`,
},
{
name: "test",
path: "a/b",
docBlob: []byte("# Test"),
node: &api.Node{},
wantErr: nil,
wantFileName: `test.md`,
wantContent: `# Test`,
},
{
name: "test",
path: "a/b",
docBlob: nil,
node: &api.Node{
Properties: map[string]interface{}{
"frontmatter": map[string]string{
"title": "Test1",
},
},
},
wantErr: nil,
wantFileName: filepath.Join("test", "_index.md"),
wantContent: `---
title: Test1
---
`,
},
{
name: "README",
path: "a/b",
docBlob: []byte("# Test"),
node: &api.Node{
Name: "README",
Properties: map[string]interface{}{
"index": true,
},
ContentSelectors: []api.ContentSelector{
api.ContentSelector{
Source: "github.com",
},
},
},
wantErr: nil,
wantFileName: filepath.Join("_index.md"),
wantContent: `# Test`,
mutate: func(writer *FSWriter) {
writer.IndexFileNames = []string{"readme"}
},
},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
testFolder := fmt.Sprintf("test%s", uuid.New().String())
testPath := filepath.Join(os.TempDir(), testFolder)
fs := &FSWriter{
Writer: &writers.FSWriter{
Root: testPath,
},
}
if tc.mutate != nil {
tc.mutate(fs)
}
fPath := filepath.Join(testPath, tc.path, tc.wantFileName)
defer func() {
if err := os.RemoveAll(testPath); err != nil {
t.Fatalf("%v\n", err)
}
}()

if tc.node != nil {
tc.node.SetParentsDownwards()
}
err := fs.Write(tc.name, tc.path, tc.docBlob, tc.node)

if err != tc.wantErr {
t.Errorf("expected err %v != %v", tc.wantErr, err)
}
if _, err := os.Stat(fPath); tc.wantErr == nil && os.IsNotExist(err) {
t.Errorf("expected file %s not found: %v", fPath, err)
}
var (
gotContent []byte
)
if gotContent, err = ioutil.ReadFile(fPath); err != nil {
t.Errorf("unexpected error opening file %v", err)
}
if !reflect.DeepEqual(gotContent, []byte(tc.wantContent)) {
t.Errorf("expected content \n%v\n != \n%v\n", tc.wantContent, string(gotContent))
}
})
}
}
25 changes: 21 additions & 4 deletions pkg/markdown/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ func TransformLinks(documentBlob []byte, callback OnLink) ([]byte, error) {
return documentBlob, nil
}

// ErrFrontMatterNotClosed is raised to signal
// that the rules for defining a frontmatter element
// in a markdown document have been violated
var ErrFrontMatterNotClosed error = errors.New("Missing closing frontmatter `---` found")

// StripFrontMatter splits a provided document into front-matter
// and content.
func StripFrontMatter(b []byte) ([]byte, []byte, error) {
Expand All @@ -73,7 +78,16 @@ func StripFrontMatter(b []byte) ([]byte, []byte, error) {
for {
line, err := buf.ReadString('\n')

if err == io.EOF {
if errors.Is(err, io.EOF) {
// handle documents that contain only forntmatter
// and no line ending after closing ---
if started && yamlEnd == 0 {
if l := strings.TrimSpace(line); l == "---" {
yamlEnd = len(b) - buf.Len() - len([]byte(line))
contentStart = len(b)
}

}
break
}

Expand All @@ -82,7 +96,9 @@ func StripFrontMatter(b []byte) ([]byte, []byte, error) {
}

if l := strings.TrimSpace(line); l != "---" {
// Only whitespace is acceptable before fron-matter.
// Only whitespace is acceptable before front-matter
// Any other preceding text is interpeted as frontmater-less
// document
if !started && len(l) > 0 {
return nil, b, nil
}
Expand All @@ -100,7 +116,7 @@ func StripFrontMatter(b []byte) ([]byte, []byte, error) {
}

if started && yamlEnd == 0 {
return nil, nil, errors.New("Missing closing front-matter `---` mark found")
return nil, nil, ErrFrontMatterNotClosed
}

fm := b[yamlBeg:yamlEnd]
Expand All @@ -121,7 +137,8 @@ func InsertFrontMatter(fm []byte, content []byte) ([]byte, error) {
}
buf := bytes.NewBuffer([]byte("---\n"))
buf.Write(fm)
buf.WriteString("---\n\n")
// TODO: configurable empty line after frontmatter
buf.WriteString("---\n")
buf.Write(content)
if data, err = ioutil.ReadAll(buf); err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 29982ce

Please sign in to comment.