Repository: ctreminiom/go-atlassian
Pull request #271 (2024-06-26 08:11:19)

Change payload for service desk request attachemnt create to provide …
…additional comment

This will be a breaking change.


Patch:
diff --git a/jira/sm/internal/attachment_impl.go b/jira/sm/internal/attachment_impl.go
index 7844b4e..1357d7e 100644
--- a/jira/sm/internal/attachment_impl.go
+++ b/jira/sm/internal/attachment_impl.go
@@ -3,12 +3,13 @@ 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/sm"
 	"net/http"
 	"net/url"
 	"strconv"
+
+	model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
+	"github.com/ctreminiom/go-atlassian/service"
+	"github.com/ctreminiom/go-atlassian/service/sm"
 )
 
 func NewAttachmentService(client service.Connector, version string) *AttachmentService {
@@ -38,8 +39,8 @@ func (s *AttachmentService) Gets(ctx context.Context, issueKeyOrID string, start
 // POST /rest/servicedeskapi/request/{issueIdOrKey}/attachment
 //
 // https://docs.go-atlassian.io/jira-service-management-cloud/request/attachment#create-attachment
-func (s *AttachmentService) Create(ctx context.Context, issueKeyOrID string, temporaryAttachmentIDs []string, public bool) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error) {
-	return s.internalClient.Create(ctx, issueKeyOrID, temporaryAttachmentIDs, public)
+func (s *AttachmentService) Create(ctx context.Context, issueKeyOrID string, payload *model.RequestAttachmentCreationPayloadScheme) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error) {
+	return s.internalClient.Create(ctx, issueKeyOrID, payload)
 }
 
 type internalServiceRequestAttachmentImpl struct {
@@ -73,21 +74,16 @@ func (i *internalServiceRequestAttachmentImpl) Gets(ctx context.Context, issueKe
 	return page, res, nil
 }
 
-func (i *internalServiceRequestAttachmentImpl) Create(ctx context.Context, issueKeyOrID string, temporaryAttachmentIDs []string, public bool) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error) {
+func (i *internalServiceRequestAttachmentImpl) Create(ctx context.Context, issueKeyOrID string, payload *model.RequestAttachmentCreationPayloadScheme) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error) {
 
 	if issueKeyOrID == "" {
 		return nil, nil, model.ErrNoIssueKeyOrIDError
 	}
 
-	if len(temporaryAttachmentIDs) == 0 {
+	if len(payload.TemporaryAttachmentIDs) == 0 {
 		return nil, nil, model.ErrNoAttachmentIDError
 	}
 
-	payload := map[string]interface{}{
-		"temporaryAttachmentIds": temporaryAttachmentIDs,
-		"public":                 public,
-	}
-
 	url := fmt.Sprintf("rest/servicedeskapi/request/%v/attachment", issueKeyOrID)
 
 	req, err := i.c.NewRequest(ctx, http.MethodPost, url, "", payload)
diff --git a/jira/sm/internal/type_impl.go b/jira/sm/internal/type_impl.go
index 42bbf11..a1ffe4f 100644
--- a/jira/sm/internal/type_impl.go
+++ b/jira/sm/internal/type_impl.go
@@ -3,12 +3,13 @@ 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/sm"
 	"net/http"
 	"net/url"
 	"strconv"
+
+	model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
+	"github.com/ctreminiom/go-atlassian/service"
+	"github.com/ctreminiom/go-atlassian/service/sm"
 )
 
 func NewTypeService(client service.Connector, version string) *TypeService {
@@ -77,6 +78,15 @@ func (t *TypeService) Fields(ctx context.Context, serviceDeskID, requestTypeID i
 	return t.internalClient.Fields(ctx, serviceDeskID, requestTypeID)
 }
 
+// Groups returns the groups for a service desk's customer request type.
+//
+// GET /rest/servicedeskapi/servicedesk/{serviceDeskId}/requesttypegroup
+//
+// https://docs.go-atlassian.io/jira-service-management-cloud/request/types#get-request-type-groups
+func (t *TypeService) Groups(ctx context.Context, serviceDeskID int) (*model.RequestTypeGroupPageScheme, *model.ResponseScheme, error) {
+	return t.internalClient.Groups(ctx, serviceDeskID)
+}
+
 type internalTypeImpl struct {
 	c       service.Connector
 	version string
@@ -231,3 +241,25 @@ func (i *internalTypeImpl) Fields(ctx context.Context, serviceDeskID, requestTyp
 
 	return fields, res, nil
 }
+
+func (i *internalTypeImpl) Groups(ctx context.Context, serviceDeskID int) (*model.RequestTypeGroupPageScheme, *model.ResponseScheme, error) {
+
+	if serviceDeskID == 0 {
+		return nil, nil, model.ErrNoServiceDeskIDError
+	}
+
+	endpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/requesttypegroup", serviceDeskID)
+
+	req, err := i.c.NewRequest(ctx, http.MethodGet, endpoint, "", nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	groups := new(model.RequestTypeGroupPageScheme)
+	res, err := i.c.Call(req, groups)
+	if err != nil {
+		return nil, res, err
+	}
+
+	return groups, res, nil
+}
diff --git a/pkg/infra/models/bitbucket_projects.go b/pkg/infra/models/bitbucket_projects.go
index af87a7a..06ad368 100644
--- a/pkg/infra/models/bitbucket_projects.go
+++ b/pkg/infra/models/bitbucket_projects.go
@@ -14,7 +14,7 @@ type BitbucketProjectPageScheme struct {
 // BitbucketProjectScheme represents a Bitbucket project.
 type BitbucketProjectScheme struct {
 	Links                   *BitbucketProjectLinksScheme `json:"links,omitempty"`                      // The links related to the project.
-	Uuid                    string                       `json:"uuid,omitempty"`                       // The UUID of the project.
+	UUID                    string                       `json:"uuid,omitempty"`                       // The UUID of the project.
 	Key                     string                       `json:"key,omitempty"`                        // The key of the project.
 	Name                    string                       `json:"name,omitempty"`                       // The name of the project.
 	Description             string                       `json:"description,omitempty"`                // The description of the project.
@@ -26,6 +26,6 @@ type BitbucketProjectScheme struct {
 
 // BitbucketProjectLinksScheme represents the links related to a Bitbucket project.
 type BitbucketProjectLinksScheme struct {
-	Html   *BitbucketLinkScheme `json:"html,omitempty"`   // The HTML link of the project.
+	HTML   *BitbucketLinkScheme `json:"html,omitempty"`   // The HTML link of the project.
 	Avatar *BitbucketLinkScheme `json:"avatar,omitempty"` // The avatar link of the project.
 }
diff --git a/pkg/infra/models/bitbucket_repository.go b/pkg/infra/models/bitbucket_repository.go
index bf43cbf..ba13a2a 100644
--- a/pkg/infra/models/bitbucket_repository.go
+++ b/pkg/infra/models/bitbucket_repository.go
@@ -30,10 +30,10 @@ type RepositoryPermissionScheme struct {
 
 // RepositoryScheme represents a repository.
 // Type is the type of the repository.
-// Uuid is the unique identifier of the repository.
+// UUID is the unique identifier of the repository.
 // FullName is the full name of the repository.
 // IsPrivate indicates if the repository is private.
-// Scm is the source control management system used by the repository.
+// SCM is the source control management system used by the repository.
 // Name is the name of the repository.
 // Description is the description of the repository.
 // CreatedOn is the creation time of the repository.
@@ -49,10 +49,10 @@ type RepositoryPermissionScheme struct {
 // Links is a collection of links related to the repository.
 type RepositoryScheme struct {
 	Type        string                  `json:"type,omitempty"`        // The type of the repository.
-	Uuid        string                  `json:"uuid,omitempty"`        // The unique identifier of the repository.
+	UUID        string                  `json:"uuid,omitempty"`        // The unique identifier of the repository.
 	FullName    string                  `json:"full_name,omitempty"`   // The full name of the repository.
 	IsPrivate   bool                    `json:"is_private,omitempty"`  // Indicates if the repository is private.
-	Scm         string                  `json:"scm,omitempty"`         // The source control management system used by the repository.
+	SCM         string                  `json:"scm,omitempty"`         // The source control management system used by the repository.
 	Name        string                  `json:"name,omitempty"`        // The name of the repository.
 	Description string                  `json:"description,omitempty"` // The description of the repository.
 	CreatedOn   string                  `json:"created_on,omitempty"`  // The creation time of the repository.
@@ -70,7 +70,7 @@ type RepositoryScheme struct {
 
 // RepositoryLinksScheme represents a collection of links related to a repository.
 // Self is the link to the repository itself.
-// Html is the link to the repository's HTML page.
+// HTML is the link to the repository's HTML page.
 // Avatar is the link to the repository's avatar.
 // PullRequests is the link to the repository's pull requests.
 // Commits is the link to the repository's commits.
@@ -81,7 +81,7 @@ type RepositoryScheme struct {
 // Hooks is the link to the repository's hooks.
 type RepositoryLinksScheme struct {
 	Self         *BitbucketLinkScheme   `json:"self,omitempty"`         // The link to the repository itself.
-	Html         *BitbucketLinkScheme   `json:"html,omitempty"`         // The link to the repository's HTML page.
+	HTML         *BitbucketLinkScheme   `json:"html,omitempty"`         // The link to the repository's HTML page.
 	Avatar       *BitbucketLinkScheme   `json:"avatar,omitempty"`       // The link to the repository's avatar.
 	PullRequests *BitbucketLinkScheme   `json:"pullrequests,omitempty"` // The link to the repository's pull requests.
 	Commits      *BitbucketLinkScheme   `json:"commits,omitempty"`      // The link to the repository's commits.
diff --git a/pkg/infra/models/bitbucket_webhooks.go b/pkg/infra/models/bitbucket_webhooks.go
index bf711bb..c8531ea 100644
--- a/pkg/infra/models/bitbucket_webhooks.go
+++ b/pkg/infra/models/bitbucket_webhooks.go
@@ -2,12 +2,12 @@ package models
 
 // WebhookSubscriptionPayloadScheme represents the payload for a webhook subscription.
 // Description is the description of the webhook subscription.
-// Url is the URL of the webhook subscription.
+// URL is the URL of the webhook subscription.
 // Active indicates if the webhook subscription is active.
 // Events is a slice of the events for the webhook subscription.
 type WebhookSubscriptionPayloadScheme struct {
 	Description string   `json:"description,omitempty"` // The description of the webhook subscription.
-	Url         string   `json:"url,omitempty"`         // The URL of the webhook subscription.
+	URL         string   `json:"url,omitempty"`         // The URL of the webhook subscription.
 	Active      bool     `json:"active,omitempty"`      // Indicates if the webhook subscription is active.
 	Events      []string `json:"events,omitempty"`      // The events for the webhook subscription.
 }
diff --git a/pkg/infra/models/bitbucket_workspace.go b/pkg/infra/models/bitbucket_workspace.go
index 904cb55..00308de 100644
--- a/pkg/infra/models/bitbucket_workspace.go
+++ b/pkg/infra/models/bitbucket_workspace.go
@@ -3,7 +3,7 @@ package models
 // WorkspaceScheme represents a workspace.
 // Type is the type of the workspace.
 // Links is a collection of links related to the workspace.
-// Uuid is the unique identifier of the workspace.
+// UUID is the unique identifier of the workspace.
 // Name is the name of the workspace.
 // Slug is the slug of the workspace.
 // IsPrivate indicates if the workspace is private.
@@ -12,7 +12,7 @@ package models
 type WorkspaceScheme struct {
 	Type      string                `json:"type,omitempty"`       // The type of the workspace.
 	Links     *WorkspaceLinksScheme `json:"links,omitempty"`      // The links related to the workspace.
-	Uuid      string                `json:"uuid,omitempty"`       // The unique identifier of the workspace.
+	UUID      string                `json:"uuid,omitempty"`       // The unique identifier of the workspace.
 	Name      string                `json:"name,omitempty"`       // The name of the workspace.
 	Slug      string                `json:"slug,omitempty"`       // The slug of the workspace.
 	IsPrivate bool                  `json:"is_private,omitempty"` // Indicates if the workspace is private.
@@ -22,7 +22,7 @@ type WorkspaceScheme struct {
 
 // WorkspaceLinksScheme represents a collection of links related to a workspace.
 // Avatar is the link to the workspace's avatar.
-// Html is the link to the workspace's HTML page.
+// HTML is the link to the workspace's HTML page.
 // Members is the link to the workspace's members.
 // Owners is the link to the workspace's owners.
 // Projects is the link to the workspace's projects.
@@ -31,7 +31,7 @@ type WorkspaceScheme struct {
 // Self is the link to the workspace itself.
 type WorkspaceLinksScheme struct {
 	Avatar       *BitbucketLinkScheme `json:"avatar,omitempty"`       // The link to the workspace's avatar.
-	Html         *BitbucketLinkScheme `json:"html,omitempty"`         // The link to the workspace's HTML page.
+	HTML         *BitbucketLinkScheme `json:"html,omitempty"`         // The link to the workspace's HTML page.
 	Members      *BitbucketLinkScheme `json:"members,omitempty"`      // The link to the workspace's members.
 	Owners       *BitbucketLinkScheme `json:"owners,omitempty"`       // The link to the workspace's owners.
 	Projects     *BitbucketLinkScheme `json:"projects,omitempty"`     // The link to the workspace's projects.
diff --git a/pkg/infra/models/bitbucket_workspace_membership.go b/pkg/infra/models/bitbucket_workspace_membership.go
index 1fa05f7..5fb446e 100644
--- a/pkg/infra/models/bitbucket_workspace_membership.go
+++ b/pkg/infra/models/bitbucket_workspace_membership.go
@@ -45,27 +45,27 @@ type WorkspaceMembershipLinksScheme struct {
 // CreatedOn is the creation time of the account.
 // DisplayName is the display name of the account.
 // Username is the username of the account.
-// Uuid is the unique identifier of the account.
+// UUID is the unique identifier of the account.
 // Type is the type of the account.
-// AccountId is the account ID of the account.
+// AccountID is the account ID of the account.
 // Nickname is the nickname of the account.
 type BitbucketAccountScheme struct {
 	Links       *BitbucketAccountLinksScheme `json:"links,omitempty"`        // The links related to the account.
 	CreatedOn   string                       `json:"created_on,omitempty"`   // The creation time of the account.
 	DisplayName string                       `json:"display_name,omitempty"` // The display name of the account.
 	Username    string                       `json:"username,omitempty"`     // The username of the account.
-	Uuid        string                       `json:"uuid,omitempty"`         // The unique identifier of the account.
+	UUID        string                       `json:"uuid,omitempty"`         // The unique identifier of the account.
 	Type        string                       `json:"type,omitempty"`         // The type of the account.
-	AccountId   string                       `json:"account_id,omitempty"`   // The account ID of the account.
+	AccountID   string                       `json:"account_id,omitempty"`   // The account ID of the account.
 	Nickname    string                       `json:"nickname,omitempty"`     // The nickname of the account.
 }
 
 // BitbucketAccountLinksScheme represents a collection of links related to a Bitbucket account.
 // Avatar is the link to the account's avatar.
 // Self is the link to the account itself.
-// Html is the link to the account's HTML page.
+// HTML is the link to the account's HTML page.
 type BitbucketAccountLinksScheme struct {
 	Avatar *BitbucketLinkScheme `json:"avatar,omitempty"` // The link to the account's avatar.
 	Self   *BitbucketLinkScheme `json:"self,omitempty"`   // The link to the account itself.
-	Html   *BitbucketLinkScheme `json:"html,omitempty"`   // The link to the account's HTML page.
+	HTML   *BitbucketLinkScheme `json:"html,omitempty"`   // The link to the account's HTML page.
 }
diff --git a/pkg/infra/models/jira_field.go b/pkg/infra/models/jira_field.go
index b073bc6..8abbf2e 100644
--- a/pkg/infra/models/jira_field.go
+++ b/pkg/infra/models/jira_field.go
@@ -60,3 +60,25 @@ type CustomFieldScheme struct {
 	FieldType   string `json:"type,omitempty"`
 	SearcherKey string `json:"searcherKey,omitempty"`
 }
+
+// CustomFieldAssetScheme represents a custom field asset in Jira.
+type CustomFieldAssetScheme struct {
+	WorkspaceId string `json:"workspaceId,omitempty"` // The ID of the workspace.
+	Id          string `json:"id,omitempty"`          // The ID of the custom field asset.
+	ObjectId    string `json:"objectId,omitempty"`    // The object ID of the custom field asset.
+}
+
+// CustomFieldRequestTypeScheme represents a custom field request type in Jira.
+type CustomFieldRequestTypeScheme struct {
+	Links         *CustomFieldRequestTypeLinkScheme   `json:"_links"`        // Links related to the custom field request type.
+	RequestType   *CustomerRequestTypeScheme          `json:"requestType"`   // The request type of the custom field.
+	CurrentStatus *CustomerRequestCurrentStatusScheme `json:"currentStatus"` // The current status of the request type.
+}
+
+// CustomFieldRequestTypeLinkScheme represents the links of a custom field request type in Jira.
+type CustomFieldRequestTypeLinkScheme struct {
+	Self     string `json:"self,omitempty"`     // The URL of the custom field request type itself.
+	JiraRest string `json:"jiraRest,omitempty"` // The Jira REST API link for the custom field request type.
+	Web      string `json:"web,omitempty"`      // The web link for the custom field request type.
+	Agent    string `json:"agent,omitempty"`    // The agent link for the custom field request type.
+}
diff --git a/pkg/infra/models/jira_field_context_option.go b/pkg/infra/models/jira_field_context_option.go
index 673bdf4..c197f3a 100644
--- a/pkg/infra/models/jira_field_context_option.go
+++ b/pkg/infra/models/jira_field_context_option.go
@@ -1,12 +1,5 @@
 package models
 
-// CustomFieldAssetScheme represents a custom field asset in Jira.
-type CustomFieldAssetScheme struct {
-	WorkspaceId string `json:"workspaceId,omitempty"` // The ID of the workspace.
-	Id          string `json:"id,omitempty"`          // The ID of the custom field asset.
-	ObjectId    string `json:"objectId,omitempty"`    // The object ID of the custom field asset.
-}
-
 // CustomFieldContextOptionPageScheme represents a page of custom field context options in Jira.
 type CustomFieldContextOptionPageScheme struct {
 	Self       string                            `json:"self,omitempty"`       // The URL of the page.
diff --git a/pkg/infra/models/jira_issue_custom_fields.go b/pkg/infra/models/jira_issue_custom_fields.go
index f895ae3..359a85c 100644
--- a/pkg/infra/models/jira_issue_custom_fields.go
+++ b/pkg/infra/models/jira_issue_custom_fields.go
@@ -4,9 +4,10 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"github.com/tidwall/gjson"
 	"reflect"
 	"time"
+
+	"github.com/tidwall/gjson"
 )
 
 // ParseMultiSelectCustomField parses a multi-select custom field from the given buffer data
@@ -1517,11 +1518,11 @@ func ParseAssetCustomField(buffer bytes.Buffer, customField string) ([]*CustomFi
 //	}
 //
 // Parameters:
-//   - customField: The name of the multi-select custom field to parse.
+//   - customField: The name of the asset custom field to parse.
 //   - buffer: A bytes.Buffer containing JSON data representing custom field values.
 //
 // Returns:
-//   - map[string]*CustomFieldAssetScheme: A map where the key is the issue key and the
+//   - map[string][]*CustomFieldAssetScheme: A map where the key is the issue key and the
 //     value is a CustomFieldAssetScheme struct representing the parsed
 //     jira assets values.
 //   - error: An error if there was a problem parsing the custom field data or if the JSON data
@@ -1818,3 +1819,129 @@ func ParseDateTimeCustomFields(buffer bytes.Buffer, customField string) (map[str
 
 	return customfieldsAsMap, nil
 }
+
+// ParseRequestTypeCustomField parses the Jira service desk request type elements from the given buffer
+// data associated with the specified custom field ID and returns a struct CustomFieldRequestTypeScheme
+//
+// Parameters:
+//   - customfieldID: A string representing the unique identifier of the custom field.
+//   - buffer: A bytes.Buffer containing the serialized data to be parsed.
+//
+// Returns:
+//   - *CustomFieldRequestTypeScheme: the customfield value as CustomFieldRequestTypeScheme type
+//
+// Example usage:
+//
+//	customfieldID := "customfield_10010"
+//	buffer := bytes.NewBuffer([]byte{ /* Serialized data */ })
+//	requestType, err := ParseRequestTypeCustomField(customfieldID, buffer)
+//	if err != nil {
+//	    log.Fatal(err)
+//	}
+//
+// fmt.Println(requestType)
+//
+// Docs: https://docs.go-atlassian.io/cookbooks/extract-customfields-from-issue-s#parse-requesttype-customfield
+func ParseRequestTypeCustomField(buffer bytes.Buffer, customField string) (*CustomFieldRequestTypeScheme, error) {
+
+	raw := gjson.ParseBytes(buffer.Bytes())
+	path := fmt.Sprintf("fields.%v", customField)
+
+	// Check if the buffer contains the "fields" object
+	if !raw.Get("fields").Exists() {
+		return nil, ErrNoFieldInformationError
+	}
+
+	// Check if the issue iteration contains information on the customfield selected,
+	// if not, continue
+	if raw.Get(path).Type == gjson.Null {
+		return nil, ErrNoAssetTypeError
+	}
+
+	var requestType *CustomFieldRequestTypeScheme
+	if err := json.Unmarshal([]byte(raw.Get(path).String()), &requestType); err != nil {
+		return nil, ErrNoAssetTypeError
+	}
+
+	return requestType, nil
+}
+
+// ParseRequestTypeCustomFields extracts and parses jira service desk reqeust type customfield data from
+// a given bytes.Buffer from multiple issues
+//
+// This function takes the name of the custom field to parse and a bytes.Buffer containing
+// JSON data representing the custom field values associated with different issues. It returns
+// a map where the key is the issue key and the value is a slice of CustomFieldRequestTypeScheme
+// structs, representing the parsed assets associated with a Jira issues.
+//
+// The JSON data within the buffer is expected to have a specific structure where the custom field
+// values are organized by issue keys and options are represented within a context. The function
+// parses this structure to extract and organize the custom field values.
+//
+// If the custom field data cannot be parsed successfully, an error is returned.
+//
+// Example Usage:
+//
+//	customFieldName := "customfield_10010"
+//	buffer := // Populate the buffer with JSON data
+//	customFields, err := ParseRequestTypeCustomFields(customFieldName, buffer)
+//	if err != nil {
+//	    // Handle the error
+//	}
+//
+//	// Iterate through the parsed custom fields
+//	for issueKey, customFieldValues := range customFields {
+//	    fmt.Printf("Issue Key: %s\n", issueKey)
+//	    fmt.Printf("Custom Field Value: %+v\n", customFieldValues)
+//	}
+//
+// Parameters:
+//   - customField: The name of the request type custom field to parse.
+//   - buffer: A bytes.Buffer containing JSON data representing custom field values.
+//
+// Returns:
+//   - map[string]*CustomFieldRequestTypeScheme: A map where the key is the issue key and the
+//     value is a CustomFieldRequestTypeScheme struct representing the parsed
+//     jira service desk request type values.
+//   - error: An error if there was a problem parsing the custom field data or if the JSON data
+//     did not conform to the expected structure.
+//
+// Docs: https://docs.go-atlassian.io/cookbooks/extract-customfields-from-issue-s#parse-requesttype-customfields
+func ParseRequestTypeCustomFields(buffer bytes.Buffer, customField string) (map[string]*CustomFieldRequestTypeScheme, error) {
+
+	raw := gjson.ParseBytes(buffer.Bytes())
+
+	// Check if the buffer contains the "issues" object
+	if !raw.Get("issues").Exists() {
+		return nil, ErrNoIssuesSliceError
+	}
+
+	// Loop through each custom field, extract the information and stores the data on a map
+	customfieldsAsMap := make(map[string]*CustomFieldRequestTypeScheme)
+	raw.Get("issues").ForEach(func(key, value gjson.Result) bool {
+
+		path, issueKey := fmt.Sprintf("fields.%v", customField), value.Get("key").String()
+
+		// Check if the issue iteration contains information on the customfield selected,
+		// if not, continue
+		if value.Get(path).Type == gjson.Null {
+			return true
+		}
+
+		var customFields *CustomFieldRequestTypeScheme
+		if err := json.Unmarshal([]byte(value.Get(path).String()), &customFields); err != nil {
+			return true
+		}
+
+		customfieldsAsMap[issueKey] = customFields
+		return true
+	})
+
+	// Check if the map processed contains elements
+	// if so, return an error interface
+	if len(customfieldsAsMap) == 0 {
+		return nil, ErrNoMapValuesError
+	}
+
+	return customfieldsAsMap, nil
+}
diff --git a/pkg/infra/models/sm_request_attachments.go b/pkg/infra/models/sm_request_attachments.go
index b268145..d565b88 100644
--- a/pkg/infra/models/sm_request_attachments.go
+++ b/pkg/infra/models/sm_request_attachments.go
@@ -62,6 +62,18 @@ type RequestAttachmentCreationCommentScheme struct {
 	} `json:"_links,omitempty"` // Links related to the comment.
 }
 
+// RequestAttachmentCreationPayloadScheme represents the payload for creating a request attachment.
+type RequestAttachmentCreationPayloadScheme struct {
+	TemporaryAttachmentIDs []string                                                 `json:"temporaryAttachmentIds,omitempty"`
+	Public                 bool                                                     `json:"public,omitempty"`
+	AdditionalComment      *RequestAttachmentCreationAdditionalCommentPayloadScheme `json:"additionalComment,omitempty"`
+}
+
+// RequestAttachmentCreationAdditionalCommentPayloadScheme represents the additional comment for creating a request attachment.
+type RequestAttachmentCreationAdditionalCommentPayloadScheme struct {
+	Body string `json:"body,omitempty"`
+}
+
 // RequestAttachmentCreationScheme represents the creation of a request attachment.
 type RequestAttachmentCreationScheme struct {
 	Comment     *RequestAttachmentCreationCommentScheme `json:"comment,omitempty"`     // The comment during the creation of the request attachment.
diff --git a/pkg/infra/models/sm_request_type.go b/pkg/infra/models/sm_request_type.go
index 6a9b717..c37cd3f 100644
--- a/pkg/infra/models/sm_request_type.go
+++ b/pkg/infra/models/sm_request_type.go
@@ -93,3 +93,28 @@ type RequestTypeJiraSchema struct {
 	Custom   string `json:"custom,omitempty"`   // The custom of the Jira schema.
 	CustomID int    `json:"customId,omitempty"` // The custom ID of the Jira schema.
 }
+
+// RequestTypeGroupPageScheme represents a page of project request type groups.
+type RequestTypeGroupPageScheme struct {
+	Expands    []string                        `json:"_expands,omitempty"`   // The fields to expand in the page.
+	Size       int                             `json:"size,omitempty"`       // The size of the page.
+	Start      int                             `json:"start,omitempty"`      // The start index of the page.
+	Limit      int                             `json:"limit,omitempty"`      // The limit of the page.
+	IsLastPage bool                            `json:"isLastPage,omitempty"` // Indicates if this is the last page.
+	Values     []*RequestTypeGroupsScheme      `json:"values,omitempty"`     // The project request types in the page.
+	Links      *RequestTypeGroupPageLinkScheme `json:"_links,omitempty"`     // The links related to the page.
+}
+
+// RequestTypeGroupPageLinkScheme represents the links related to a page of project request type groups.
+type RequestTypeGroupPageLinkScheme struct {
+	Base    string `json:"base,omitempty"`    // The base link of the page.
+	Context string `json:"context,omitempty"` // The context link of the page.
+	Next    string `json:"next,omitempty"`    // The next link of the page.
+	Prev    string `json:"prev,omitempty"`    // The previous link of the page.
+}
+
+// RequestTypeGroupsScheme represents the groups for request types.
+type RequestTypeGroupsScheme struct {
+	ID   string `json:"id,omitempty"`   // The ID of the request type group.
+	Name string `json:"name,omitempty"` // The name of the request type group.
+}
diff --git a/service/sm/attachment.go b/service/sm/attachment.go
index 121d8e8..a3770f1 100644
--- a/service/sm/attachment.go
+++ b/service/sm/attachment.go
@@ -2,6 +2,7 @@ package sm
 
 import (
 	"context"
+
 	model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
 )
 
@@ -21,5 +22,5 @@ type AttachmentConnector interface {
 	// POST /rest/servicedeskapi/request/{issueIdOrKey}/attachment
 	//
 	// https://docs.go-atlassian.io/jira-service-management-cloud/request/attachment#create-attachment
-	Create(ctx context.Context, issueKeyOrID string, temporaryAttachmentIDs []string, public bool) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error)
+	Create(ctx context.Context, issueKeyOrID string, payload *model.RequestAttachmentCreationPayloadScheme) (*model.RequestAttachmentCreationScheme, *model.ResponseScheme, error)
 }
diff --git a/service/sm/type.go b/service/sm/type.go
index 385722e..2c58542 100644
--- a/service/sm/type.go
+++ b/service/sm/type.go
@@ -2,6 +2,7 @@ package sm
 
 import (
 	"context"
+
 	model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
 )
 
@@ -49,4 +50,11 @@ type TypeConnector interface {
 	//
 	// https://docs.go-atlassian.io/jira-service-management-cloud/request/types#get-request-type-fields
 	Fields(ctx context.Context, serviceDeskID, requestTypeID int) (*model.RequestTypeFieldsScheme, *model.ResponseScheme, error)
+
+	// Groups returns the groups for a service desk's customer request types.
+	//
+	// GET /rest/servicedeskapi/servicedesk/{serviceDeskId}/requesttypegroup
+	//
+	// https://docs.go-atlassian.io/jira-service-management-cloud/request/types#get-request-type-groups
+	Groups(ctx context.Context, serviceDeskID int) (*model.RequestTypeGroupPageScheme, *model.ResponseScheme, error)
 }


Test patch:
diff --git a/bitbucket/internal/workspace_webhooks_impl_test.go b/bitbucket/internal/workspace_webhooks_impl_test.go
index 9776003..f8abca6 100644
--- a/bitbucket/internal/workspace_webhooks_impl_test.go
+++ b/bitbucket/internal/workspace_webhooks_impl_test.go
@@ -3,12 +3,14 @@ package internal
 import (
 	"context"
 	"errors"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
 	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_internalWorkspaceWebhookServiceImpl_Gets(t *testing.T) {
@@ -246,7 +248,7 @@ func Test_internalWorkspaceWebhookServiceImpl_Create(t *testing.T) {
 
 	payloadMocked := &model.WebhookSubscriptionPayloadScheme{
 		Description: "",
-		Url:         "",
+		URL:         "",
 		Active:      false,
 		Events:      nil,
 	}
@@ -365,7 +367,7 @@ func Test_internalWorkspaceWebhookServiceImpl_Update(t *testing.T) {
 
 	payloadMocked := &model.WebhookSubscriptionPayloadScheme{
 		Description: "",
-		Url:         "",
+		URL:         "",
 		Active:      false,
 		Events:      nil,
 	}
diff --git a/jira/sm/internal/attachment_impl_test.go b/jira/sm/internal/attachment_impl_test.go
index aa9d8ce..6e3d592 100644
--- a/jira/sm/internal/attachment_impl_test.go
+++ b/jira/sm/internal/attachment_impl_test.go
@@ -3,12 +3,14 @@ package internal
 import (
 	"context"
 	"errors"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
 	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_internalServiceRequestAttachmentImpl_Gets(t *testing.T) {
@@ -167,10 +169,9 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 	}
 
 	type args struct {
-		ctx                    context.Context
-		issueKeyOrID           string
-		temporaryAttachmentIDs []string
-		public                 bool
+		ctx          context.Context
+		issueKeyOrID string
+		payload      *model.RequestAttachmentCreationPayloadScheme
 	}
 
 	testCases := []struct {
@@ -184,10 +185,12 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 		{
 			name: "when the parameters are correct",
 			args: args{
-				ctx:                    context.Background(),
-				issueKeyOrID:           "DUMMY-2",
-				temporaryAttachmentIDs: []string{"10001"},
-				public:                 true,
+				ctx:          context.Background(),
+				issueKeyOrID: "DUMMY-2",
+				payload: &model.RequestAttachmentCreationPayloadScheme{
+					TemporaryAttachmentIDs: []string{"10001"},
+					Public:                 true,
+				},
 			},
 			on: func(fields *fields) {
 
@@ -198,7 +201,10 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 					http.MethodPost,
 					"rest/servicedeskapi/request/DUMMY-2/attachment",
 					"",
-					map[string]interface{}{"public": true, "temporaryAttachmentIds": []string{"10001"}}).
+					&model.RequestAttachmentCreationPayloadScheme{
+						TemporaryAttachmentIDs: []string{"10001"},
+						Public:                 true,
+					}).
 					Return(&http.Request{}, nil)
 
 				client.On("Call",
@@ -213,10 +219,12 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 		{
 			name: "when the http call cannot be executed",
 			args: args{
-				ctx:                    context.Background(),
-				issueKeyOrID:           "DUMMY-2",
-				temporaryAttachmentIDs: []string{"10001"},
-				public:                 true,
+				ctx:          context.Background(),
+				issueKeyOrID: "DUMMY-2",
+				payload: &model.RequestAttachmentCreationPayloadScheme{
+					TemporaryAttachmentIDs: []string{"10001"},
+					Public:                 true,
+				},
 			},
 			on: func(fields *fields) {
 
@@ -227,7 +235,10 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 					http.MethodPost,
 					"rest/servicedeskapi/request/DUMMY-2/attachment",
 					"",
-					map[string]interface{}{"public": true, "temporaryAttachmentIds": []string{"10001"}}).
+					&model.RequestAttachmentCreationPayloadScheme{
+						TemporaryAttachmentIDs: []string{"10001"},
+						Public:                 true,
+					}).
 					Return(&http.Request{}, nil)
 
 				client.On("Call",
@@ -244,10 +255,12 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 		{
 			name: "when the request cannot be created",
 			args: args{
-				ctx:                    context.Background(),
-				issueKeyOrID:           "DUMMY-2",
-				temporaryAttachmentIDs: []string{"10001"},
-				public:                 true,
+				ctx:          context.Background(),
+				issueKeyOrID: "DUMMY-2",
+				payload: &model.RequestAttachmentCreationPayloadScheme{
+					TemporaryAttachmentIDs: []string{"10001"},
+					Public:                 true,
+				},
 			},
 			on: func(fields *fields) {
 
@@ -258,7 +271,10 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 					http.MethodPost,
 					"rest/servicedeskapi/request/DUMMY-2/attachment",
 					"",
-					map[string]interface{}{"public": true, "temporaryAttachmentIds": []string{"10001"}}).
+					&model.RequestAttachmentCreationPayloadScheme{
+						TemporaryAttachmentIDs: []string{"10001"},
+						Public:                 true,
+					}).
 					Return(&http.Request{}, errors.New("client: no http request created"))
 
 				fields.c = client
@@ -290,7 +306,7 @@ func Test_internalServiceRequestAttachmentImpl_Create(t *testing.T) {
 			attachmentService := NewAttachmentService(testCase.fields.c, "latest")
 
 			gotResult, gotResponse, err := attachmentService.Create(testCase.args.ctx, testCase.args.issueKeyOrID,
-				testCase.args.temporaryAttachmentIDs, testCase.args.public)
+				testCase.args.payload)
 
 			if testCase.wantErr {