Skip to content

Commit

Permalink
Merge pull request #49 from ctreminiom/feature/confluence-cloud
Browse files Browse the repository at this point in the history
Feature/confluence cloud
  • Loading branch information
ctreminiom authored Jul 1, 2021
2 parents 300505a + 405f453 commit 1d18342
Show file tree
Hide file tree
Showing 57 changed files with 13,830 additions and 8 deletions.
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ go-atlassian is a Go module that enables the interaction with the Atlassian Clou

## ✨ Features

### 🛫 Jira Software Cloud
### 🏷 Jira Software Cloud
- CRUD Application Roles, Dashboards, Filters, Groups, Issues.
- Search Issues using the JQL query.
- Add attachments into JIRA issues, and transition issues.
Expand Down Expand Up @@ -59,7 +59,7 @@ go-atlassian is a Go module that enables the interaction with the Atlassian Clou
- Get SCIM Schema(s).

### 🛰️ Confluence Cloud
- In Development 🔨
- CRUD Contents and Spaces

### 🚠 Jira Agile Cloud
- Get the board backlogs issues.
Expand Down Expand Up @@ -90,6 +90,9 @@ $ go get -u -v github.com/ctreminiom/go-atlassian/jira/

## Atlassian Cloud Admin
$ go get -u -v github.com/ctreminiom/go-atlassian/admin/

## Confluence Cloud
$ go get -u -v github.com/ctreminiom/go-atlassian/confluence/
```

## 📓 Documentation
Expand Down Expand Up @@ -144,8 +147,17 @@ func main() {
}
````

## ⭐️ Project assistance
## 🧳 JetBrains OS licenses
`go-atlassian` had been being developed with GoLand under the **free JetBrains Open Source license(s)** granted by JetBrains s.r.o., hence I would like to express my thanks here.

<img src="./static/jetbrains-logo.svg">

## 🪐 GitBook Host
`go-atlassian` documentation is hosted using the GitBook non-profit / open-source plan so hence I would like to express my thanks here.

<img src="./static/gitbook-logo.svg">

## ⭐️ Project assistance
If you want to say **thank you** or/and support active development of `go-atlassian`:

- Add a [GitHub Star](https://github.com/ctreminiom/go-atlassian) to the project.
Expand All @@ -165,11 +177,6 @@ but focused on Cloud solutions.
go test -v ./...
```

## 💳 Credits
In addition to all the contributors we would like to thank these vendors:
- **Atlassian** for developing such a powerful ecosystem.
- **Gitbook** for provided full features for open-source projects

## 📝 License
Copyright © 2021 [Carlos Treminio](https://github.com/ctreminiom).
This project is [MIT](https://opensource.org/licenses/MIT) licensed.
Expand Down
25 changes: 25 additions & 0 deletions confluence/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package confluence

type AuthenticationService struct {
client *Client

basicAuthProvided bool
mail, token string

userAgentProvided bool
agent string
}

func (a *AuthenticationService) SetBasicAuth(mail, token string) {

a.mail = mail
a.token = token

a.basicAuthProvided = true
}

func (a *AuthenticationService) SetUserAgent(agent string) {

a.agent = agent
a.userAgentProvided = true
}
182 changes: 182 additions & 0 deletions confluence/confluence.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package confluence

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strings"
)

type Client struct {
HTTP *http.Client
Site *url.URL

Auth *AuthenticationService
Content *ContentService
Space *SpaceService
}

func New(httpClient *http.Client, site string) (client *Client, err error) {

if httpClient == nil {
httpClient = http.DefaultClient
}

if !strings.HasSuffix(site, "/") {
site += "/"
}

siteAsURL, err := url.Parse(site)
if err != nil {
return
}

client = &Client{}
client.HTTP = httpClient
client.Site = siteAsURL
client.Auth = &AuthenticationService{client: client}

client.Content = &ContentService{
client: client,
Attachment: &ContentAttachmentService{client: client},
ChildrenDescendant: &ContentChildrenDescendantService{client: client},
Comment: &ContentCommentService{client: client},
Permission: &ContentPermissionService{client: client},
}

client.Space = &SpaceService{client: client}
return
}

func (c *Client) newRequest(ctx context.Context, method, apiEndpoint string, payload io.Reader) (request *http.Request, err error) {

relativePath, err := url.Parse(apiEndpoint)
if err != nil {
return nil, fmt.Errorf(urlParsedError, err.Error())
}

var endpoint = c.Site.ResolveReference(relativePath).String()

request, err = http.NewRequestWithContext(ctx, method, endpoint, payload)
if err != nil {
return nil, fmt.Errorf(requestCreationError, err.Error())
}

if c.Auth.basicAuthProvided {
request.SetBasicAuth(c.Auth.mail, c.Auth.token)
}

if c.Auth.userAgentProvided {
request.Header.Set("User-Agent", c.Auth.agent)
}

return
}

func (c *Client) Call(request *http.Request, structure interface{}) (result *ResponseScheme, err error) {
response, _ := c.HTTP.Do(request)
return transformTheHTTPResponse(response, structure)
}

func transformStructToReader(structure interface{}) (reader io.Reader, err error) {

if structure == nil || reflect.ValueOf(structure).IsNil() {
return nil, structureNotParsedError
}

structureAsBodyBytes, err := json.Marshal(structure)
if err != nil {
return nil, err
}

return bytes.NewReader(structureAsBodyBytes), nil
}

func transformTheHTTPResponse(response *http.Response, structure interface{}) (result *ResponseScheme, err error) {

if response == nil {
return nil, errors.New("validation failed, please provide a http.Response pointer")
}

responseTransformed := &ResponseScheme{}
responseTransformed.Code = response.StatusCode
responseTransformed.Endpoint = response.Request.URL.String()
responseTransformed.Method = response.Request.Method

var wasSuccess = response.StatusCode >= 200 && response.StatusCode < 300
if !wasSuccess {

if response.StatusCode == http.StatusBadRequest {

responseAsBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return responseTransformed, err
}

var apiError ApiErrorResponseScheme
if err = json.Unmarshal(responseAsBytes, &apiError); err != nil {
return responseTransformed, err
}

responseTransformed.API = &apiError
return responseTransformed, fmt.Errorf(requestFailedError, response.StatusCode)
}
return responseTransformed, fmt.Errorf(requestFailedError, response.StatusCode)
}

responseAsBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return responseTransformed, err
}

if structure != nil {
if err = json.Unmarshal(responseAsBytes, &structure); err != nil {
return responseTransformed, err
}
}

responseTransformed.Bytes.Write(responseAsBytes)

return responseTransformed, nil
}

type ResponseScheme struct {
Code int
Endpoint string
Method string
API *ApiErrorResponseScheme
Bytes bytes.Buffer
Headers map[string][]string
}

type ApiErrorResponseScheme struct {
StatusCode int `json:"statusCode"`
Message string `json:"message"`
Data *ApiErrorResponseDataScheme `json:"data"`
}

type ApiErrorResponseDataScheme struct {
Authorized bool `json:"authorized"`
Valid bool `json:"valid"`
Errors []struct {
Message struct {
Key string `json:"key"`
Args []interface{} `json:"args"`
} `json:"message"`
} `json:"errors"`
Successful bool `json:"successful"`
}

var (
requestFailedError = "request failed. Please analyze the request body for more details. Status Code: %d"
requestCreationError = "request creation failed: %v"
urlParsedError = "URL parsing failed: %v"
structureNotParsedError = errors.New("failed to parse the interface pointer, please provide a valid one")
)
Loading

0 comments on commit 1d18342

Please sign in to comment.