From 82e36eacc1b0fd8775c5d7c0d978527f4f957cac Mon Sep 17 00:00:00 2001 From: Johannes Rothe Date: Mon, 5 Sep 2022 22:38:38 +0200 Subject: [PATCH] Create pagetree from API request --- go.mod | 7 +++ go.sum | 16 +++++ main.go | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4400047 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module confluence-pagetree + +go 1.18 + +require github.com/alexflint/go-arg v1.4.3 + +require github.com/alexflint/go-scalar v1.1.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9ef1991 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo= +github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA= +github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM= +github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..fb3bb7d --- /dev/null +++ b/main.go @@ -0,0 +1,190 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "io" + "log" + "net/http" + "strings" + + "github.com/alexflint/go-arg" +) + +type NodeData struct { + id string + title string +} + +type Node struct { + children []*Node + data NodeData +} + +func (n Node) printChildren(level int) { + log.Printf("%v%v", strings.Repeat("\t", level), n.data.title) + // leaf note + if len(n.children) == 0 { + return + } + for _, child := range n.children { + child.printChildren(level + 1) + } +} + +func (n *Node) getChildWithID(id string) (node *Node, hasChild bool) { + for _, c := range n.children { + if c.data.id == id { + return c, true + } + } + empty := Node{} + return &empty, false +} + +func (n *Node) addChild(child *Node) { + n.children = append(n.children, child) +} + +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 + root := Node{data: NodeData{title: "root"}} + 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 + } + n := Node{data: NodeData{id: ancestor.ID, title: ancestor.Title}} + parent.addChild(&n) + parent = &n + } + // create leaf node + n := Node{data: NodeData{id: page.ID, title: page.Title}} + parent.addChild(&n) + } + root.printChildren(0) +} + +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) +}