Repository: ctreminiom/go-atlassian
Pull request #187 (2023-04-13 20:08:38)

:sparkles: Created the Confluence Space V2 service.
1. Added the possibility to extract the Confluence space information using the V2 API
2. Documented the service methods on the documentation site.


Patch:
diff --git a/confluence/internal/space_v2_impl.go b/confluence/internal/space_v2_impl.go
new file mode 100644
index 0000000..407acc7
--- /dev/null
+++ b/confluence/internal/space_v2_impl.go
@@ -0,0 +1,143 @@
+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"
+	"strconv"
+	"strings"
+)
+
+type SpaceV2Service struct {
+	internalClient confluence.SpaceV2Connector
+}
+
+// Bulk returns all spaces.
+//
+// The results will be sorted by id ascending.
+//
+// The number of results is limited by the limit parameter and additional results (if available)
+//
+// will be available through the next URL present in the Link response header.
+//
+// GET /wiki/api/v2/spaces
+//
+// https://docs.go-atlassian.io/confluence-cloud/v2/space#get-spaces
+func (s *SpaceV2Service) Bulk(ctx context.Context, options *model.GetSpacesOptionSchemeV2, cursor string, limit int) (*model.SpaceChunkV2Scheme, *model.ResponseScheme, error) {
+	return s.internalClient.Bulk(ctx, options, cursor, limit)
+}
+
+// Get returns a specific space.
+//
+// GET /wiki/api/v2/spaces/{id}
+//
+// https://docs.go-atlassian.io/confluence-cloud/v2/space#get-space-by-id
+func (s *SpaceV2Service) Get(ctx context.Context, spaceID int, descriptionFormat string) (*model.SpaceSchemeV2, *model.ResponseScheme, error) {
+	return s.internalClient.Get(ctx, spaceID, descriptionFormat)
+}
+
+func NewSpaceV2Service(client service.Client) *SpaceV2Service {
+
+	return &SpaceV2Service{
+		internalClient: &internalSpaceV2Impl{c: client},
+	}
+}
+
+type internalSpaceV2Impl struct {
+	c service.Client
+}
+
+func (i *internalSpaceV2Impl) Bulk(ctx context.Context, options *model.GetSpacesOptionSchemeV2, cursor string, limit int) (*model.SpaceChunkV2Scheme, *model.ResponseScheme, error) {
+
+	query := url.Values{}
+	query.Add("limit", strconv.Itoa(limit))
+
+	if cursor != "" {
+		query.Add("cursor", cursor)
+	}
+
+	if options != nil {
+
+		if len(options.IDs) != 0 {
+			query.Add("ids", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(options.IDs)), ","), "[]"))
+		}
+
+		if len(options.Keys) != 0 {
+			query.Add("keys", strings.Join(options.Keys, ","))
+		}
+
+		if options.Type != "" {
+			query.Add("type", options.Type)
+		}
+
+		if options.Status != "" {
+			query.Add("status", options.Status)
+		}
+
+		if len(options.Labels) != 0 {
+			query.Add("labels", strings.Join(options.Labels, ","))
+		}
+
+		if options.Sort != "" {
+			query.Add("sort", options.Sort)
+		}
+
+		if options.DescriptionFormat != "" {
+			query.Add("description-format", options.DescriptionFormat)
+		}
+
+		if options.SerializeIDs {
+			query.Add("serialize-ids-as-strings", "true")
+		}
+
+	}
+
+	endpoint := fmt.Sprintf("wiki/api/v2/spaces?%v", query.Encode())
+
+	request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	chunk := new(model.SpaceChunkV2Scheme)
+	response, err := i.c.Call(request, chunk)
+	if err != nil {
+		return nil, response, err
+	}
+
+	return chunk, response, nil
+}
+
+func (i *internalSpaceV2Impl) Get(ctx context.Context, spaceID int, descriptionFormat string) (*model.SpaceSchemeV2, *model.ResponseScheme, error) {
+
+	if spaceID == 0 {
+		return nil, nil, model.ErrNoSpaceIDError
+	}
+
+	var endpoint strings.Builder
+	endpoint.WriteString(fmt.Sprintf("wiki/api/v2/spaces/%v", spaceID))
+
+	if descriptionFormat != "" {
+		query := url.Values{}
+		query.Add("description-format", descriptionFormat)
+
+		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
+	}
+
+	space := new(model.SpaceSchemeV2)
+	response, err := i.c.Call(request, space)
+	if err != nil {
+		return nil, response, err
+	}
+
+	return space, response, nil
+}
diff --git a/confluence/v2/api_client_impl.go b/confluence/v2/api_client_impl.go
index 2133671..5ee4455 100644
--- a/confluence/v2/api_client_impl.go
+++ b/confluence/v2/api_client_impl.go
@@ -37,15 +37,17 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
 
 	client.Auth = internal.NewAuthenticationService(client)
 	client.Page = internal.NewPageService(client)
+	client.Space = internal.NewSpaceV2Service(client)
 
 	return client, nil
 }
 
 type Client struct {
-	HTTP common.HttpClient
-	Site *url.URL
-	Auth common.Authentication
-	Page *internal.PageService
+	HTTP  common.HttpClient
+	Site  *url.URL
+	Auth  common.Authentication
+	Page  *internal.PageService
+	Space *internal.SpaceV2Service
 }
 
 func (c *Client) NewFormRequest(ctx context.Context, method, apiEndpoint, contentType string, payload io.Reader) (*http.Request, error) {
diff --git a/jira/internal/link_impl.go b/jira/internal/link_impl.go
index 5c1b7e6..0b753d9 100644
--- a/jira/internal/link_impl.go
+++ b/jira/internal/link_impl.go
@@ -5,7 +5,7 @@ import (
 	"github.com/ctreminiom/go-atlassian/service"
 )
 
-func NewLinkService(client service.Client, version string, type_ *LinkTypeService, remote *RemoteLinkService) (*LinkADFService, *LinkRichTextService, error) {
+func NewLinkService(client service.Client, version string, type_ *LinkTypeService) (*LinkADFService, *LinkRichTextService, error) {
 
 	if version == "" {
 		return nil, nil, model.ErrNoVersionProvided
@@ -16,8 +16,7 @@ func NewLinkService(client service.Client, version string, type_ *LinkTypeServic
 			c:       client,
 			version: version,
 		},
-		Type:   type_,
-		Remote: remote,
+		Type: type_,
 	}
 
 	richTextService := &LinkRichTextService{
@@ -25,8 +24,7 @@ func NewLinkService(client service.Client, version string, type_ *LinkTypeServic
 			c:       client,
 			version: version,
 		},
-		Type:   type_,
-		Remote: remote,
+		Type: type_,
 	}
 
 	return adfService, richTextService, nil
diff --git a/jira/internal/link_impl_adf.go b/jira/internal/link_impl_adf.go
index 4016510..aa836f2 100644
--- a/jira/internal/link_impl_adf.go
+++ b/jira/internal/link_impl_adf.go
@@ -12,7 +12,6 @@ import (
 type LinkADFService struct {
 	internalClient jira.LinkAdfIssueConnector
 	Type           *LinkTypeService
-	Remote         *RemoteLinkService
 }
 
 type internalLinkADFServiceImpl struct {
diff --git a/jira/internal/link_impl_rich_text.go b/jira/internal/link_impl_rich_text.go
index 649717e..2c400c2 100644
--- a/jira/internal/link_impl_rich_text.go
+++ b/jira/internal/link_impl_rich_text.go
@@ -12,7 +12,6 @@ import (
 type LinkRichTextService struct {
 	internalClient jira.LinkRichTextConnector
 	Type           *LinkTypeService
-	Remote         *RemoteLinkService
 }
 
 type internalLinkRichTextServiceImpl struct {
diff --git a/jira/internal/remote_link_impl.go b/jira/internal/remote_link_impl.go
deleted file mode 100644
index 9e6b42d..0000000
--- a/jira/internal/remote_link_impl.go
+++ /dev/null
@@ -1,253 +0,0 @@
-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/jira"
-	"net/http"
-	"net/url"
-	"strings"
-)
-
-func NewRemoteLinkService(client service.Client, version string) (*RemoteLinkService, error) {
-
-	if version == "" {
-		return nil, model.ErrNoVersionProvided
-	}
-
-	return &RemoteLinkService{
-		internalClient: &internalRemoteLinkImpl{c: client, version: version},
-	}, nil
-}
-
-type RemoteLinkService struct {
-	internalClient jira.RemoteLinkConnector
-}
-
-// Gets returns the remote issue links for an issue.
-//
-// When a remote issue link global ID is provided the record with that global ID is returned,
-//
-// otherwise all remote issue links are returned.
-//
-// # Where a global ID includes reserved URL characters these must be escaped in the request
-//
-// GET /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#get-remote-issue-links
-func (r *RemoteLinkService) Gets(ctx context.Context, issueKeyOrId, globalId string) ([]*model.RemoteLinkScheme, *model.ResponseScheme, error) {
-	return r.internalClient.Gets(ctx, issueKeyOrId, globalId)
-}
-
-// Get returns a remote issue link for an issue.
-//
-// GET /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#get-remote-issue-link
-func (r *RemoteLinkService) Get(ctx context.Context, issueKeyOrId, linkId string) (*model.RemoteLinkScheme, *model.ResponseScheme, error) {
-	return r.internalClient.Get(ctx, issueKeyOrId, linkId)
-}
-
-// Create creates or updates a remote issue link for an issue.
-//
-// If a globalId is provided and a remote issue link with that global ID is found it is updated.
-//
-// Any fields without values in the request are set to null. Otherwise, the remote issue link is created.
-//
-// POST /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#create-remote-issue-link
-func (r *RemoteLinkService) Create(ctx context.Context, issueKeyOrId string, payload *model.RemoteLinkScheme) (*model.RemoteLinkIdentify, *model.ResponseScheme, error) {
-	return r.internalClient.Create(ctx, issueKeyOrId, payload)
-}
-
-// Update updates a remote issue link for an issue.
-//
-// Note: Fields without values in the request are set to null.
-//
-// PUT /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#update-remote-issue-link
-func (r *RemoteLinkService) Update(ctx context.Context, issueKeyOrId, linkId string, payload *model.RemoteLinkScheme) (*model.ResponseScheme, error) {
-	return r.internalClient.Update(ctx, issueKeyOrId, linkId, payload)
-}
-
-// DeleteById deletes a remote issue link from an issue.
-//
-// DELETE /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#delete-remote-issue-link-by-id
-func (r *RemoteLinkService) DeleteById(ctx context.Context, issueKeyOrId, linkId string) (*model.ResponseScheme, error) {
-	return r.internalClient.DeleteById(ctx, issueKeyOrId, linkId)
-}
-
-// DeleteByGlobalId deletes the remote issue link from the issue using the link's global ID.
-//
-// Where the global ID includes reserved URL characters these must be escaped in the request.
-//
-// For example, pass system=http://www.mycompany.com/support&id=1 as system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1.
-//
-// DELETE /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-//
-// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#delete-remote-issue-link-by-global-id
-func (r *RemoteLinkService) DeleteByGlobalId(ctx context.Context, issueKeyOrId, globalId string) (*model.ResponseScheme, error) {
-	return r.internalClient.DeleteByGlobalId(ctx, issueKeyOrId, globalId)
-}
-
-type internalRemoteLinkImpl struct {
-	c       service.Client
-	version string
-}
-
-func (i *internalRemoteLinkImpl) Gets(ctx context.Context, issueKeyOrId, globalId string) ([]*model.RemoteLinkScheme, *model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	var endpoint strings.Builder
-	endpoint.WriteString(fmt.Sprintf("rest/api/%v/issue/%v/remotelink", i.version, issueKeyOrId))
-
-	if globalId != "" {
-
-		params := url.Values{}
-		params.Add("globalId", globalId)
-		endpoint.WriteString(fmt.Sprintf("?%v", params.Encode()))
-	}
-
-	request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint.String(), nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var remoteLinks []*model.RemoteLinkScheme
-	response, err := i.c.Call(request, &remoteLinks)
-	if err != nil {
-		return nil, response, err
-	}
-
-	return remoteLinks, response, nil
-}
-
-func (i *internalRemoteLinkImpl) Get(ctx context.Context, issueKeyOrId, linkId string) (*model.RemoteLinkScheme, *model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	if linkId == "" {
-		return nil, nil, model.ErrNoRemoteLinkIDError
-	}
-
-	endpoint := fmt.Sprintf("rest/api/%v/issue/%v/remotelink/%v", i.version, issueKeyOrId, linkId)
-
-	request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	remoteLink := new(model.RemoteLinkScheme)
-	response, err := i.c.Call(request, remoteLink)
-	if err != nil {
-		return nil, response, err
-	}
-
-	return remoteLink, response, nil
-}
-
-func (i *internalRemoteLinkImpl) Create(ctx context.Context, issueKeyOrId string, payload *model.RemoteLinkScheme) (*model.RemoteLinkIdentify, *model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	reader, err := i.c.TransformStructToReader(payload)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	endpoint := fmt.Sprintf("rest/api/%v/issue/%v/remotelink", i.version, issueKeyOrId)
-
-	request, err := i.c.NewRequest(ctx, http.MethodPost, endpoint, reader)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	identify := new(model.RemoteLinkIdentify)
-	response, err := i.c.Call(request, identify)
-	if err != nil {
-		return nil, response, err
-	}
-
-	return identify, response, nil
-}
-
-func (i *internalRemoteLinkImpl) Update(ctx context.Context, issueKeyOrId, linkId string, payload *model.RemoteLinkScheme) (*model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	if linkId == "" {
-		return nil, model.ErrNoRemoteLinkIDError
-	}
-
-	reader, err := i.c.TransformStructToReader(payload)
-	if err != nil {
-		return nil, err
-	}
-
-	endpoint := fmt.Sprintf("rest/api/%v/issue/%v/remotelink/%v", i.version, issueKeyOrId, linkId)
-
-	request, err := i.c.NewRequest(ctx, http.MethodPut, endpoint, reader)
-	if err != nil {
-		return nil, err
-	}
-
-	return i.c.Call(request, nil)
-}
-
-func (i *internalRemoteLinkImpl) DeleteById(ctx context.Context, issueKeyOrId, linkId string) (*model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	if linkId == "" {
-		return nil, model.ErrNoRemoteLinkIDError
-	}
-
-	endpoint := fmt.Sprintf("rest/api/%v/issue/%v/remotelink/%v", i.version, issueKeyOrId, linkId)
-
-	request, err := i.c.NewRequest(ctx, http.MethodDelete, endpoint, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return i.c.Call(request, nil)
-}
-
-func (i *internalRemoteLinkImpl) DeleteByGlobalId(ctx context.Context, issueKeyOrId, globalId string) (*model.ResponseScheme, error) {
-
-	if issueKeyOrId == "" {
-		return nil, model.ErrNoIssueKeyOrIDError
-	}
-
-	if globalId == "" {
-		return nil, model.ErrNoRemoteLinkGlobalIDError
-	}
-
-	params := url.Values{}
-	params.Add("globalId", globalId)
-
-	endpoint := fmt.Sprintf("rest/api/%v/issue/%v/remotelink?%v", i.version, issueKeyOrId, params.Encode())
-
-	request, err := i.c.NewRequest(ctx, http.MethodDelete, endpoint, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return i.c.Call(request, nil)
-}
diff --git a/jira/v2/api_client_impl.go b/jira/v2/api_client_impl.go
index 45b0893..cb5ee9b 100644
--- a/jira/v2/api_client_impl.go
+++ b/jira/v2/api_client_impl.go
@@ -120,12 +120,7 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
 		return nil, err
 	}
 
-	remoteLink, err := internal.NewRemoteLinkService(client, "2")
-	if err != nil {
-		return nil, err
-	}
-
-	_, link, err := internal.NewLinkService(client, "2", linkType, remoteLink)
+	_, link, err := internal.NewLinkService(client, "2", linkType)
 	if err != nil {
 		return nil, err
 	}
diff --git a/jira/v3/api_client_impl.go b/jira/v3/api_client_impl.go
index cf8d237..4bfa6be 100644
--- a/jira/v3/api_client_impl.go
+++ b/jira/v3/api_client_impl.go
@@ -120,12 +120,7 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
 		return nil, err
 	}
 
-	remoteLink, err := internal.NewRemoteLinkService(client, "3")
-	if err != nil {
-		return nil, err
-	}
-
-	link, _, err := internal.NewLinkService(client, "3", linkType, remoteLink)
+	link, _, err := internal.NewLinkService(client, "3", linkType)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/infra/models/confluence_space_v2.go b/pkg/infra/models/confluence_space_v2.go
new file mode 100644
index 0000000..3974d4a
--- /dev/null
+++ b/pkg/infra/models/confluence_space_v2.go
@@ -0,0 +1,38 @@
+package models
+
+type SpaceChunkV2Scheme struct {
+	Results []*SpaceSchemeV2 `json:"results,omitempty"`
+	Links   struct {
+		Next string `json:"next"`
+	} `json:"_links"`
+}
+
+type SpacePageLinkSchemeV2 struct {
+	Next string `json:"next,omitempty"`
+}
+
+type GetSpacesOptionSchemeV2 struct {
+	IDs               []int
+	Keys              []string
+	Type              string
+	Status            string
+	Labels            []string
+	Sort              string
+	DescriptionFormat string
+	SerializeIDs      bool
+}
+
+type SpaceSchemeV2 struct {
+	ID          int                       `json:"id,omitempty"`
+	Key         string                    `json:"key,omitempty"`
+	Name        string                    `json:"name,omitempty"`
+	Type        string                    `json:"type,omitempty"`
+	Status      string                    `json:"status,omitempty"`
+	HomepageId  int                       `json:"homepageId,omitempty"`
+	Description *SpaceDescriptionSchemeV2 `json:"description,omitempty"`
+}
+
+type SpaceDescriptionSchemeV2 struct {
+	Plain *PageBodyRepresentationScheme `json:"plain,omitempty"`
+	View  *PageBodyRepresentationScheme `json:"view,omitempty"`
+}
diff --git a/pkg/infra/models/errors.go b/pkg/infra/models/errors.go
index 0c1587a..e32418c 100644
--- a/pkg/infra/models/errors.go
+++ b/pkg/infra/models/errors.go
@@ -60,8 +60,6 @@ var (
 	ErrNoGroupIDError                      = errors.New("jira: no group name set")
 	ErrNoGroupsNameError                   = errors.New("jira: no groups names set")
 	ErrNoIssueKeyOrIDError                 = errors.New("jira: no issue key/id set")
-	ErrNoRemoteLinkIDError                 = errors.New("jira: no remote link id set")
-	ErrNoRemoteLinkGlobalIDError           = errors.New("jira: no global remote link id set")
 	ErrNoIssueSchemeError                  = errors.New("jira: no jira.IssueScheme set")
 	ErrNoTransitionIDError                 = errors.New("jira: no transition id set")
 	ErrNoAttachmentIDError                 = errors.New("jira: no attachment id set")
diff --git a/pkg/infra/models/jira_remote_links.go b/pkg/infra/models/jira_remote_links.go
deleted file mode 100644
index b6509c2..0000000
--- a/pkg/infra/models/jira_remote_links.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package models
-
-type RemoteLinkIdentify struct {
-	ID   int    `json:"id,omitempty"`
-	Self string `json:"self,omitempty"`
-}
-
-type RemoteLinkScheme struct {
-	Application  *RemoteLinkApplicationScheme `json:"application,omitempty"`
-	GlobalID     string                       `json:"globalId,omitempty"`
-	ID           int                          `json:"id,omitempty"`
-	Object       *RemoteLinkObjectScheme      `json:"object,omitempty"`
-	Relationship string                       `json:"relationship,omitempty"`
-	Self         string                       `json:"self,omitempty"`
-}
-
-type RemoteLinkObjectScheme struct {
-	Icon    *RemoteLinkObjectLinkScheme   `json:"icon,omitempty"`
-	Status  *RemoteLinkObjectStatusScheme `json:"status,omitempty"`
-	Summary string                        `json:"summary,omitempty"`
-	Title   string                        `json:"title,omitempty"`
-	URL     string                        `json:"url,omitempty"`
-}
-
-type RemoteLinkObjectStatusScheme struct {
-	Icon     *RemoteLinkObjectLinkScheme `json:"icon,omitempty"`
-	Resolved bool                        `json:"resolved,omitempty"`
-}
-
-type RemoteLinkObjectLinkScheme struct {
-	Link     string `json:"link,omitempty"`
-	Title    string `json:"title,omitempty"`
-	URL16X16 string `json:"url16x16,omitempty"`
-}
-
-type RemoteLinkApplicationScheme struct {
-	Name string `json:"name,omitempty"`
-	Type string `json:"type,omitempty"`
-}
diff --git a/service/confluence/space.go b/service/confluence/space.go
index 9cfe7bf..f624363 100644
--- a/service/confluence/space.go
+++ b/service/confluence/space.go
@@ -5,6 +5,29 @@ import (
 	model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
 )
 
+type SpaceV2Connector interface {
+
+	// Bulk returns all spaces.
+	//
+	// The results will be sorted by id ascending.
+	//
+	// The number of results is limited by the limit parameter and additional results (if available)
+	//
+	// will be available through the next URL present in the Link response header.
+	//
+	// GET /wiki/api/v2/spaces
+	//
+	// https://docs.go-atlassian.io/confluence-cloud/v2/space#get-spaces
+	Bulk(ctx context.Context, options *model.GetSpacesOptionSchemeV2, cursor string, limit int) (*model.SpaceChunkV2Scheme, *model.ResponseScheme, error)
+
+	// Get returns a specific space.
+	//
+	// GET /wiki/api/v2/spaces/{id}
+	//
+	// https://docs.go-atlassian.io/confluence-cloud/v2/space#get-space-by-id
+	Get(ctx context.Context, spaceID int, descriptionFormat string) (*model.SpaceSchemeV2, *model.ResponseScheme, error)
+}
+
 type SpaceConnector interface {
 
 	// Gets returns all spaces.
diff --git a/service/jira/remoteLink.go b/service/jira/remoteLink.go
deleted file mode 100644
index c5ded5a..0000000
--- a/service/jira/remoteLink.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package jira
-
-import (
-	"context"
-	"github.com/ctreminiom/go-atlassian/pkg/infra/models"
-)
-
-type RemoteLinkConnector interface {
-
-	// Gets returns the remote issue links for an issue.
-	//
-	// When a remote issue link global ID is provided the record with that global ID is returned,
-	//
-	// otherwise all remote issue links are returned.
-	//
-	// Where a global ID includes reserved URL characters these must be escaped in the request
-	//
-	// GET /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#get-remote-issue-links
-	Gets(ctx context.Context, issueKeyOrId, globalId string) ([]*models.RemoteLinkScheme, *models.ResponseScheme, error)
-
-	// Get returns a remote issue link for an issue.
-	//
-	// GET /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#get-remote-issue-link
-	Get(ctx context.Context, issueKeyOrId, linkId string) (*models.RemoteLinkScheme, *models.ResponseScheme, error)
-
-	// Create creates or updates a remote issue link for an issue.
-	//
-	// If a globalId is provided and a remote issue link with that global ID is found it is updated.
-	//
-	// Any fields without values in the request are set to null. Otherwise, the remote issue link is created.
-	//
-	// POST /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#create-remote-issue-link
-	Create(ctx context.Context, issueKeyOrId string, payload *models.RemoteLinkScheme) (*models.RemoteLinkIdentify, *models.ResponseScheme, error)
-
-	// Update updates a remote issue link for an issue.
-	//
-	// Note: Fields without values in the request are set to null.
-	//
-	// PUT /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#update-remote-issue-link
-	Update(ctx context.Context, issueKeyOrId, linkId string, payload *models.RemoteLinkScheme) (*models.ResponseScheme, error)
-
-	// DeleteById deletes a remote issue link from an issue.
-	//
-	// DELETE /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink/{linkId}
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#delete-remote-issue-link-by-id
-	DeleteById(ctx context.Context, issueKeyOrId, linkId string) (*models.ResponseScheme, error)
-
-	// DeleteByGlobalId deletes the remote issue link from the issue using the link's global ID.
-	//
-	// Where the global ID includes reserved URL characters these must be escaped in the request.
-	//
-	// For example, pass system=http://www.mycompany.com/support&id=1 as system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1.
-	//
-	// DELETE /rest/api/{2-3}/issue/{issueIdOrKey}/remotelink
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/issues/link/remote#delete-remote-issue-link-by-global-id
-	DeleteByGlobalId(ctx context.Context, issueKeyOrId, globalId string) (*models.ResponseScheme, error)
-}


Test patch:
diff --git a/confluence/internal/space_v2_impl_test.go b/confluence/internal/space_v2_impl_test.go
new file mode 100644
index 0000000..f9b086c
--- /dev/null
+++ b/confluence/internal/space_v2_impl_test.go
@@ -0,0 +1,244 @@
+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_internalSpaceV2Impl_Bulk(t *testing.T) {
+
+	optionsMocked := &model.GetSpacesOptionSchemeV2{
+		IDs:               []int{10001, 10002},
+		Keys:              []string{"DUMMY"},
+		Type:              "global",
+		Status:            "current",
+		Labels:            []string{"test-label"},
+		Sort:              "-name",
+		DescriptionFormat: "view",
+		SerializeIDs:      true,
+	}
+
+	type fields struct {
+		c service.Client
+	}
+
+	type args struct {
+		ctx     context.Context
+		options *model.GetSpacesOptionSchemeV2
+		cursor  string
+		limit   int
+	}
+
+	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(),
+				options: optionsMocked,
+				cursor:  "cursor_sample_uuid",
+				limit:   50,
+			},
+			on: func(fields *fields) {
+
+				client := mocks.NewClient(t)
+
+				client.On("NewRequest",
+					context.Background(),
+					http.MethodGet,
+					"wiki/api/v2/spaces?cursor=cursor_sample_uuid&description-format=view&ids=10001%2C10002&keys=DUMMY&labels=test-label&limit=50&serialize-ids-as-strings=true&sort=-name&status=current&type=global",
+					nil).
+					Return(&http.Request{}, nil)
+
+				client.On("Call",
+					&http.Request{},
+					&model.SpaceChunkV2Scheme{}).
+					Return(&model.ResponseScheme{}, nil)
+
+				fields.c = client
+
+			},
+		},
+
+		{
+			name: "when the http request cannot be created",
+			args: args{
+				ctx:     context.TODO(),
+				options: optionsMocked,
+				cursor:  "cursor_sample_uuid",
+				limit:   50,
+			},
+			on: func(fields *fields) {
+
+				client := mocks.NewClient(t)
+
+				client.On("NewRequest",
+					context.Background(),
+					http.MethodGet,
+					"wiki/api/v2/spaces?cursor=cursor_sample_uuid&description-format=view&ids=10001%2C10002&keys=DUMMY&labels=test-label&limit=50&serialize-ids-as-strings=true&sort=-name&status=current&type=global",
+					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"),
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+
+			if testCase.on != nil {
+				testCase.on(&testCase.fields)
+			}
+
+			newService := NewSpaceV2Service(testCase.fields.c)
+
+			gotResult, gotResponse, err := newService.Bulk(testCase.args.ctx, testCase.args.options, testCase.args.cursor,
+				testCase.args.limit)
+
+			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_internalSpaceV2Impl_Get(t *testing.T) {
+
+	type fields struct {
+		c service.Client
+	}
+
+	type args struct {
+		ctx               context.Context
+		spaceID           int
+		descriptionFormat 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(),
+				spaceID:           10001,
+				descriptionFormat: "view",
+			},
+			on: func(fields *fields) {
+
+				client := mocks.NewClient(t)
+
+				client.On("NewRequest",
+					context.Background(),
+					http.MethodGet,
+					"wiki/api/v2/spaces/10001?description-format=view",
+					nil).
+					Return(&http.Request{}, nil)
+
+				client.On("Call",
+					&http.Request{},
+					&model.SpaceSchemeV2{}).
+					Return(&model.ResponseScheme{}, nil)
+
+				fields.c = client
+
+			},
+		},
+
+		{
+			name: "when the space id is not provided",
+			args: args{
+				ctx: context.TODO(),
+			},
+			wantErr: true,
+			Err:     model.ErrNoSpaceIDError,
+		},
+
+		{
+			name: "when the http request cannot be created",
+			args: args{
+				ctx:               context.TODO(),
+				spaceID:           10001,
+				descriptionFormat: "view",
+			},
+			on: func(fields *fields) {
+
+				client := mocks.NewClient(t)
+
+				client.On("NewRequest",
+					context.Background(),
+					http.MethodGet,
+					"wiki/api/v2/spaces/10001?description-format=view",
+					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"),
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+
+			if testCase.on != nil {
+				testCase.on(&testCase.fields)
+			}
+
+			newService := NewSpaceV2Service(testCase.fields.c)
+
+			gotResult, gotResponse, err := newService.Get(testCase.args.ctx, testCase.args.spaceID, testCase.args.descriptionFormat)
+
+			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)
+			}
+
+		})
+	}
+}
diff --git a/jira/internal/link_impl_adf_test.go b/jira/internal/link_impl_adf_test.go
index 84d00e1..7e22d84 100644
--- a/jira/internal/link_impl_adf_test.go
+++ b/jira/internal/link_impl_adf_test.go
@@ -122,7 +122,7 @@ func Test_internalLinkADFServiceImpl_Get(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResult, gotResponse, err := linkService.Get(testCase.args.ctx, testCase.args.linkId)
@@ -267,7 +267,7 @@ func Test_internalLinkADFServiceImpl_Gets(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResult, gotResponse, err := linkService.Gets(testCase.args.ctx, testCase.args.issueKeyOrId)
@@ -401,7 +401,7 @@ func Test_internalLinkADFServiceImpl_Delete(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResponse, err := linkService.Delete(testCase.args.ctx, testCase.args.linkId)
@@ -583,7 +583,7 @@ func Test_internalLinkADFServiceImpl_Create(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			linkService, _, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResponse, err := linkService.Create(testCase.args.ctx, testCase.args.payload)
diff --git a/jira/internal/link_impl_rich_text_test.go b/jira/internal/link_impl_rich_text_test.go
index 9ed602a..0b73bd2 100644
--- a/jira/internal/link_impl_rich_text_test.go
+++ b/jira/internal/link_impl_rich_text_test.go
@@ -122,7 +122,7 @@ func Test_internalLinkRichTextServiceImpl_Get(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResult, gotResponse, err := linkService.Get(testCase.args.ctx, testCase.args.linkId)
@@ -267,7 +267,7 @@ func Test_internalLinkRichTextServiceImpl_Gets(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResult, gotResponse, err := linkService.Gets(testCase.args.ctx, testCase.args.issueKeyOrId)
@@ -401,7 +401,7 @@ func Test_internalLinkRichTextServiceImpl_Delete(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResponse, err := linkService.Delete(testCase.args.ctx, testCase.args.linkId)
@@ -561,7 +561,7 @@ func Test_internalLinkRichTextServiceImpl_Create(t *testing.T) {
 				testCase.on(&testCase.fields)
 			}
 
-			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil, nil)
+			_, linkService, err := NewLinkService(testCase.fields.c, testCase.fields.version, nil)
 			assert.NoError(t, err)
 
 			gotResponse, err := linkService.Create(testCase.args.ctx, testCase.args.payload)
diff --git a/jira/internal/link_impl_test.go b/jira/internal/link_impl_test.go
index 57fb254..df382cf 100644
--- a/jira/internal/link_impl_test.go
+++ b/jira/internal/link_impl_test.go
@@ -41,7 +41,7 @@ func Test_NewLinkService(t *testing.T) {
 	}
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			got, got1, err := NewLinkService(testCase.args.client, testCase.args.version, nil, nil)
+			got, got1, err := NewLinkService(testCase.args.client, testCase.args.version, nil)
 
 			if testCase.wantErr {
 
diff --git a/jira/internal/remote_link_impl_test.go b/jira/internal/remote_link_impl_test.go
deleted file mode 100644
index 0afe461..0000000
--- a/jira/internal/remote_link_impl_test.go
+++ /dev/null
@@ -1,1191 +0,0 @@
-package internal
-
-import (
-	"bytes"
-	"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"
-	"github.com/stretchr/testify/mock"
-	"net/http"
-	"testing"
-)
-
-func Test_internalRemoteLinkImpl_Gets(t *testing.T) {
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                    context.Context
-		issueKeyOrId, globalId string
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					mock.Anything).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/3/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					mock.Anything).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					mock.Anything).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResult, gotResponse, err := applicationService.Gets(testCase.args.ctx, testCase.args.issueKeyOrId, testCase.args.globalId)
-
-			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_internalRemoteLinkImpl_Get(t *testing.T) {
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                  context.Context
-		issueKeyOrId, linkId string
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkScheme{}).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/3/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkScheme{}).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the remote link is not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-3",
-			},
-			wantErr: true,
-			Err:     model.ErrNoRemoteLinkIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkScheme{}).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResult, gotResponse, err := applicationService.Get(testCase.args.ctx, testCase.args.issueKeyOrId, testCase.args.linkId)
-
-			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_internalRemoteLinkImpl_Update(t *testing.T) {
-
-	payloadMocked := &model.RemoteLinkScheme{
-		Application: &model.RemoteLinkApplicationScheme{
-			Name: "My Acme Tracker",
-			Type: "com.acme.tracker",
-		},
-		GlobalID: "system=http://www.mycompany.com/support&id=1",
-		Object: &model.RemoteLinkObjectScheme{
-			Icon: &model.RemoteLinkObjectLinkScheme{
-				Title:    "Support Ticket",
-				URL16X16: "http://www.mycompany.com/support/ticket.png",
-			},
-			Status: &model.RemoteLinkObjectStatusScheme{
-				Icon: &model.RemoteLinkObjectLinkScheme{
-					Link:     "http://www.mycompany.com/support?id=1&details=closed",
-					Title:    "Case Closed",
-					URL16X16: "http://www.mycompany.com/support/resolved.png",
-				},
-				Resolved: true,
-			},
-			Summary: "Customer support issue",
-			Title:   "TSTSUP-111",
-			URL:     "http://www.mycompany.com/support?id=1",
-		},
-		Relationship: "causes",
-	}
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                  context.Context
-		issueKeyOrId, linkId string
-		payload              *model.RemoteLinkScheme
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPut,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-				payload:      payloadMocked,
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPut,
-					"rest/api/3/issue/KP-23/remotelink/10001",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-				payload:      payloadMocked,
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the remote link is not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-3",
-			},
-			wantErr: true,
-			Err:     model.ErrNoRemoteLinkIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPut,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-				payload:      payloadMocked,
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPut,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-				payload:      payloadMocked,
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResponse, err := applicationService.Update(testCase.args.ctx, testCase.args.issueKeyOrId, testCase.args.linkId,
-				testCase.args.payload)
-
-			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)
-			}
-		})
-	}
-}
-
-func Test_internalRemoteLinkImpl_Create(t *testing.T) {
-
-	payloadMocked := &model.RemoteLinkScheme{
-		Application: &model.RemoteLinkApplicationScheme{
-			Name: "My Acme Tracker",
-			Type: "com.acme.tracker",
-		},
-		GlobalID: "system=http://www.mycompany.com/support&id=1",
-		Object: &model.RemoteLinkObjectScheme{
-			Icon: &model.RemoteLinkObjectLinkScheme{
-				Title:    "Support Ticket",
-				URL16X16: "http://www.mycompany.com/support/ticket.png",
-			},
-			Status: &model.RemoteLinkObjectStatusScheme{
-				Icon: &model.RemoteLinkObjectLinkScheme{
-					Link:     "http://www.mycompany.com/support?id=1&details=closed",
-					Title:    "Case Closed",
-					URL16X16: "http://www.mycompany.com/support/resolved.png",
-				},
-				Resolved: true,
-			},
-			Summary: "Customer support issue",
-			Title:   "TSTSUP-111",
-			URL:     "http://www.mycompany.com/support?id=1",
-		},
-		Relationship: "causes",
-	}
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx          context.Context
-		issueKeyOrId string
-		payload      *model.RemoteLinkScheme
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPost,
-					"rest/api/2/issue/KP-23/remotelink",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkIdentify{}).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				payload:      payloadMocked,
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPost,
-					"rest/api/3/issue/KP-23/remotelink",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkIdentify{}).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				payload:      payloadMocked,
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPost,
-					"rest/api/2/issue/KP-23/remotelink",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				payload:      payloadMocked,
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("TransformStructToReader",
-					payloadMocked).
-					Return(bytes.NewReader([]byte{}), nil)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodPost,
-					"rest/api/2/issue/KP-23/remotelink",
-					bytes.NewReader([]byte{})).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					&model.RemoteLinkIdentify{}).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				payload:      payloadMocked,
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResult, gotResponse, err := applicationService.Create(testCase.args.ctx, testCase.args.issueKeyOrId,
-				testCase.args.payload)
-
-			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_internalRemoteLinkImpl_DeleteById(t *testing.T) {
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                  context.Context
-		issueKeyOrId, linkId string
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/3/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the remote link is not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-3",
-			},
-			wantErr: true,
-			Err:     model.ErrNoRemoteLinkIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink/10001",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				linkId:       "10001",
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResponse, err := applicationService.DeleteById(testCase.args.ctx, testCase.args.issueKeyOrId, testCase.args.linkId)
-
-			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)
-			}
-		})
-	}
-}
-
-func Test_internalRemoteLinkImpl_DeleteByGlobalId(t *testing.T) {
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                    context.Context
-		issueKeyOrId, globalId string
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		on      func(*fields)
-		args    args
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/3/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: false,
-		},
-
-		{
-			name:   "when the issue key or not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx: context.Background(),
-			},
-			wantErr: true,
-			Err:     model.ErrNoIssueKeyOrIDError,
-		},
-
-		{
-			name:   "when the global link is not provided",
-			fields: fields{version: "2"},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-3",
-			},
-			wantErr: true,
-			Err:     model.ErrNoRemoteLinkGlobalIDError,
-		},
-
-		{
-			name:   "when the request cannot be created",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, errors.New("unable to create the http request"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: true,
-			Err:     errors.New("unable to create the http request"),
-		},
-
-		{
-			name:   "when the http call cannot be executed",
-			fields: fields{version: "2"},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodDelete,
-					"rest/api/2/issue/KP-23/remotelink?globalId=system%3Dhttp%3A%2F%2Fwww.mycompany.com%2Fsupport%26id%3D1",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					nil).
-					Return(&model.ResponseScheme{}, errors.New("error, unable to execute the http call"))
-
-				fields.c = client
-			},
-			args: args{
-				ctx:          context.Background(),
-				issueKeyOrId: "KP-23",
-				globalId:     "system=http://www.mycompany.com/support&id=1",
-			},
-			wantErr: true,
-			Err:     errors.New("error, unable to execute the http call"),
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-
-			if testCase.on != nil {
-				testCase.on(&testCase.fields)
-			}
-
-			applicationService, err := NewRemoteLinkService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResponse, err := applicationService.DeleteByGlobalId(testCase.args.ctx, testCase.args.issueKeyOrId, testCase.args.globalId)
-
-			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)
-			}
-		})
-	}
-}