215 lines
5.6 KiB
Go
Raw Normal View History

2022-09-05 22:38:38 +02:00
package main
import (
"encoding/base64"
"encoding/json"
2022-09-06 22:13:42 +02:00
"fmt"
2022-09-05 22:38:38 +02:00
"io"
"log"
"net/http"
2022-09-06 22:13:42 +02:00
"os"
"text/template"
2022-09-05 22:38:38 +02:00
"github.com/alexflint/go-arg"
)
type NodeData struct {
2022-09-06 22:13:42 +02:00
Id string
Title string
2022-09-05 22:38:38 +02:00
}
type Node struct {
2022-09-06 22:13:42 +02:00
Children []*Node
Data NodeData
2022-09-05 22:38:38 +02:00
}
2022-09-06 22:13:42 +02:00
func (n Node) writeHTML(w io.Writer) {
tmpl, err := template.New("root").Parse(`
{{ define "tree" }}
{{ if gt (len .Children) 0 }}
<ul>
{{ range $index, $element := .Children}}
<li>
{{ $element.Data.Title }}
{{ template "tree" $element }}
</li>
{{ end }}
</ul>
{{ end }}
{{ end }}
<html>
{{ template "tree" . }}
</html>
`)
if err != nil {
fmt.Println("ERROR creating template")
2022-09-05 22:38:38 +02:00
}
2022-09-06 22:13:42 +02:00
err = tmpl.Execute(w, n)
if err != nil {
fmt.Printf("ERROR executing template: %v", err)
2022-09-05 22:38:38 +02:00
}
}
func (n *Node) getChildWithID(id string) (node *Node, hasChild bool) {
2022-09-06 22:13:42 +02:00
for _, c := range n.Children {
if c.Data.Id == id {
2022-09-05 22:38:38 +02:00
return c, true
}
}
empty := Node{}
return &empty, false
}
func (n *Node) addChild(child *Node) {
2022-09-06 22:13:42 +02:00
n.Children = append(n.Children, child)
2022-09-05 22:38:38 +02:00
}
type spaceResult struct {
Results []struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Title string `json:"title"`
Ancestors []struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Title string `json:"title"`
MacroRenderedOutput struct {
} `json:"macroRenderedOutput"`
Extensions struct {
Position int `json:"position"`
} `json:"extensions"`
Expandable struct {
Container string `json:"container"`
Metadata string `json:"metadata"`
Restrictions string `json:"restrictions"`
History string `json:"history"`
Body string `json:"body"`
Version string `json:"version"`
Descendants string `json:"descendants"`
Space string `json:"space"`
ChildTypes string `json:"childTypes"`
Operations string `json:"operations"`
SchedulePublishDate string `json:"schedulePublishDate"`
Children string `json:"children"`
Ancestors string `json:"ancestors"`
} `json:"_expandable"`
Links struct {
Self string `json:"self"`
Tinyui string `json:"tinyui"`
Editui string `json:"editui"`
Webui string `json:"webui"`
} `json:"_links"`
} `json:"ancestors"`
MacroRenderedOutput struct {
} `json:"macroRenderedOutput"`
Extensions struct {
Position int `json:"position"`
} `json:"extensions"`
Expandable struct {
ChildTypes string `json:"childTypes"`
Container string `json:"container"`
Metadata string `json:"metadata"`
Operations string `json:"operations"`
SchedulePublishDate string `json:"schedulePublishDate"`
Children string `json:"children"`
Restrictions string `json:"restrictions"`
History string `json:"history"`
Body string `json:"body"`
Version string `json:"version"`
Descendants string `json:"descendants"`
Space string `json:"space"`
} `json:"_expandable"`
Links struct {
Self string `json:"self"`
Tinyui string `json:"tinyui"`
Editui string `json:"editui"`
Webui string `json:"webui"`
} `json:"_links"`
} `json:"results"`
Start int `json:"start"`
Limit int `json:"limit"`
Size int `json:"size"`
Links struct {
Base string `json:"base"`
Context string `json:"context"`
Next string `json:"next"`
Self string `json:"self"`
} `json:"_links"`
}
func getSpaceChildren(url, user, token string) spaceResult {
b64Auth := base64.StdEncoding.EncodeToString([]byte(user + ":" + token))
client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Authorization", "Basic "+b64Auth)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Error fetching url: %v, status code: %v", url, resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var result spaceResult
json.Unmarshal(body, &result)
return result
}
func createPageTree(s *spaceResult) {
var parent *Node
2022-09-06 22:13:42 +02:00
root := Node{Data: NodeData{Title: "root"}}
2022-09-05 22:38:38 +02:00
for _, page := range s.Results {
parent = &root
// ignore the root node
if len(page.Ancestors) == 0 {
continue
}
// create branches from ancestors
for i, ancestor := range page.Ancestors {
// skip root node
if i == 0 {
continue
}
node, hasChild := parent.getChildWithID(ancestor.ID)
if hasChild {
parent = node
continue
}
2022-09-06 22:13:42 +02:00
n := Node{Data: NodeData{Id: ancestor.ID, Title: ancestor.Title}}
2022-09-05 22:38:38 +02:00
parent.addChild(&n)
parent = &n
}
// create leaf node
2022-09-06 22:13:42 +02:00
n := Node{Data: NodeData{Id: page.ID, Title: page.Title}}
2022-09-05 22:38:38 +02:00
parent.addChild(&n)
}
2022-09-06 22:13:42 +02:00
fname := "tree.html"
f, err := os.Create(fname)
if err != nil {
log.Fatalf("Failed to open %v", fname)
}
defer f.Close()
root.writeHTML(f)
2022-09-05 22:38:38 +02:00
}
func main() {
var args struct {
Baseurl string `arg:"required" help:"Base URL of the Confluence instance (required)"`
Spacekey string `arg:"required" help:"Spacekey to export (required)"`
User string `arg:"required" help:"User used for authentication (required)"`
Token string `arg:"required" help:"Token used for authentication (required)"`
}
arg.MustParse(&args)
url := args.Baseurl + "/wiki/rest/api/space/" + args.Spacekey + "/content/page?expand=ancestors&limit=9999"
result := getSpaceChildren(url, args.User, args.Token)
createPageTree(&result)
}