Skip to content

Commit 7abe395

Browse files
authored
Merge pull request #3 from seanson/add_confluence_server_support
Add confluence server support
2 parents 9760d41 + f885be7 commit 7abe395

File tree

8 files changed

+115
-41
lines changed

8 files changed

+115
-41
lines changed

CHANGELOG.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
## To be released
1+
## 0.2.0
22

33
FEATURES:
44

5-
* **New Resource:** `confluence_content`
5+
- **Experimental:** Support for Confluence Server
6+
- Added additional parameters for public and api sites and schemes for Confluence Server
7+
- Fix: Fixed index error when creating resources without a parent
8+
9+
## 0.1.0
10+
11+
FEATURES:
12+
13+
- **New Resource:** `confluence_content`

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.2.0

confluence/attachment.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ type Metadata struct {
2626

2727
// AttachmentLinks is part of Content
2828
type AttachmentLinks struct {
29-
Context string `json:"context,omitempty"` // "/wiki"
29+
Context string `json:"context,omitempty"` // ""
3030
Download string `json:"download,omitempty"` // prefix with Context
3131
}
3232

3333
func (c *Client) CreateAttachment(attachment *Attachment, data, pageId string) (*Attachment, error) {
3434
var response AttachmentResults
35-
path := fmt.Sprintf("/wiki/rest/api/content/%s/child/attachment", pageId)
35+
path := fmt.Sprintf("/rest/api/content/%s/child/attachment", pageId)
3636
if err := c.PostForm(path, attachment.Title, data, &response); err != nil {
3737
return nil, err
3838
}
@@ -45,7 +45,7 @@ func (c *Client) CreateAttachment(attachment *Attachment, data, pageId string) (
4545
func (c *Client) UpdateAttachment(attachment *Attachment, data, pageId string) (*Attachment, error) {
4646
var response AttachmentResults
4747
attachment.Version.Number++
48-
path := fmt.Sprintf("/wiki/rest/api/content/%s/child/attachment/%s", pageId, attachment.Id)
48+
path := fmt.Sprintf("/rest/api/content/%s/child/attachment/%s", pageId, attachment.Id)
4949
if err := c.PutForm(path, attachment.Title, data, &response); err != nil {
5050
return nil, err
5151
}
@@ -57,24 +57,23 @@ func (c *Client) UpdateAttachment(attachment *Attachment, data, pageId string) (
5757

5858
func (c *Client) GetAttachment(id string) (*Attachment, error) {
5959
var response Attachment
60-
path := fmt.Sprintf("/wiki/rest/api/content/%s?expand=version", id)
60+
path := fmt.Sprintf("/rest/api/content/%s?expand=version", id)
6161
if err := c.Get(path, &response); err != nil {
6262
return nil, err
6363
}
6464
return &response, nil
6565
}
6666

6767
func (c *Client) GetAttachmentBody(attachment *Attachment) (string, error) {
68-
path := attachment.Links.Context + attachment.Links.Download
69-
result, err := c.GetString(path)
68+
result, err := c.GetString(attachment.Links.Download)
7069
if err != nil {
7170
return "", err
7271
}
7372
return result, nil
7473
}
7574

7675
func (c *Client) DeleteAttachment(id, pageId string) error {
77-
path := fmt.Sprintf("/wiki/rest/api/content/%s", id)
76+
path := fmt.Sprintf("/rest/api/content/%s", id)
7877
if err := c.Delete(path); err != nil {
7978
return err
8079
}

confluence/client.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@ import (
1616
type Client struct {
1717
client *http.Client
1818
baseURL *url.URL
19+
basePath string
1920
publicURL *url.URL
2021
}
2122

2223
// NewClientInput provides information to connect to the Confluence API
2324
type NewClientInput struct {
24-
site string
25-
user string
26-
token string
25+
site string
26+
siteScheme string
27+
publicSite string
28+
publicSiteScheme string
29+
context string
30+
user string
31+
token string
2732
}
2833

2934
// ErrorResponse describes why a request failed
@@ -41,16 +46,30 @@ type ErrorResponse struct {
4146
// NewClient returns an authenticated client ready to use
4247
func NewClient(input *NewClientInput) *Client {
4348
publicURL := url.URL{
44-
Scheme: "https",
45-
Host: input.site + ".atlassian.net",
49+
Scheme: input.publicSiteScheme,
50+
Host: input.site,
51+
}
52+
if input.publicSite != "" {
53+
publicURL.Host = input.publicSite
54+
}
55+
56+
basePath := input.context
57+
58+
// Default to /wiki if using Confluence Cloud`
59+
if strings.HasSuffix(input.site, ".atlassian.net") {
60+
basePath = "/wiki"
61+
}
62+
baseURL := url.URL{
63+
Scheme: input.siteScheme,
64+
Host: input.site,
4665
}
47-
baseURL := publicURL
4866
baseURL.User = url.UserPassword(input.user, input.token)
4967
return &Client{
5068
client: &http.Client{
5169
Timeout: time.Second * 10,
5270
},
5371
baseURL: &baseURL,
72+
basePath: basePath,
5473
publicURL: &publicURL,
5574
}
5675
}
@@ -159,7 +178,8 @@ func (c *Client) do(method, path, contentType string, body *bytes.Buffer, result
159178

160179
// do uses the client to send a specified request
161180
func (c *Client) doRaw(method, path, contentType string, body *bytes.Buffer) (*bytes.Buffer, error) {
162-
u, err := c.baseURL.Parse(path)
181+
fullPath := c.basePath + path
182+
u, err := c.baseURL.Parse(fullPath)
163183
if err != nil {
164184
return nil, err
165185
}
@@ -193,7 +213,7 @@ func (c *Client) doRaw(method, path, contentType string, body *bytes.Buffer) (*b
193213
}
194214
s := body.String()
195215
return nil, fmt.Errorf("%s\n\n%s %s\n%s\n\n%s",
196-
resp.Status, method, path, s, responseBody)
216+
resp.Status, method, fullPath, s, responseBody)
197217
}
198218
result := new(bytes.Buffer)
199219
_, err = result.ReadFrom(resp.Body)

confluence/content.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ type Version struct {
4545

4646
func (c *Client) CreateContent(content *Content) (*Content, error) {
4747
var response Content
48-
if err := c.Post("/wiki/rest/api/content", content, &response); err != nil {
48+
if err := c.Post("/rest/api/content", content, &response); err != nil {
4949
return nil, err
5050
}
5151
return &response, nil
5252
}
5353

5454
func (c *Client) GetContent(id string) (*Content, error) {
5555
var response Content
56-
path := fmt.Sprintf("/wiki/rest/api/content/%s?expand=space,body.storage,version,ancestors", id)
56+
path := fmt.Sprintf("/rest/api/content/%s?expand=space,body.storage,version,ancestors", id)
5757
if err := c.Get(path, &response); err != nil {
5858
return nil, err
5959
}
@@ -63,15 +63,15 @@ func (c *Client) GetContent(id string) (*Content, error) {
6363
func (c *Client) UpdateContent(content *Content) (*Content, error) {
6464
var response Content
6565
content.Version.Number++
66-
path := fmt.Sprintf("/wiki/rest/api/content/%s", content.Id)
66+
path := fmt.Sprintf("/rest/api/content/%s", content.Id)
6767
if err := c.Put(path, content, &response); err != nil {
6868
return nil, err
6969
}
7070
return &response, nil
7171
}
7272

7373
func (c *Client) DeleteContent(id string) error {
74-
path := fmt.Sprintf("/wiki/rest/api/content/%s", id)
74+
path := fmt.Sprintf("/rest/api/content/%s", id)
7575
if err := c.Delete(path); err != nil {
7676
return err
7777
}

confluence/provider.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,43 @@ func Provider() *schema.Provider {
1111
"site": {
1212
Type: schema.TypeString,
1313
Required: true,
14-
Description: "Cloud Confluence Site Name (the name before atlassian.net)",
14+
Description: "Confluence hostname (<name>.atlassian.net if using Cloud Confluence, otherwise hostname)",
1515
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_SITE", nil),
1616
},
17+
"site_scheme": {
18+
Type: schema.TypeString,
19+
Required: true,
20+
Description: "Optional https or http scheme to use for API calls",
21+
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_SITE_SCHEME", "https"),
22+
},
23+
"public_site": {
24+
Type: schema.TypeString,
25+
Required: true,
26+
Description: "Optional public Confluence Server hostname if different than API hostname",
27+
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_PUBLIC_SITE", ""),
28+
},
29+
"public_site_scheme": {
30+
Type: schema.TypeString,
31+
Required: true,
32+
Description: "Optional https or http scheme to use for public site URLs",
33+
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_PUBLIC_SITE_SCHEME", "https"),
34+
},
35+
"context": {
36+
Type: schema.TypeString,
37+
Required: true,
38+
Description: "Confluence path context (Will default to /wiki if using an atlassian.net hostname)",
39+
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_CONTEXT", ""),
40+
},
1741
"user": {
1842
Type: schema.TypeString,
1943
Required: true,
20-
Description: "User's email address",
44+
Description: "User's email address for Cloud Confluence or username for Confluence Server",
2145
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_USER", nil),
2246
},
2347
"token": {
2448
Type: schema.TypeString,
2549
Required: true,
26-
Description: "Confluence API Token for user",
50+
Description: "Confluence API Token for Cloud Confluence or password for Confluence Server",
2751
DefaultFunc: schema.EnvDefaultFunc("CONFLUENCE_TOKEN", nil),
2852
},
2953
},
@@ -38,8 +62,12 @@ func Provider() *schema.Provider {
3862

3963
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
4064
return NewClient(&NewClientInput{
41-
site: d.Get("site").(string),
42-
token: d.Get("token").(string),
43-
user: d.Get("user").(string),
65+
site: d.Get("site").(string),
66+
siteScheme: d.Get("site_scheme").(string),
67+
publicSite: d.Get("public_site").(string),
68+
publicSiteScheme: d.Get("public_site_scheme").(string),
69+
context: d.Get("context").(string),
70+
token: d.Get("token").(string),
71+
user: d.Get("user").(string),
4472
}), nil
4573
}

confluence/resource_content.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ func updateResourceDataFromContent(d *schema.ResourceData, content *Content, cli
139139
"title": content.Title,
140140
"version": content.Version.Number,
141141
"url": client.URL(content.Links.Context + content.Links.WebUI),
142-
"parent": content.Ancestors[len(content.Ancestors)-1].Id, // the last ancestor is the parent
142+
}
143+
if len(content.Ancestors) > 1 {
144+
m["parent"] = content.Ancestors[len(content.Ancestors)-1].Id
143145
}
144146
for k, v := range m {
145147
err := d.Set(k, v)

docs/index.md

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ layout: "confluence"
33
page_title: "Provider: Confluence"
44
sidebar_current: "docs-confluence-index"
55
description: |-
6-
The Confluence provider is used to interact with Cloud Confluence.
6+
The Confluence provider is used to interact with Confluence.
77
It can automate the publishing of content and is often used to publish
88
information about other resources created in terraform.
99
---
1010

1111
# Confluence Provider
1212

13-
The Confluence provider is used to interact with the Cloud Confluence. The
13+
The Confluence provider is used to interact with the Confluence. The
1414
provider needs to be configured with the proper credentials before it can be
1515
used.
1616

@@ -20,7 +20,7 @@ Use the navigation to the left to read about the available data sources.
2020

2121
```hcl
2222
provider "confluence" {
23-
site = "my-site"
23+
site = "my-site.atlassian.net"
2424
user = "my-user"
2525
token = "my-token"
2626
}
@@ -38,14 +38,31 @@ Static credentials must be passed to the provider block.
3838

3939
## Argument Reference
4040

41-
* `site` - (Required) The site is the name of the site and appears in your wiki
42-
URL (https://*my-site*.atlassian.net/wiki/spaces/my-space/). This can also be
43-
set via the `CONFLUENCE_SITE` environment variable.
41+
- `site` - (Required) For Confluence Cloud: The site is the name of the site
42+
and appears in your wiki URL (https://_my-site.atlassian.net_/wiki/spaces/my-space/).
43+
For Confluence Server users this should be the hostname of your Confluence
44+
instance that can receive `/rest/api` requests. This can also be set via the
45+
`CONFLUENCE_SITE` environment variable.
4446

45-
* `user` - (Required) The user is your user's email address. This can also be
46-
set via the `CONFLUENCE_USER` environment variable.
47+
- `site_schema` - (Optional) Set the schema for connecting to the REST API.
48+
Defaults to `https`. This can also be set via the `CONFLUENCE_SITE_SCHEMA`
49+
environment variable.
4750

48-
* `token` - (Required) The token is a secret every user can generate. It is
49-
similar to a password and should be treated as such. This can also be set via
50-
the `CONFLUENCE_TOKEN` environment variable. See [Manage your
51-
account](https://id.atlassian.com/manage/api-tokens).
51+
- `public_site` - (Optional) For Confluence Server instances where your
52+
Confluence site URL is different than the hostname that serves REST API
53+
requests. Defaults to `site` if not set. This can also be set via the
54+
`CONFLUENCE_PUBLIC_SITE` environment variable.
55+
56+
- `public_site_schema` - (Optional) Set the schema for generated public URLs.
57+
Defaults to `https`. This can also be set via the `CONFLUENCE_PUBLIC_SITE_SCHEMA`
58+
environment variable.
59+
60+
- `user` - (Required) For Confluence Cloud the user is your user's email
61+
address. For Confluence Server this is the username of the user to login.
62+
This can also be set via the `CONFLUENCE_USER` environment variable.
63+
64+
- `token` - (Required) For Confluence Cloud the token is a secret every user
65+
can generate. It is similar to a password and should be treated as such. For
66+
Confluence Server, this is the password of the user. This can also be set via
67+
the `CONFLUENCE_TOKEN` environment variable. For Cloud token details, see
68+
[Manage your account](https://id.atlassian.com/manage/api-tokens).

0 commit comments

Comments
 (0)