Skip to content

Commit

Permalink
Merge pull request #184 from ctreminiom/feature/confluence-views
Browse files Browse the repository at this point in the history
✨ Created the Analytics Confluence service.
  • Loading branch information
ctreminiom authored Apr 3, 2023
2 parents fcb3966 + fac8be5 commit 36300c9
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 8 deletions.
18 changes: 10 additions & 8 deletions confluence/api_client_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,21 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
client.Label = internal.NewLabelService(client)
client.Search = internal.NewSearchService(client)
client.LongTask = internal.NewTaskService(client)
client.Analytics = internal.NewAnalyticsService(client)

return client, nil
}

type Client struct {
HTTP common.HttpClient
Site *url.URL
Auth common.Authentication
Content *internal.ContentService
Space *internal.SpaceService
Label *internal.LabelService
Search *internal.SearchService
LongTask *internal.TaskService
HTTP common.HttpClient
Site *url.URL
Auth common.Authentication
Content *internal.ContentService
Space *internal.SpaceService
Label *internal.LabelService
Search *internal.SearchService
LongTask *internal.TaskService
Analytics *internal.AnalyticsService
}

func (c *Client) NewFormRequest(ctx context.Context, method, apiEndpoint, contentType string, payload io.Reader) (*http.Request, error) {
Expand Down
105 changes: 105 additions & 0 deletions confluence/internal/analytics_impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package internal

import (
"context"
"fmt"
model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
"github.com/ctreminiom/go-atlassian/service"
"github.com/ctreminiom/go-atlassian/service/confluence"
"net/http"
"net/url"
"strings"
)

func NewAnalyticsService(client service.Client) *AnalyticsService {

return &AnalyticsService{
internalClient: &internalAnalyticsServiceImpl{c: client},
}
}

type AnalyticsService struct {
internalClient confluence.AnalyticsConnector
}

// Get gets the total number of views a piece of content has.
//
// GET /wiki/rest/api/analytics/content/{contentId}/views
//
// https://docs.go-atlassian.io/confluence-cloud/analytics#get-views
func (a *AnalyticsService) Get(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error) {
return a.internalClient.Get(ctx, contentId, fromDate)
}

// Distinct get the total number of distinct viewers a piece of content has.
//
// GET /wiki/rest/api/analytics/content/{contentId}/viewers
//
// https://docs.go-atlassian.io/confluence-cloud/analytics#get-viewers
func (a *AnalyticsService) Distinct(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error) {
return a.internalClient.Distinct(ctx, contentId, fromDate)
}

type internalAnalyticsServiceImpl struct {
c service.Client
}

func (i *internalAnalyticsServiceImpl) Get(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error) {

if contentId == "" {
return nil, nil, model.ErrNoContentIDError
}

var endpoint strings.Builder
endpoint.WriteString(fmt.Sprintf("wiki/rest/api/analytics/content/%v/views", contentId))

if fromDate != "" {
query := url.Values{}
query.Add("fromDate", fromDate)

endpoint.WriteString(fmt.Sprintf("?%v", query.Encode()))
}

request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, nil, err
}

views := new(model.ContentViewScheme)
response, err := i.c.Call(request, views)
if err != nil {
return nil, response, err
}

return views, response, nil
}

func (i *internalAnalyticsServiceImpl) Distinct(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error) {

if contentId == "" {
return nil, nil, model.ErrNoContentIDError
}

var endpoint strings.Builder
endpoint.WriteString(fmt.Sprintf("wiki/rest/api/analytics/content/%v/viewers", contentId))

if fromDate != "" {
query := url.Values{}
query.Add("fromDate", fromDate)

endpoint.WriteString(fmt.Sprintf("?%v", query.Encode()))
}

request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, nil, err
}

views := new(model.ContentViewScheme)
response, err := i.c.Call(request, views)
if err != nil {
return nil, response, err
}

return views, response, nil
}
232 changes: 232 additions & 0 deletions confluence/internal/analytics_impl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package internal

import (
"context"
"errors"
model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
"github.com/ctreminiom/go-atlassian/service"
"github.com/ctreminiom/go-atlassian/service/mocks"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)

func Test_internalAnalyticsServiceImpl_Get(t *testing.T) {

type fields struct {
c service.Client
}

type args struct {
ctx context.Context
contentId, fromDate string
}

testCases := []struct {
name string
fields fields
args args
on func(*fields)
wantErr bool
Err error
}{
{
name: "when the parameters are correct",
args: args{
ctx: context.TODO(),
contentId: "2337372172371",
fromDate: "2023-10-03",
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("NewRequest",
context.Background(),
http.MethodGet,
"wiki/rest/api/analytics/content/2337372172371/views?fromDate=2023-10-03",
nil).
Return(&http.Request{}, nil)

client.On("Call",
&http.Request{},
&model.ContentViewScheme{}).
Return(&model.ResponseScheme{}, nil)

fields.c = client
},
},

{
name: "when the http request cannot be created",
args: args{
ctx: context.TODO(),
contentId: "2337372172371",
fromDate: "2023-10-03",
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("NewRequest",
context.Background(),
http.MethodGet,
"wiki/rest/api/analytics/content/2337372172371/views?fromDate=2023-10-03",
nil).
Return(&http.Request{}, errors.New("error, unable to create the http request"))

fields.c = client

},
wantErr: true,
Err: errors.New("error, unable to create the http request"),
},

{
name: "when the content id is not provided",
args: args{
ctx: context.TODO(),
},
wantErr: true,
Err: model.ErrNoContentIDError,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

if testCase.on != nil {
testCase.on(&testCase.fields)
}

newService := NewAnalyticsService(testCase.fields.c)

gotResult, gotResponse, err := newService.Get(testCase.args.ctx, testCase.args.contentId, testCase.args.fromDate)

if testCase.wantErr {

if err != nil {
t.Logf("error returned: %v", err.Error())
}

assert.EqualError(t, err, testCase.Err.Error())
} else {

assert.NoError(t, err)
assert.NotEqual(t, gotResponse, nil)
assert.NotEqual(t, gotResult, nil)
}

})
}
}

func Test_internalAnalyticsServiceImpl_Distinct(t *testing.T) {

type fields struct {
c service.Client
}

type args struct {
ctx context.Context
contentId, fromDate string
}

testCases := []struct {
name string
fields fields
args args
on func(*fields)
wantErr bool
Err error
}{
{
name: "when the parameters are correct",
args: args{
ctx: context.TODO(),
contentId: "2337372172371",
fromDate: "2023-10-03",
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("NewRequest",
context.Background(),
http.MethodGet,
"wiki/rest/api/analytics/content/2337372172371/viewers?fromDate=2023-10-03",
nil).
Return(&http.Request{}, nil)

client.On("Call",
&http.Request{},
&model.ContentViewScheme{}).
Return(&model.ResponseScheme{}, nil)

fields.c = client
},
},

{
name: "when the http request cannot be created",
args: args{
ctx: context.TODO(),
contentId: "2337372172371",
fromDate: "2023-10-03",
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("NewRequest",
context.Background(),
http.MethodGet,
"wiki/rest/api/analytics/content/2337372172371/viewers?fromDate=2023-10-03",
nil).
Return(&http.Request{}, errors.New("error, unable to create the http request"))

fields.c = client

},
wantErr: true,
Err: errors.New("error, unable to create the http request"),
},

{
name: "when the content id is not provided",
args: args{
ctx: context.TODO(),
},
wantErr: true,
Err: model.ErrNoContentIDError,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

if testCase.on != nil {
testCase.on(&testCase.fields)
}

newService := NewAnalyticsService(testCase.fields.c)

gotResult, gotResponse, err := newService.Distinct(testCase.args.ctx, testCase.args.contentId, testCase.args.fromDate)

if testCase.wantErr {

if err != nil {
t.Logf("error returned: %v", err.Error())
}

assert.EqualError(t, err, testCase.Err.Error())
} else {

assert.NoError(t, err)
assert.NotEqual(t, gotResponse, nil)
assert.NotEqual(t, gotResult, nil)
}

})
}
}
6 changes: 6 additions & 0 deletions pkg/infra/models/confluence_analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package models

type ContentViewScheme struct {
ID int `json:"id,omitempty"`
Count int `json:"count,omitempty"`
}
23 changes: 23 additions & 0 deletions service/confluence/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package confluence

import (
"context"
model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
)

type AnalyticsConnector interface {

// Get gets the total number of views a piece of content has.
//
// GET /wiki/rest/api/analytics/content/{contentId}/views
//
// https://docs.go-atlassian.io/confluence-cloud/analytics#get-views
Get(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error)

// Distinct get the total number of distinct viewers a piece of content has.
//
// GET /wiki/rest/api/analytics/content/{contentId}/viewers
//
// https://docs.go-atlassian.io/confluence-cloud/analytics#get-viewers
Distinct(ctx context.Context, contentId, fromDate string) (*model.ContentViewScheme, *model.ResponseScheme, error)
}

0 comments on commit 36300c9

Please sign in to comment.