Repository: ctreminiom/go-atlassian
Pull request #172 (2023-03-03 00:04:31)

:construction: Custom-fields extraction support


Patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1997110..cf57db6 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -3,11 +3,7 @@ on:
   push:
     tags:
       - v*
-    branches:
-      - master
-      - main
-      - feature/*
-      - bug/*
+    branches: [ main, dev, feature/* ]
   pull_request:
 permissions:
   contents: read
@@ -16,7 +12,7 @@ permissions:
 jobs:
   golangci:
 
-    max-parallel: 3
+    max-parallel: 2
     matrix:
       go-version: [ 1.16, 1.17 ]
       os: [ ubuntu-latest, macos-latest, windows-latest ]
diff --git a/go.mod b/go.mod
index 36fd3f4..8929210 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.14
 require (
 	github.com/google/uuid v1.3.0
 	github.com/imdario/mergo v0.3.13
+	github.com/perimeterx/marshmallow v1.1.4
 	github.com/stretchr/testify v1.8.2
 	github.com/tidwall/gjson v1.14.3
 )
diff --git a/go.sum b/go.sum
index 1c6346e..6b0106b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,18 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
+github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
+github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -21,6 +29,10 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
 github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/jira/internal/user_search_impl.go b/jira/internal/user_search_impl.go
index efadd6b..f336a75 100644
--- a/jira/internal/user_search_impl.go
+++ b/jira/internal/user_search_impl.go
@@ -40,11 +40,12 @@ func (u *UserSearchService) Projects(ctx context.Context, accountId string, proj
 
 // Do return a list of users that match the search string and property.
 //
+//
 // This operation takes the users in the range defined by startAt and maxResults, up to the thousandth user,
 //
 // and then returns only the users from that range that match the search string and property.
 //
-// # This means the operation usually returns fewer users than specified in maxResults
+// This means the operation usually returns fewer users than specified in maxResults
 //
 // GET /rest/api/{2-3}/user/search
 //
@@ -53,70 +54,11 @@ func (u *UserSearchService) Do(ctx context.Context, accountId, query string, sta
 	return u.internalClient.Do(ctx, accountId, query, startAt, maxResults)
 }
 
-// Check returns a list of users who fulfill these criteria:
-//
-// 1. their user attributes match a search string.
-// 2. they have a set of permissions for a project or issue.
-//
-// If no search string is provided, a list of all users with the permissions is returned.
-//
-// GET /rest/api/{2-3}/user/permission/search
-//
-// https://docs.go-atlassian.io/jira-software-cloud/users/search#find-users-with-permissions
-func (u *UserSearchService) Check(ctx context.Context, permission string, options *model.UserPermissionCheckParamsScheme, startAt, maxResults int) ([]*model.UserScheme, *model.ResponseScheme, error) {
-	return u.internalClient.Check(ctx, permission, options, startAt, maxResults)
-}
-
 type internalUserSearchImpl struct {
 	c       service.Client
 	version string
 }
 
-func (i *internalUserSearchImpl) Check(ctx context.Context, permission string, options *model.UserPermissionCheckParamsScheme, startAt, maxResults int) ([]*model.UserScheme, *model.ResponseScheme, error) {
-
-	if permission == "" {
-		return nil, nil, model.ErrNoPermissionGrantIDError
-	}
-
-	params := url.Values{}
-	params.Add("startAt", strconv.Itoa(startAt))
-	params.Add("maxResults", strconv.Itoa(maxResults))
-
-	if options != nil {
-
-		if options.Query != "" {
-			params.Add("query", options.Query)
-		}
-
-		if options.AccountID != "" {
-			params.Add("accountId", options.AccountID)
-		}
-
-		if options.IssueKey != "" {
-			params.Add("issueKey", options.IssueKey)
-		}
-
-		if options.ProjectKey != "" {
-			params.Add("projectKey", options.ProjectKey)
-		}
-	}
-
-	endpoint := fmt.Sprintf("rest/api/%v/user/permission/search?%v", i.version, params.Encode())
-
-	request, err := i.c.NewRequest(ctx, http.MethodGet, endpoint, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var users []*model.UserScheme
-	response, err := i.c.Call(request, &users)
-	if err != nil {
-		return nil, response, err
-	}
-
-	return users, response, nil
-}
-
 func (i *internalUserSearchImpl) Projects(ctx context.Context, accountId string, projectKeys []string, startAt, maxResults int) ([]*model.UserScheme, *model.ResponseScheme, error) {
 
 	if len(projectKeys) == 0 {
diff --git a/pkg/infra/models/agile_sprint.go b/pkg/infra/models/agile_sprint.go
index 5e82bf2..520a800 100644
--- a/pkg/infra/models/agile_sprint.go
+++ b/pkg/infra/models/agile_sprint.go
@@ -44,3 +44,14 @@ type SprintMovePayloadScheme struct {
 	RankAfterIssue    string   `json:"rankAfterIssue,omitempty"`
 	RankCustomFieldId int      `json:"rankCustomFieldId,omitempty"`
 }
+
+type SprintDetailScheme struct {
+	ID            int    `json:"id,omitempty"`
+	State         string `json:"state,omitempty"`
+	Name          string `json:"name,omitempty"`
+	StartDate     string `json:"startDate,omitempty"`
+	EndDate       string `json:"endDate,omitempty"`
+	CompleteDate  string `json:"completeDate,omitempty"`
+	OriginBoardID int    `json:"originBoardId,omitempty"`
+	Goal          string `json:"goal,omitempty"`
+}
diff --git a/pkg/infra/models/errors.go b/pkg/infra/models/errors.go
index eebc22b..1791d97 100644
--- a/pkg/infra/models/errors.go
+++ b/pkg/infra/models/errors.go
@@ -51,6 +51,7 @@ var (
 	ErrNoDashboardIDError                  = errors.New("jira: no dashboard id set")
 	ErrNoGroupNameError                    = errors.New("jira: no group name set")
 	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")
 	ErrNoIssueSchemeError                  = errors.New("jira: no jira.IssueScheme set")
 	ErrNoTransitionIDError                 = errors.New("jira: no transition id set")
@@ -65,6 +66,10 @@ var (
 	ErrNoPropertyKeyError                  = errors.New("jira: no property key set")
 	ErrNoProjectFeatureKeyError            = errors.New("jira: no project feature key set")
 	ErrNoFieldIDError                      = errors.New("jira: no field id set")
+	ErrNoEditOperatorError                 = errors.New("jira: no update operation set")
+	ErrNoOperatorError                     = errors.New("jira: no operation set")
+	ErrNoEditValueError                    = errors.New("jira: no update operation value set")
+	ErrNoCustomFieldError                  = errors.New("jira: no custom-fields set")
 	ErrNoWorkflowStatusesError             = errors.New("jira: no workflow statuses set")
 	ErrNoWorkflowScopeError                = errors.New("jira: no workflow scope set")
 	ErrNoWorkflowStatusNameOrIdError       = errors.New("jira: no workflow status name or id set")
@@ -108,4 +113,18 @@ var (
 	ErrInvalidStatusCodeError              = errors.New("client: invalid http response status, please refer the response.body for more details")
 	ErrNilPayloadError                     = errors.New("client: please provide the necessary payload struct")
 	ErrNonPayloadPointerError              = errors.New("client: please provide a valid payload struct pointer (&)")
+	ErrNoFieldInformationError             = errors.New("custom-field: please provide a buffer with a valid fields object")
+	ErrNoCustomFieldUnmarshalError         = errors.New("custom-field: no valid json provided")
+	ErrNoMultiSelectTypeError              = errors.New("custom-field: no multiselect type found")
+	ErrNoUrlTypeError                      = errors.New("custom-field: no url type set")
+	ErrNoTextTypeError                     = errors.New("custom-field: no text type set")
+	ErrNoDateTimeTypeError                 = errors.New("custom-field: no date-time type set")
+	ErrNoDateTypeError                     = errors.New("custom-field: no date type set")
+	ErrNoSelectTypeError                   = errors.New("custom-field: no select type set")
+	ErrNoButtonTypeError                   = errors.New("custom-field: no button type set")
+	ErrNoUserTypeError                     = errors.New("custom-field: no user type set")
+	ErrNoMultiUserTypeError                = errors.New("custom-field: no multi-user type set")
+	ErrNoCheckBoxTypeError                 = errors.New("custom-field: no check-box type set")
+	ErrNoCascadingParentError              = errors.New("custom-field: no cascading parent value set")
+	ErrNoCascadingChildError               = errors.New("custom-field: no cascading child value set")
 )
diff --git a/pkg/infra/models/jira_customFields.go b/pkg/infra/models/jira_customFields.go
index dba2014..a02d04c 100644
--- a/pkg/infra/models/jira_customFields.go
+++ b/pkg/infra/models/jira_customFields.go
@@ -1,20 +1,19 @@
 package models
 
 import (
-	"fmt"
 	"time"
 )
 
 type CustomFields struct{ Fields []map[string]interface{} }
 
-func (c *CustomFields) Groups(customFieldID string, groups []string) (err error) {
+func (c *CustomFields) Groups(customFieldID string, groups []string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(groups) == 0 {
-		return fmt.Errorf("error, please provide a valid groups value")
+		return ErrNoGroupsNameError
 	}
 
 	var groupsNode []map[string]interface{}
@@ -33,17 +32,17 @@ func (c *CustomFields) Groups(customFieldID string, groups []string) (err error)
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Group(customFieldID, group string) (err error) {
+func (c *CustomFields) Group(customFieldID, group string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(group) == 0 {
-		return fmt.Errorf("error, please provide a valid group value")
+		return ErrNoGroupNameError
 	}
 
 	var groupNode = map[string]interface{}{}
@@ -56,17 +55,17 @@ func (c *CustomFields) Group(customFieldID, group string) (err error) {
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) URL(customFieldID, URL string) (err error) {
+func (c *CustomFields) URL(customFieldID, URL string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(URL) == 0 {
-		return fmt.Errorf("error, please provide a valid URL value")
+		return ErrNoUrlTypeError
 	}
 
 	var urlNode = map[string]interface{}{}
@@ -76,17 +75,17 @@ func (c *CustomFields) URL(customFieldID, URL string) (err error) {
 	fieldsNode["fields"] = urlNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Text(customFieldID, textValue string) (err error) {
+func (c *CustomFields) Text(customFieldID, textValue string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(textValue) == 0 {
-		return fmt.Errorf("error, please provide a valid textValue value")
+		return ErrNoTextTypeError
 	}
 
 	var urlNode = map[string]interface{}{}
@@ -96,17 +95,17 @@ func (c *CustomFields) Text(customFieldID, textValue string) (err error) {
 	fieldsNode["fields"] = urlNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) DateTime(customFieldID string, dateValue time.Time) (err error) {
+func (c *CustomFields) DateTime(customFieldID string, dateValue time.Time) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if dateValue.IsZero() {
-		return fmt.Errorf("error, please provide a valid dateValue value")
+		return ErrNoDateTimeTypeError
 	}
 
 	var dateNode = map[string]interface{}{}
@@ -116,17 +115,17 @@ func (c *CustomFields) DateTime(customFieldID string, dateValue time.Time) (err
 	fieldsNode["fields"] = dateNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
 func (c *CustomFields) Date(customFieldID string, dateTimeValue time.Time) (err error) {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if dateTimeValue.IsZero() {
-		return fmt.Errorf("error, please provide a valid dateValue value")
+		return ErrNoDateTypeError
 	}
 
 	var dateTimeNode = map[string]interface{}{}
@@ -139,14 +138,14 @@ func (c *CustomFields) Date(customFieldID string, dateTimeValue time.Time) (err
 	return
 }
 
-func (c *CustomFields) MultiSelect(customFieldID string, options []string) (err error) {
+func (c *CustomFields) MultiSelect(customFieldID string, options []string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(options) == 0 {
-		return fmt.Errorf("error, please provide a valid options value")
+		return ErrNoMultiSelectTypeError
 	}
 
 	var groupsNode []map[string]interface{}
@@ -165,17 +164,17 @@ func (c *CustomFields) MultiSelect(customFieldID string, options []string) (err
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Select(customFieldID string, option string) (err error) {
+func (c *CustomFields) Select(customFieldID string, option string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(option) == 0 {
-		return fmt.Errorf("error, please provide a valid option value")
+		return ErrNoSelectTypeError
 	}
 
 	var selectNode = map[string]interface{}{}
@@ -188,17 +187,17 @@ func (c *CustomFields) Select(customFieldID string, option string) (err error) {
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) RadioButton(customFieldID, button string) (err error) {
+func (c *CustomFields) RadioButton(customFieldID, button string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(button) == 0 {
-		return fmt.Errorf("error, please provide a button option value")
+		return ErrNoButtonTypeError
 	}
 
 	var selectNode = map[string]interface{}{}
@@ -211,17 +210,17 @@ func (c *CustomFields) RadioButton(customFieldID, button string) (err error) {
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) User(customFieldID string, accountID string) (err error) {
+func (c *CustomFields) User(customFieldID string, accountID string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(accountID) == 0 {
-		return fmt.Errorf("error, please provide a accountID option value")
+		return ErrNoUserTypeError
 	}
 
 	var userNode = map[string]interface{}{}
@@ -234,17 +233,17 @@ func (c *CustomFields) User(customFieldID string, accountID string) (err error)
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Users(customFieldID string, accountIDs []string) (err error) {
+func (c *CustomFields) Users(customFieldID string, accountIDs []string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(accountIDs) == 0 {
-		return fmt.Errorf("error, please provide a accountIDs value")
+		return ErrNoMultiUserTypeError
 	}
 
 	var accountsNode []map[string]interface{}
@@ -263,13 +262,13 @@ func (c *CustomFields) Users(customFieldID string, accountIDs []string) (err err
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Number(customFieldID string, numberValue float64) (err error) {
+func (c *CustomFields) Number(customFieldID string, numberValue float64) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	var urlNode = map[string]interface{}{}
@@ -279,17 +278,17 @@ func (c *CustomFields) Number(customFieldID string, numberValue float64) (err er
 	fieldsNode["fields"] = urlNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) CheckBox(customFieldID string, options []string) (err error) {
+func (c *CustomFields) CheckBox(customFieldID string, options []string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(options) == 0 {
-		return fmt.Errorf("error, please provide a valid options value")
+		return ErrNoCheckBoxTypeError
 	}
 
 	var groupsNode []map[string]interface{}
@@ -308,21 +307,21 @@ func (c *CustomFields) CheckBox(customFieldID string, options []string) (err err
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
 
-func (c *CustomFields) Cascading(customFieldID, parent, child string) (err error) {
+func (c *CustomFields) Cascading(customFieldID, parent, child string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
-	if len(parent) == 0 {
-		return fmt.Errorf("error, please provide a parent value")
+	if parent == "" {
+		return ErrNoCascadingParentError
 	}
 
-	if len(child) == 0 {
-		return fmt.Errorf("error, please provide a child value")
+	if child == "" {
+		return ErrNoCascadingChildError
 	}
 
 	var childNode = map[string]interface{}{}
@@ -339,5 +338,5 @@ func (c *CustomFields) Cascading(customFieldID, parent, child string) (err error
 	fieldsNode["fields"] = fieldNode
 
 	c.Fields = append(c.Fields, fieldsNode)
-	return
+	return nil
 }
diff --git a/pkg/infra/models/jira_field_context_option.go b/pkg/infra/models/jira_field_context_option.go
index c2201b6..58a3425 100644
--- a/pkg/infra/models/jira_field_context_option.go
+++ b/pkg/infra/models/jira_field_context_option.go
@@ -31,3 +31,16 @@ type OrderFieldOptionPayloadScheme struct {
 	Position             string   `json:"position,omitempty"`
 	CustomFieldOptionIds []string `json:"customFieldOptionIds,omitempty"`
 }
+
+type CascadingSelectScheme struct {
+	Self  string                      `json:"self,omitempty"`
+	Value string                      `json:"value,omitempty"`
+	Id    string                      `json:"id,omitempty"`
+	Child *CascadingSelectChildScheme `json:"child,omitempty"`
+}
+
+type CascadingSelectChildScheme struct {
+	Self  string `json:"self,omitempty"`
+	Value string `json:"value,omitempty"`
+	Id    string `json:"id,omitempty"`
+}
diff --git a/pkg/infra/models/jira_group.go b/pkg/infra/models/jira_group.go
index 8ff5e56..5f6a15b 100644
--- a/pkg/infra/models/jira_group.go
+++ b/pkg/infra/models/jira_group.go
@@ -27,14 +27,17 @@ type GroupUserPageScheme struct {
 }
 
 type BulkGroupScheme struct {
-	MaxResults int  `json:"maxResults,omitempty"`
-	StartAt    int  `json:"startAt,omitempty"`
-	Total      int  `json:"total,omitempty"`
-	IsLast     bool `json:"isLast,omitempty"`
-	Values     []struct {
-		Name    string `json:"name,omitempty"`
-		GroupID string `json:"groupId,omitempty"`
-	} `json:"values,omitempty"`
+	MaxResults int                  `json:"maxResults,omitempty"`
+	StartAt    int                  `json:"startAt,omitempty"`
+	Total      int                  `json:"total,omitempty"`
+	IsLast     bool                 `json:"isLast,omitempty"`
+	Values     []*GroupDetailScheme `json:"values,omitempty"`
+}
+
+type GroupDetailScheme struct {
+	Self    string `json:"self,omitempty"`
+	Name    string `json:"name,omitempty"`
+	GroupID string `json:"groupId,omitempty"`
 }
 
 type GroupMemberPageScheme struct {
diff --git a/pkg/infra/models/jira_issue_custom_fields.go b/pkg/infra/models/jira_issue_custom_fields.go
new file mode 100644
index 0000000..0da4fc4
--- /dev/null
+++ b/pkg/infra/models/jira_issue_custom_fields.go
@@ -0,0 +1,394 @@
+package models
+
+import (
+	"bytes"
+	"github.com/perimeterx/marshmallow"
+)
+
+func ParseMultiSelectCustomField(buffer bytes.Buffer, customField string) ([]*CustomFieldContextOptionScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var records []*CustomFieldContextOptionScheme
+
+	switch options := customFields[customField].(type) {
+	case []interface{}:
+
+		for _, option := range options {
+
+			record := &CustomFieldContextOptionScheme{
+				ID:    option.(map[string]interface{})["id"].(string),
+				Value: option.(map[string]interface{})["value"].(string),
+			}
+
+			isDisabled, wasFound := option.(map[string]interface{})["disabled"].(bool)
+			if wasFound {
+				record.Disabled = isDisabled
+			}
+
+			optionID, wasFound := option.(map[string]interface{})["optionId"].(string)
+			if wasFound {
+				record.OptionID = optionID
+			}
+
+			records = append(records, record)
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return records, nil
+}
+
+func ParseMultiGroupPickerCustomField(buffer bytes.Buffer, customField string) ([]*GroupDetailScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var groups []*GroupDetailScheme
+
+	switch options := customFields[customField].(type) {
+	case []interface{}:
+
+		for _, option := range options {
+
+			group := &GroupDetailScheme{
+				Self:    option.(map[string]interface{})["self"].(string),
+				Name:    option.(map[string]interface{})["name"].(string),
+				GroupID: option.(map[string]interface{})["groupId"].(string),
+			}
+
+			groups = append(groups, group)
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return groups, nil
+}
+
+func ParseMultiUserPickerCustomField(buffer bytes.Buffer, customField string) ([]*UserDetailScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var users []*UserDetailScheme
+
+	switch options := customFields[customField].(type) {
+	case []interface{}:
+
+		for _, option := range options {
+
+			user := &UserDetailScheme{
+				Self:        option.(map[string]interface{})["self"].(string),
+				AccountID:   option.(map[string]interface{})["accountId"].(string),
+				AccountType: option.(map[string]interface{})["accountType"].(string),
+				DisplayName: option.(map[string]interface{})["displayName"].(string),
+				Active:      option.(map[string]interface{})["active"].(bool),
+				TimeZone:    option.(map[string]interface{})["timeZone"].(string),
+			}
+
+			email, wasFound := option.(map[string]interface{})["emailAddress"].(string)
+			if wasFound {
+				user.EmailAddress = email
+			}
+
+			users = append(users, user)
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return users, nil
+}
+
+func ParseCascadingSelectCustomField(buffer bytes.Buffer, customField string) (*CascadingSelectScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	cascading := &CascadingSelectScheme{}
+
+	switch option := customFields[customField].(type) {
+	case map[string]interface{}:
+
+		cascading = &CascadingSelectScheme{
+			Self:  option["self"].(string),
+			Value: option["value"].(string),
+			Id:    option["id"].(string),
+			Child: &CascadingSelectChildScheme{
+				Self:  option["child"].(map[string]interface{})["self"].(string),
+				Value: option["child"].(map[string]interface{})["value"].(string),
+				Id:    option["child"].(map[string]interface{})["id"].(string),
+			},
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return cascading, nil
+}
+
+func ParseMultiCheckboxesCustomField(buffer bytes.Buffer, customField string) ([]*CustomFieldContextOptionScheme, error) {
+	return ParseMultiSelectCustomField(buffer, customField)
+}
+
+func ParseMultiVersionCustomField(buffer bytes.Buffer, customField string) ([]*VersionDetailScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var records []*VersionDetailScheme
+
+	switch options := customFields[customField].(type) {
+	case []interface{}:
+
+		for _, option := range options {
+
+			record := &VersionDetailScheme{
+				Self:        option.(map[string]interface{})["self"].(string),
+				ID:          option.(map[string]interface{})["id"].(string),
+				Description: option.(map[string]interface{})["description"].(string),
+				Name:        option.(map[string]interface{})["name"].(string),
+				Archived:    option.(map[string]interface{})["archived"].(bool),
+				Released:    option.(map[string]interface{})["released"].(bool),
+				ReleaseDate: option.(map[string]interface{})["releaseDate"].(string),
+			}
+
+			records = append(records, record)
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return records, nil
+}
+
+func ParseUserPickerCustomField(buffer bytes.Buffer, customField string) (*UserDetailScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	user := &UserDetailScheme{}
+
+	switch option := customFields[customField].(type) {
+	case map[string]interface{}:
+
+		user = &UserDetailScheme{
+			Self:        option["self"].(string),
+			AccountID:   option["accountId"].(string),
+			DisplayName: option["displayName"].(string),
+			Active:      option["active"].(bool),
+			TimeZone:    option["timeZone"].(string),
+			AccountType: option["accountType"].(string),
+		}
+
+		email, wasFound := option["emailAddress"].(string)
+		if wasFound {
+			user.EmailAddress = email
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return user, nil
+}
+
+func ParseFloatCustomField(buffer bytes.Buffer, customField string) (float64, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return 0, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return 0, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	switch value := customFields[customField].(type) {
+
+	case float64:
+		return value, nil
+	case nil:
+		return 0, nil
+	default:
+		return 0, ErrNoMultiSelectTypeError
+	}
+
+	return 0, err
+}
+
+func ParseLabelCustomField(buffer bytes.Buffer, customField string) ([]string, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var labels []string
+	switch value := customFields[customField].(type) {
+	case []interface{}:
+		for _, label := range value {
+			labels = append(labels, label.(string))
+		}
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return labels, err
+}
+
+func ParseSprintCustomField(buffer bytes.Buffer, customField string) ([]*SprintDetailScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	var records []*SprintDetailScheme
+
+	switch options := customFields[customField].(type) {
+	case []interface{}:
+
+		for _, option := range options {
+
+			record := &SprintDetailScheme{
+				Name:          option.(map[string]interface{})["name"].(string),
+				State:         option.(map[string]interface{})["state"].(string),
+				ID:            int(option.(map[string]interface{})["id"].(float64)),
+				StartDate:     option.(map[string]interface{})["startDate"].(string),
+				EndDate:       option.(map[string]interface{})["endDate"].(string),
+				OriginBoardID: int(option.(map[string]interface{})["boardId"].(float64)),
+				Goal:          option.(map[string]interface{})["goal"].(string),
+			}
+
+			completeDate, wasFound := option.(map[string]interface{})["completeDate"].(string)
+			if wasFound {
+				record.CompleteDate = completeDate
+			}
+			records = append(records, record)
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return records, nil
+}
+
+func ParseSelectCustomField(buffer bytes.Buffer, customField string) (*CustomFieldContextOptionScheme, error) {
+
+	raw, err := marshmallow.Unmarshal(buffer.Bytes(), &struct{}{})
+	if err != nil {
+		return nil, ErrNoCustomFieldUnmarshalError
+	}
+
+	fields, containsFields := raw["fields"]
+	if !containsFields {
+		return nil, ErrNoFieldInformationError
+	}
+
+	customFields := fields.(map[string]interface{})
+	cascading := &CustomFieldContextOptionScheme{}
+
+	switch option := customFields[customField].(type) {
+	case map[string]interface{}:
+
+		cascading = &CustomFieldContextOptionScheme{
+			ID:    option["id"].(string),
+			Value: option["value"].(string),
+		}
+
+	case nil:
+		return nil, nil
+	default:
+		return nil, ErrNoMultiSelectTypeError
+	}
+
+	return cascading, nil
+}
diff --git a/pkg/infra/models/jira_issue_operations.go b/pkg/infra/models/jira_issue_operations.go
index b4082f7..ca8663b 100644
--- a/pkg/infra/models/jira_issue_operations.go
+++ b/pkg/infra/models/jira_issue_operations.go
@@ -1,13 +1,11 @@
 package models
 
-import "fmt"
-
 type UpdateOperations struct{ Fields []map[string]interface{} }
 
-func (u *UpdateOperations) AddArrayOperation(customFieldID string, mapping map[string]string) (err error) {
+func (u *UpdateOperations) AddArrayOperation(customFieldID string, mapping map[string]string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	var operations []map[string]interface{}
@@ -26,25 +24,24 @@ func (u *UpdateOperations) AddArrayOperation(customFieldID string, mapping map[s
 	updateNode["update"] = fieldNode
 
 	u.Fields = append(u.Fields, updateNode)
-	return
+	return nil
 }
 
-func (u *UpdateOperations) AddStringOperation(customFieldID, operation, value string) (err error) {
+func (u *UpdateOperations) AddStringOperation(customFieldID, operation, value string) error {
 
 	if len(customFieldID) == 0 {
-		return fmt.Errorf("error, please provide a valid customFieldID value")
+		return ErrNoFieldIDError
 	}
 
 	if len(operation) == 0 {
-		return fmt.Errorf("error, please provide a valid operation value")
+		return ErrNoEditOperatorError
 	}
 
 	if len(value) == 0 {
-		return fmt.Errorf("error, please provide a valid value value")
+		return ErrNoEditValueError
 	}
 
 	var operations []map[string]interface{}
-
 	var operationNode = map[string]interface{}{}
 	operationNode[operation] = value
 
@@ -58,5 +55,5 @@ func (u *UpdateOperations) AddStringOperation(customFieldID, operation, value st
 
 	u.Fields = append(u.Fields, updateNode)
 
-	return
+	return nil
 }
diff --git a/pkg/infra/models/jira_issue_v2.go b/pkg/infra/models/jira_issue_v2.go
index f0b2219..967c76f 100644
--- a/pkg/infra/models/jira_issue_v2.go
+++ b/pkg/infra/models/jira_issue_v2.go
@@ -2,7 +2,6 @@ package models
 
 import (
 	"encoding/json"
-	"fmt"
 	"github.com/imdario/mergo"
 )
 
@@ -15,63 +14,74 @@ type IssueSchemeV2 struct {
 	Fields      *IssueFieldsSchemeV2     `json:"fields,omitempty"`
 }
 
-func (i *IssueSchemeV2) MergeCustomFields(fields *CustomFields) (result map[string]interface{}, err error) {
+func (i *IssueSchemeV2) MergeCustomFields(fields *CustomFields) (map[string]interface{}, error) {
 
-	if fields == nil {
-		return nil, fmt.Errorf("error, please provide a value *CustomFields pointer")
-	}
-
-	if len(fields.Fields) == 0 {
-		return nil, fmt.Errorf("error!, the Fields tag does not contains custom fields")
+	if fields == nil || len(fields.Fields) == 0 {
+		return nil, ErrNoCustomFieldError
 	}
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
 	//For each customField created, merge it into the eAsMap
 	for _, customField := range fields.Fields {
-		_ = mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride)
+		if err := mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride); err != nil {
+			return nil, err
+		}
 	}
 
 	return issueSchemeAsMap, nil
 }
 
-func (i *IssueSchemeV2) MergeOperations(operations *UpdateOperations) (result map[string]interface{}, err error) {
-
-	if operations == nil {
-		return nil, fmt.Errorf("error, please provide a value *UpdateOperations pointer")
-	}
+func (i *IssueSchemeV2) MergeOperations(operations *UpdateOperations) (map[string]interface{}, error) {
 
-	if len(operations.Fields) == 0 {
-		return nil, fmt.Errorf("error!, the Fields tag does not contains custom fields")
+	if operations == nil || len(operations.Fields) == 0 {
+		return nil, ErrNoOperatorError
 	}
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
 	//For each customField created, merge it into the eAsMap
 	for _, customField := range operations.Fields {
-		_ = mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride)
+		if err := mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride); err != nil {
+			return nil, err
+		}
 	}
 
 	return issueSchemeAsMap, nil
 }
 
-func (i *IssueSchemeV2) ToMap() (result map[string]interface{}, err error) {
+func (i *IssueSchemeV2) ToMap() (map[string]interface{}, error) {
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
-	return issueSchemeAsMap, err
+	return issueSchemeAsMap, nil
 }
 
 type IssueFieldsSchemeV2 struct {
diff --git a/pkg/infra/models/jira_issue_v3.go b/pkg/infra/models/jira_issue_v3.go
index d88de17..b15c9e2 100644
--- a/pkg/infra/models/jira_issue_v3.go
+++ b/pkg/infra/models/jira_issue_v3.go
@@ -2,7 +2,6 @@ package models
 
 import (
 	"encoding/json"
-	"fmt"
 	"github.com/imdario/mergo"
 )
 
@@ -15,63 +14,74 @@ type IssueScheme struct {
 	Fields      *IssueFieldsScheme       `json:"fields,omitempty"`
 }
 
-func (i *IssueScheme) MergeCustomFields(fields *CustomFields) (result map[string]interface{}, err error) {
+func (i *IssueScheme) MergeCustomFields(fields *CustomFields) (map[string]interface{}, error) {
 
-	if fields == nil {
-		return nil, fmt.Errorf("error, please provide a value *CustomFields pointer")
-	}
-
-	if len(fields.Fields) == 0 {
-		return nil, fmt.Errorf("error!, the Fields tag does not contains custom fields")
+	if fields == nil || len(fields.Fields) == 0 {
+		return nil, ErrNoCustomFieldError
 	}
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
 	//For each customField created, merge it into the eAsMap
 	for _, customField := range fields.Fields {
-		_ = mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride)
+		if err := mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride); err != nil {
+			return nil, err
+		}
 	}
 
 	return issueSchemeAsMap, nil
 }
 
-func (i *IssueScheme) MergeOperations(operations *UpdateOperations) (result map[string]interface{}, err error) {
-
-	if operations == nil {
-		return nil, fmt.Errorf("error, please provide a value *UpdateOperations pointer")
-	}
+func (i *IssueScheme) MergeOperations(operations *UpdateOperations) (map[string]interface{}, error) {
 
-	if len(operations.Fields) == 0 {
-		return nil, fmt.Errorf("error!, the Fields tag does not contains custom fields")
+	if operations == nil || len(operations.Fields) == 0 {
+		return nil, ErrNoOperatorError
 	}
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
 	//For each customField created, merge it into the eAsMap
 	for _, customField := range operations.Fields {
-		_ = mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride)
+		if err := mergo.Merge(&issueSchemeAsMap, customField, mergo.WithOverride); err != nil {
+			return nil, err
+		}
 	}
 
 	return issueSchemeAsMap, nil
 }
 
-func (i *IssueScheme) ToMap() (result map[string]interface{}, err error) {
+func (i *IssueScheme) ToMap() (map[string]interface{}, error) {
 
 	//Convert the IssueScheme struct to map[string]interface{}
-	issueSchemeAsBytes, _ := json.Marshal(i)
+	issueSchemeAsBytes, err := json.Marshal(i)
+	if err != nil {
+		return nil, err
+	}
 
 	issueSchemeAsMap := make(map[string]interface{})
-	_ = json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap)
+	if err := json.Unmarshal(issueSchemeAsBytes, &issueSchemeAsMap); err != nil {
+		return nil, err
+	}
 
-	return issueSchemeAsMap, err
+	return issueSchemeAsMap, nil
 }
 
 type IssueFieldsScheme struct {
diff --git a/pkg/infra/models/jira_user.go b/pkg/infra/models/jira_user.go
index 93827ba..0aeacbc 100644
--- a/pkg/infra/models/jira_user.go
+++ b/pkg/infra/models/jira_user.go
@@ -52,10 +52,3 @@ type UserSearchPageScheme struct {
 	IsLast     bool          `json:"isLast,omitempty"`
 	Values     []*UserScheme `json:"values,omitempty"`
 }
-
-type UserPermissionCheckParamsScheme struct {
-	Query      string
-	AccountID  string
-	IssueKey   string
-	ProjectKey string
-}
diff --git a/pkg/infra/models/jira_version.go b/pkg/infra/models/jira_version.go
index 48df004..f16039b 100644
--- a/pkg/infra/models/jira_version.go
+++ b/pkg/infra/models/jira_version.go
@@ -76,3 +76,13 @@ type VersionUnresolvedIssuesCountScheme struct {
 	IssuesUnresolvedCount int    `json:"issuesUnresolvedCount"`
 	IssuesCount           int    `json:"issuesCount"`
 }
+
+type VersionDetailScheme struct {
+	Self        string `json:"self,omitempty"`
+	ID          string `json:"id,omitempty"`
+	Description string `json:"description,omitempty"`
+	Name        string `json:"name,omitempty"`
+	Archived    bool   `json:"archived,omitempty"`
+	Released    bool   `json:"released,omitempty"`
+	ReleaseDate string `json:"releaseDate,omitempty"`
+}
diff --git a/pkg/infra/models/sm_request_field.go b/pkg/infra/models/sm_request_field.go
index 77995eb..f0cebdc 100644
--- a/pkg/infra/models/sm_request_field.go
+++ b/pkg/infra/models/sm_request_field.go
@@ -15,12 +15,8 @@ type CreateCustomerRequestPayloadScheme struct {
 
 func (c *CreateCustomerRequestPayloadScheme) MergeFields(fields *CustomerRequestFields) (map[string]interface{}, error) {
 
-	if fields == nil {
-		return nil, fmt.Errorf("error, please provide a value *CustomFields pointer")
-	}
-
-	if len(fields.Fields) == 0 {
-		return nil, fmt.Errorf("error!, the Fields tag does not contains custom fields")
+	if fields == nil || len(fields.Fields) == 0 {
+		return nil, ErrNoCustomFieldError
 	}
 
 	//Convert the IssueScheme struct to map[string]interface{}
diff --git a/service/jira/user.go b/service/jira/user.go
index c9eccdf..9e992a8 100644
--- a/service/jira/user.go
+++ b/service/jira/user.go
@@ -88,17 +88,4 @@ type UserSearchConnector interface {
 	//
 	// https://docs.go-atlassian.io/jira-software-cloud/users/search#find-users
 	Do(ctx context.Context, accountId, query string, startAt, maxResults int) ([]*model.UserScheme, *model.ResponseScheme, error)
-
-	// Check returns a list of users who fulfill these criteria:
-	//
-	// 1. their user attributes match a search string.
-	// 2. they have a set of permissions for a project or issue.
-	//
-	//
-	// If no search string is provided, a list of all users with the permissions is returned.
-	//
-	// GET /rest/api/{2-3}/user/permission/search
-	//
-	// https://docs.go-atlassian.io/jira-software-cloud/users/search#find-users-with-permissions
-	Check(ctx context.Context, permission string, options *model.UserPermissionCheckParamsScheme, startAt, maxResults int) ([]*model.UserScheme, *model.ResponseScheme, error)
 }


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0ee3230..dc9d392 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,9 +11,9 @@ jobs:
 
   test:
     strategy:
-      max-parallel: 9
+      max-parallel: 3
       matrix:
-        go-version: [1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x]
+        go-version: [1.16.x, 1.17.x, 1.18.x]
         platform: [ubuntu-latest, macos-latest, windows-latest]
 
     runs-on: ${{ matrix.platform }}
diff --git a/jira/internal/user_search_impl_test.go b/jira/internal/user_search_impl_test.go
index 01a60e3..07704a9 100644
--- a/jira/internal/user_search_impl_test.go
+++ b/jira/internal/user_search_impl_test.go
@@ -314,178 +314,3 @@ func Test_internalUserSearchImpl_Do(t *testing.T) {
 		})
 	}
 }
-
-func Test_internalUserSearchImpl_Check(t *testing.T) {
-
-	type fields struct {
-		c       service.Client
-		version string
-	}
-
-	type args struct {
-		ctx                 context.Context
-		permission          string
-		options             *model.UserPermissionCheckParamsScheme
-		startAt, maxResults int
-	}
-
-	testCases := []struct {
-		name    string
-		fields  fields
-		args    args
-		on      func(*fields)
-		wantErr bool
-		Err     error
-	}{
-		{
-			name:   "when the api version is v3",
-			fields: fields{version: "3"},
-			args: args{
-				ctx:        context.TODO(),
-				permission: "CREATE_ISSUES",
-				options: &model.UserPermissionCheckParamsScheme{
-					Query:      "project A",
-					AccountID:  "uuid-sample",
-					IssueKey:   "DFUMM-1",
-					ProjectKey: "DUMMY",
-				},
-				startAt:    100,
-				maxResults: 50,
-			},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/3/user/permission/search?accountId=uuid-sample&issueKey=DFUMM-1&maxResults=50&projectKey=DUMMY&query=project+A&startAt=100",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					mock.Anything).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			wantErr: false,
-			Err:     nil,
-		},
-
-		{
-			name:   "when the permission grant is not provided",
-			fields: fields{version: "3"},
-			args: args{
-				ctx: context.TODO(),
-			},
-			on: func(fields *fields) {
-				client := mocks.NewClient(t)
-				fields.c = client
-			},
-			wantErr: true,
-			Err:     model.ErrNoPermissionGrantIDError,
-		},
-
-		{
-			name:   "when the api version is v2",
-			fields: fields{version: "2"},
-			args: args{
-				ctx:        context.TODO(),
-				permission: "CREATE_ISSUES",
-				options: &model.UserPermissionCheckParamsScheme{
-					Query:      "project A",
-					AccountID:  "uuid-sample",
-					IssueKey:   "DFUMM-1",
-					ProjectKey: "DUMMY",
-				},
-				startAt:    100,
-				maxResults: 50,
-			},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/2/user/permission/search?accountId=uuid-sample&issueKey=DFUMM-1&maxResults=50&projectKey=DUMMY&query=project+A&startAt=100",
-					nil).
-					Return(&http.Request{}, nil)
-
-				client.On("Call",
-					&http.Request{},
-					mock.Anything).
-					Return(&model.ResponseScheme{}, nil)
-
-				fields.c = client
-			},
-			wantErr: false,
-			Err:     nil,
-		},
-
-		{
-			name:   "when the http request cannot be created",
-			fields: fields{version: "3"},
-			args: args{
-				ctx:        context.TODO(),
-				permission: "CREATE_ISSUES",
-				options: &model.UserPermissionCheckParamsScheme{
-					Query:      "project A",
-					AccountID:  "uuid-sample",
-					IssueKey:   "DFUMM-1",
-					ProjectKey: "DUMMY",
-				},
-				startAt:    100,
-				maxResults: 50,
-			},
-			on: func(fields *fields) {
-
-				client := mocks.NewClient(t)
-
-				client.On("NewRequest",
-					context.Background(),
-					http.MethodGet,
-					"rest/api/3/user/permission/search?accountId=uuid-sample&issueKey=DFUMM-1&maxResults=50&projectKey=DUMMY&query=project+A&startAt=100",
-					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, err := NewUserSearchService(testCase.fields.c, testCase.fields.version)
-			assert.NoError(t, err)
-
-			gotResult, gotResponse, err := newService.Check(testCase.args.ctx, testCase.args.permission, testCase.args.options,
-				testCase.args.startAt, testCase.args.maxResults)
-
-			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/pkg/infra/models/jira_customFields_test.go b/pkg/infra/models/jira_customFields_test.go
new file mode 100644
index 0000000..06a78f0
--- /dev/null
+++ b/pkg/infra/models/jira_customFields_test.go
@@ -0,0 +1,969 @@
+package models
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+	"time"
+)
+
+func TestCustomFields_Cascading(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		parent        string
+		child         string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				parent:        "America",
+				child:         "US",
+			},
+			wantErr: false,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				parent:        "America",
+				child:         "US",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the parent value is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				parent:        "",
+				child:         "US",
+			},
+			wantErr: true,
+			Err:     ErrNoCascadingParentError,
+		},
+
+		{
+			name:   "when the child value is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				parent:        "America",
+				child:         "",
+			},
+			wantErr: true,
+			Err:     ErrNoCascadingChildError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Cascading(testCase.args.customFieldID, testCase.args.parent, testCase.args.child)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_CheckBox(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		options       []string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				options:       []string{"Value"},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				options:       []string{"Value"},
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the options are not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				options:       nil,
+			},
+			wantErr: true,
+			Err:     ErrNoCheckBoxTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.CheckBox(testCase.args.customFieldID, testCase.args.options)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Date(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		dateTimeValue time.Time
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				dateTimeValue: time.Now().AddDate(0, -1, 0),
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				dateTimeValue: time.Now().AddDate(0, -1, 0),
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the date is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				dateTimeValue: time.Time{},
+			},
+			wantErr: true,
+			Err:     ErrNoDateTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Date(testCase.args.customFieldID, testCase.args.dateTimeValue)
+
+			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)
+			}
+
+		})
+	}
+}
+
+func TestCustomFields_DateTime(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		dateValue     time.Time
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				dateValue:     time.Now().AddDate(0, -1, 0),
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				dateValue:     time.Now().AddDate(0, -1, 0),
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the date-time is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				dateValue:     time.Time{},
+			},
+			wantErr: true,
+			Err:     ErrNoDateTimeTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.DateTime(testCase.args.customFieldID, testCase.args.dateValue)
+
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Group(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		group         string
+	}
+
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				group:         "jira-users",
+			},
+			wantErr: false,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				group:         "jira-users",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the group name is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_10001",
+				group:         "",
+			},
+			wantErr: true,
+			Err:     ErrNoGroupNameError,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Group(testCase.args.customFieldID, testCase.args.group)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Groups(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		groups        []string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				groups:        []string{"jira-users", "jira-admins"},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				groups:        []string{"jira-users", "jira-admins"},
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the groups names are not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				groups:        nil,
+			},
+			wantErr: true,
+			Err:     ErrNoGroupsNameError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Groups(testCase.args.customFieldID, testCase.args.groups)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_MultiSelect(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		options       []string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				options:       []string{"options"},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				options:       []string{"options"},
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the options are not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				options:       nil,
+			},
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.MultiSelect(testCase.args.customFieldID, testCase.args.options)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Number(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		numberValue   float64
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				numberValue:   0,
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Number(testCase.args.customFieldID, testCase.args.numberValue)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_RadioButton(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		button        string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				button:        "Button 1 ",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				button:        "Button 1 ",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the option is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				button:        "",
+			},
+			wantErr: true,
+			Err:     ErrNoButtonTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.RadioButton(testCase.args.customFieldID, testCase.args.button)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Select(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		option        string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				option:        "Option 1",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the customfield is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				option:        "Option 1",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the option is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				option:        "",
+			},
+			wantErr: true,
+			Err:     ErrNoSelectTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Select(testCase.args.customFieldID, testCase.args.option)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Text(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		textValue     string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				textValue:     "Application",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				textValue:     "Application",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the value is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				textValue:     "",
+			},
+			wantErr: true,
+			Err:     ErrNoTextTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Text(testCase.args.customFieldID, testCase.args.textValue)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_URL(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		URL           string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				URL:           "https://www.google.com/",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				URL:           "https://www.google.com/",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the url is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				URL:           "",
+			},
+			wantErr: true,
+			Err:     ErrNoUrlTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.URL(testCase.args.customFieldID, testCase.args.URL)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_User(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		accountID     string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				accountID:     "uuid-sample",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				accountID:     "uuid-sample",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the user is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				accountID:     "",
+			},
+			wantErr: true,
+			Err:     ErrNoUserTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.User(testCase.args.customFieldID, testCase.args.accountID)
+			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)
+			}
+		})
+	}
+}
+
+func TestCustomFields_Users(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		accountIDs    []string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				accountIDs:    []string{"user-1", "user-2"},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				accountIDs:    []string{"user-1", "user-2"},
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+		{
+			name:   "when the users are not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "customfield_1000",
+				accountIDs:    nil,
+			},
+			wantErr: true,
+			Err:     ErrNoMultiUserTypeError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			c := &CustomFields{
+				Fields: testCase.fields.Fields,
+			}
+
+			err := c.Users(testCase.args.customFieldID, testCase.args.accountIDs)
+			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)
+			}
+		})
+	}
+}
diff --git a/pkg/infra/models/jira_issue_custom_fields_test.go b/pkg/infra/models/jira_issue_custom_fields_test.go
new file mode 100644
index 0000000..596d238
--- /dev/null
+++ b/pkg/infra/models/jira_issue_custom_fields_test.go
@@ -0,0 +1,1744 @@
+package models
+
+import (
+	"bytes"
+	"reflect"
+	"testing"
+)
+
+func TestParseMultiSelectField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10044",
+        "value": "Option 1",
+        "id": "10044",
+		"optionId": "12222",
+		"disabled": true
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10046",
+        "value": "Option 3",
+        "id": "10046"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10047",
+        "value": "Option 4",
+        "id": "10047"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10044",
+        "value": "Option 1",
+        "id": "10044"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10046",
+        "value": "Option 3",
+        "id": "10046"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10046": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10046": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*CustomFieldContextOptionScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10046",
+			},
+			want: []*CustomFieldContextOptionScheme{
+				{
+					ID:       "10044",
+					Value:    "Option 1",
+					OptionID: "12222",
+					Disabled: true,
+				},
+				{
+					ID:    "10046",
+					Value: "Option 3",
+				},
+				{
+					ID:    "10047",
+					Value: "Option 4",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseMultiSelectCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseMultiGroupPickerField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10052": [
+      {
+        "name": "jira-administrators",
+        "groupId": "1da6f895-2b42-423b-8bfb-1e09ee8d7764",
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=1da6f895-2b42-423b-8bfb-1e09ee8d7764"
+      },
+      {
+        "name": "jira-administrators-system",
+        "groupId": "be9ba0ab-ecdc-445b-9ce6-b95202026c1a",
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=be9ba0ab-ecdc-445b-9ce6-b95202026c1a"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10052": [
+      {
+        "name": "jira-administrators",
+        "groupId": "1da6f895-2b42-423b-8bfb-1e09ee8d7764",
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=1da6f895-2b42-423b-8bfb-1e09ee8d7764"
+      },
+      {
+        "name": "jira-administrators-system",
+        "groupId": "be9ba0ab-ecdc-445b-9ce6-b95202026c1a",
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=be9ba0ab-ecdc-445b-9ce6-b95202026c1a"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10052": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10052": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*GroupDetailScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10052",
+			},
+			want: []*GroupDetailScheme{
+				{
+					Self:    "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=1da6f895-2b42-423b-8bfb-1e09ee8d7764",
+					Name:    "jira-administrators",
+					GroupID: "1da6f895-2b42-423b-8bfb-1e09ee8d7764",
+				},
+				{
+					Self:    "https://ctreminiom.atlassian.net/rest/api/3/group?groupId=be9ba0ab-ecdc-445b-9ce6-b95202026c1a",
+					Name:    "jira-administrators-system",
+					GroupID: "be9ba0ab-ecdc-445b-9ce6-b95202026c1a",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10052",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10052",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10052",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10052",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseMultiGroupPickerCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseMultiUserPickerField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10055": [
+     {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6a63157ed50cd2b9eaca",
+        "accountId": "5e5f6a63157ed50cd2b9eaca",
+        "avatarUrls": {
+          "48x48": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "24x24": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "16x16": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "32x32": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png"
+        },
+        "displayName": "Carlos Treminio",
+        "active": true,
+        "timeZone": "Asia/Dhaka",
+        "accountType": "atlassian"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5b86be50b8e3cb5895860d6d",
+        "accountId": "5b86be50b8e3cb5895860d6d",
+        "emailAddress": "ctreminiom079@gmail.com",
+        "avatarUrls": {
+          "48x48": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "24x24": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "16x16": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "32x32": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png"
+        },
+        "displayName": "Carlos Treminio",
+        "active": true,
+        "timeZone": "America/Guatemala",
+        "accountType": "atlassian"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=557058%3Ad6b5955a-e193-41e1-b051-79cdb0755d68",
+        "accountId": "557058:d6b5955a-e193-41e1-b051-79cdb0755d68",
+        "avatarUrls": {
+          "48x48": "https://secure.gravatar.com/avatar/53e3e37950768a905d53cebdfcbd63e3?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-1.png",
+          "24x24": "https://secure.gravatar.com/avatar/53e3e37950768a905d53cebdfcbd63e3?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-1.png",
+          "16x16": "https://secure.gravatar.com/avatar/53e3e37950768a905d53cebdfcbd63e3?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-1.png",
+          "32x32": "https://secure.gravatar.com/avatar/53e3e37950768a905d53cebdfcbd63e3?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FT-1.png"
+        },
+        "displayName": "Trello",
+        "active": true,
+        "timeZone": "Europe/London",
+        "accountType": "app"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10055": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6a63157ed50cd2b9eaca",
+        "accountId": "5e5f6a63157ed50cd2b9eaca",
+        "avatarUrls": {
+          "48x48": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "24x24": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "16x16": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png",
+          "32x32": "https://secure.gravatar.com/avatar/2e6d2ee8550c63137e196a2890bc25a9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-4.png"
+        },
+        "displayName": "Carlos Treminio",
+        "active": true,
+        "timeZone": "Asia/Dhaka",
+        "accountType": "atlassian"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5b86be50b8e3cb5895860d6d",
+        "accountId": "5b86be50b8e3cb5895860d6d",
+        "emailAddress": "ctreminiom079@gmail.com",
+        "avatarUrls": {
+          "48x48": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "24x24": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "16x16": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png",
+          "32x32": "https://secure.gravatar.com/avatar/b830f79c6cc32dcbcb9842f98cd3d3cd?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FCT-6.png"
+        },
+        "displayName": "Carlos Treminio",
+        "active": true,
+        "timeZone": "America/Guatemala",
+        "accountType": "atlassian"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10055": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10055": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*UserDetailScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10055",
+			},
+			want: []*UserDetailScheme{
+				{
+					Self:        "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6a63157ed50cd2b9eaca",
+					AccountID:   "5e5f6a63157ed50cd2b9eaca",
+					DisplayName: "Carlos Treminio",
+					Active:      true,
+					TimeZone:    "Asia/Dhaka",
+					AccountType: "atlassian",
+				},
+				{
+					Self:         "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5b86be50b8e3cb5895860d6d",
+					AccountID:    "5b86be50b8e3cb5895860d6d",
+					EmailAddress: "ctreminiom079@gmail.com",
+					DisplayName:  "Carlos Treminio",
+					Active:       true,
+					TimeZone:     "America/Guatemala",
+					AccountType:  "atlassian",
+				},
+				{
+					Self:        "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=557058%3Ad6b5955a-e193-41e1-b051-79cdb0755d68",
+					AccountID:   "557058:d6b5955a-e193-41e1-b051-79cdb0755d68",
+					DisplayName: "Trello",
+					Active:      true,
+					TimeZone:    "Europe/London",
+					AccountType: "app",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10055",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10055",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10055",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10055",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseMultiUserPickerCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseCascadingSelectField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+  "fields": {
+    "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10054",
+      "value": "America",
+      "id": "10054",
+      "child": {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10057",
+        "value": "Costa Rica",
+        "id": "10057"
+      }
+    }
+  }
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+  "fields_no_mapped": {
+    "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10054",
+      "value": "America",
+      "id": "10054",
+      "child": {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10057",
+        "value": "Costa Rica",
+        "id": "10057"
+      }
+    }
+  }
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10045": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10045": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    *CascadingSelectScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10045",
+			},
+			want: &CascadingSelectScheme{
+				Self:  "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10054",
+				Value: "America",
+				Id:    "10054",
+				Child: &CascadingSelectChildScheme{
+					Self:  "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10057",
+					Value: "Costa Rica",
+					Id:    "10057",
+				},
+			},
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseCascadingSelectCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseMultiCheckboxesCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10037",
+        "value": "Option 2",
+        "id": "10037"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10039",
+        "value": "Options 4",
+        "id": "10039"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10037",
+        "value": "Option 2",
+        "id": "10037"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10039",
+        "value": "Options 4",
+        "id": "10039"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10046": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10046": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*CustomFieldContextOptionScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10046",
+			},
+			want: []*CustomFieldContextOptionScheme{
+				{
+					ID:    "10037",
+					Value: "Option 2",
+				},
+				{
+					ID:    "10039",
+					Value: "Options 4",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseMultiCheckboxesCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseMultiVersionCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/version/10000",
+        "id": "10000",
+        "description": "",
+        "name": "Version 00",
+        "archived": false,
+        "released": false,
+        "releaseDate": "2021-02-23"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/version/10002",
+        "id": "10002",
+        "description": "Version Sandbox description - UPDATED",
+        "name": "Version Sandbox - UPDATED",
+        "archived": false,
+        "released": true,
+        "releaseDate": "2021-03-06"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10046": [
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/version/10000",
+        "id": "10000",
+        "description": "",
+        "name": "Version 00",
+        "archived": false,
+        "released": false,
+        "releaseDate": "2021-02-23"
+      },
+      {
+        "self": "https://ctreminiom.atlassian.net/rest/api/3/version/10002",
+        "id": "10002",
+        "description": "Version Sandbox description - UPDATED",
+        "name": "Version Sandbox - UPDATED",
+        "archived": false,
+        "released": true,
+        "releaseDate": "2021-03-06"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10046": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10046": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*VersionDetailScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10046",
+			},
+			want: []*VersionDetailScheme{
+				{
+					Self:        "https://ctreminiom.atlassian.net/rest/api/3/version/10000",
+					ID:          "10000",
+					Description: "",
+					Name:        "Version 00",
+					Archived:    false,
+					Released:    false,
+					ReleaseDate: "2021-02-23",
+				},
+				{
+					Self:        "https://ctreminiom.atlassian.net/rest/api/3/version/10002",
+					ID:          "10002",
+					Description: "Version Sandbox description - UPDATED",
+					Name:        "Version Sandbox - UPDATED",
+					Archived:    false,
+					Released:    true,
+					ReleaseDate: "2021-03-06",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseMultiVersionCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseUserPickerCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+  "fields": {
+    "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6acefc1fca0af44135f8",
+      "accountId": "5e5f6acefc1fca0af44135f8",
+      "avatarUrls": {
+        "48x48": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "24x24": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "16x16": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "32x32": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png"
+      },
+      "displayName": "Eduardo Navarro",
+      "active": true,
+      "timeZone": "Europe/London",
+      "accountType": "atlassian"
+    }
+  }
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+  "fields_no_mapped": {
+    "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6acefc1fca0af44135f8",
+      "accountId": "5e5f6acefc1fca0af44135f8",
+      "avatarUrls": {
+        "48x48": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "24x24": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "16x16": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png",
+        "32x32": "https://secure.gravatar.com/avatar/6c20a29c5ab36b3cbc121782edaadfc9?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEN-4.png"
+      },
+      "displayName": "Eduardo Navarro",
+      "active": true,
+      "timeZone": "Europe/London",
+      "accountType": "atlassian"
+    }
+  }
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10045": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10045": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    *UserDetailScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10045",
+			},
+			want: &UserDetailScheme{
+				Self:         "https://ctreminiom.atlassian.net/rest/api/3/user?accountId=5e5f6acefc1fca0af44135f8",
+				AccountID:    "5e5f6acefc1fca0af44135f8",
+				EmailAddress: "",
+				DisplayName:  "Eduardo Navarro",
+				Active:       true,
+				TimeZone:     "Europe/London",
+				AccountType:  "atlassian",
+			},
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseUserPickerCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseFloatCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+  "fields": {
+    "customfield_10045": 1000.323
+  }
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+  "fields_no_mapped": {
+    "customfield_10045": 1000.323
+  }
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10045": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10045": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    float64
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10045",
+			},
+			want:    1000.323,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10045",
+			},
+			want:    0,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10045",
+			},
+			want:    0,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10045",
+			},
+			want:    0,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10045",
+			},
+			want:    0,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseFloatCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseLabelCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+  "fields": {
+    "customfield_10045": [
+      "asd",
+      "asds"
+    ]
+  }
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+  "fields_no_mapped": {
+   "customfield_10045": [
+      "asd",
+      "asds"
+    ]
+  }
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10045": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10045": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []string
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10045",
+			},
+			want:    []string{"asd", "asds"},
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseLabelCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseSprintCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+	"fields": {
+		"customfield_10046": [
+      {
+        "id": 4,
+        "name": "KP Sprint 3",
+        "state": "active",
+        "boardId": 4,
+        "goal": "",
+        "startDate": "2023-03-04T02:03:16.273Z",
+        "endDate": "2023-03-17T02:03:00.000Z"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+	"field_no_mapped": {
+		"customfield_10046": [
+      {
+        "id": 4,
+        "name": "KP Sprint 3",
+        "state": "active",
+        "boardId": 4,
+        "goal": "",
+        "startDate": "2023-03-04T02:03:16.273Z",
+        "endDate": "2023-03-17T02:03:00.000Z"
+      }
+    ]
+	}
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10046": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10046": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    []*SprintDetailScheme
+		want1   bool
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10046",
+			},
+			want: []*SprintDetailScheme{
+				{
+					ID:            4,
+					State:         "active",
+					Name:          "KP Sprint 3",
+					StartDate:     "2023-03-04T02:03:16.273Z",
+					EndDate:       "2023-03-17T02:03:00.000Z",
+					OriginBoardID: 4,
+					Goal:          "",
+				},
+			},
+			want1:   true,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10046",
+			},
+			want:    nil,
+			want1:   false,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseSprintCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseMultiSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseMultiSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseMultiSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestParseSelectCustomField(t *testing.T) {
+
+	bufferMocked := bytes.Buffer{}
+	bufferMocked.WriteString(`
+{
+  "fields": {
+    "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10058",
+      "value": "Scranton 1",
+      "id": "10058"
+    }
+  }
+}`)
+
+	bufferMockedWithNoFields := bytes.Buffer{}
+	bufferMockedWithNoFields.WriteString(`
+{
+  "fields_no_mapped": {
+   "customfield_10045": {
+      "self": "https://ctreminiom.atlassian.net/rest/api/3/customFieldOption/10058",
+      "value": "Scranton 1",
+      "id": "10058"
+    }
+  }
+}`)
+
+	bufferMockedWithNoJSON := bytes.Buffer{}
+	bufferMockedWithNoJSON.WriteString(`{}{`)
+
+	bufferMockedWithNoInfo := bytes.Buffer{}
+	bufferMockedWithNoInfo.WriteString(`
+{
+	"fields": {
+		"customfield_10045": null
+	}
+}`)
+
+	bufferMockedWithInvalidType := bytes.Buffer{}
+	bufferMockedWithInvalidType.WriteString(`
+{
+	"fields": {
+		"customfield_10045": "Test field sample"
+	}
+}`)
+
+	type args struct {
+		buffer      bytes.Buffer
+		customField string
+	}
+
+	testCases := []struct {
+		name    string
+		args    args
+		want    *CustomFieldContextOptionScheme
+		wantErr bool
+		Err     error
+	}{
+		{
+			name: "when the buffer contains information",
+			args: args{
+				buffer:      bufferMocked,
+				customField: "customfield_10045",
+			},
+			want: &CustomFieldContextOptionScheme{
+				ID:    "10058",
+				Value: "Scranton 1",
+			},
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer no contains information",
+			args: args{
+				buffer:      bufferMockedWithNoInfo,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: false,
+		},
+
+		{
+			name: "when the buffer does not contains the fields object",
+			args: args{
+				buffer:      bufferMockedWithNoFields,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoFieldInformationError,
+		},
+
+		{
+			name: "when the buffer does not contains a valid field type",
+			args: args{
+				buffer:      bufferMockedWithInvalidType,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoMultiSelectTypeError,
+		},
+
+		{
+			name: "when the buffer cannot be parsed",
+			args: args{
+				buffer:      bufferMockedWithNoJSON,
+				customField: "customfield_10045",
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldUnmarshalError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			got, err := ParseSelectCustomField(testCase.args.buffer, testCase.args.customField)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("ParseSelectCustomField() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("ParseSelectCustomField() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("ParseSelectCustomField() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
diff --git a/pkg/infra/models/jira_issue_operations_test.go b/pkg/infra/models/jira_issue_operations_test.go
new file mode 100644
index 0000000..22cd330
--- /dev/null
+++ b/pkg/infra/models/jira_issue_operations_test.go
@@ -0,0 +1,143 @@
+package models
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestUpdateOperations_AddArrayOperation(t *testing.T) {
+
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		mapping       map[string]string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the values are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "custom_field_id",
+				mapping: map[string]string{
+					"value1": "verb"},
+			},
+			wantErr: false,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				mapping: map[string]string{
+					"value1": "verb"},
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			u := &UpdateOperations{
+				Fields: testCase.fields.Fields,
+			}
+			if err := u.AddArrayOperation(testCase.args.customFieldID, testCase.args.mapping); (err != nil) != testCase.wantErr {
+
+				if !reflect.DeepEqual(err, testCase.Err) {
+					t.Errorf("AddArrayOperation() got = (%v), want (%v)", err, testCase.Err)
+				}
+
+				t.Errorf("AddArrayOperation() error = %v, wantErr %v", err, testCase.wantErr)
+			}
+		})
+	}
+}
+
+func TestUpdateOperations_AddStringOperation(t *testing.T) {
+	type fields struct {
+		Fields []map[string]interface{}
+	}
+	type args struct {
+		customFieldID string
+		operation     string
+		value         string
+	}
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				customFieldID: "custom_field_id",
+				operation:     "operation_sample",
+				value:         "value_sample",
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-field is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "",
+				operation:     "operation_sample",
+				value:         "value_sample",
+			},
+			wantErr: true,
+			Err:     ErrNoFieldIDError,
+		},
+
+		{
+			name:   "when the operation is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "custom_field_id",
+				operation:     "",
+				value:         "value_sample",
+			},
+			wantErr: true,
+			Err:     ErrNoEditOperatorError,
+		},
+
+		{
+			name:   "when the operator value is not provided",
+			fields: fields{},
+			args: args{
+				customFieldID: "custom_field_id",
+				operation:     "operation_sample",
+				value:         "",
+			},
+			wantErr: true,
+			Err:     ErrNoEditValueError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			u := &UpdateOperations{
+				Fields: testCase.fields.Fields,
+			}
+			if err := u.AddStringOperation(testCase.args.customFieldID, testCase.args.operation, testCase.args.value); (err != nil) != testCase.wantErr {
+
+				if !reflect.DeepEqual(err, testCase.Err) {
+					t.Errorf("AddStringOperation() got = (%v), want (%v)", err, testCase.Err)
+				}
+
+				t.Errorf("AddStringOperation() error = %v, wantErr %v", err, testCase.wantErr)
+			}
+		})
+	}
+}
diff --git a/pkg/infra/models/jira_issue_v2_test.go b/pkg/infra/models/jira_issue_v2_test.go
new file mode 100644
index 0000000..aa019da
--- /dev/null
+++ b/pkg/infra/models/jira_issue_v2_test.go
@@ -0,0 +1,189 @@
+package models
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestIssueSchemeV2_MergeCustomFields(t *testing.T) {
+
+	var customFields = &CustomFields{}
+	customFields.Number("customfield_10043", 1000.3232)
+
+	type fields struct {
+		ID          string
+		Key         string
+		Self        string
+		Transitions []*IssueTransitionScheme
+		Changelog   *IssueChangelogScheme
+		Fields      *IssueFieldsSchemeV2
+	}
+
+	type args struct {
+		fields *CustomFields
+	}
+
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    map[string]interface{}
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				fields: customFields,
+			},
+			want: map[string]interface{}{
+				"fields": map[string]interface{}{
+					"customfield_10043": 1000.3232,
+				},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-fields are not provided",
+			fields: fields{},
+			args: args{
+				fields: nil,
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldError,
+		},
+
+		{
+			name:   "when the custom-field don't have information",
+			fields: fields{},
+			args: args{
+				fields: &CustomFields{},
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			i := &IssueSchemeV2{
+				ID:          testCase.fields.ID,
+				Key:         testCase.fields.Key,
+				Self:        testCase.fields.Self,
+				Transitions: testCase.fields.Transitions,
+				Changelog:   testCase.fields.Changelog,
+				Fields:      testCase.fields.Fields,
+			}
+			got, err := i.MergeCustomFields(testCase.args.fields)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("MergeCustomFields() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("MergeCustomFields() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("AddArrayOperation() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestIssueSchemeV2_MergeOperations(t *testing.T) {
+
+	var operations = &UpdateOperations{}
+	operations.AddArrayOperation("labels", map[string]string{"triaged": "remove"})
+
+	type fields struct {
+		ID          string
+		Key         string
+		Self        string
+		Transitions []*IssueTransitionScheme
+		Changelog   *IssueChangelogScheme
+		Fields      *IssueFieldsSchemeV2
+	}
+	type args struct {
+		operations *UpdateOperations
+	}
+
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    map[string]interface{}
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				operations: operations,
+			},
+			want: map[string]interface{}{
+				"update": map[string]interface{}{
+					"labels": []map[string]interface{}{
+						{
+							"remove": "triaged",
+						},
+					},
+				},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the operations are not provided",
+			fields: fields{},
+			args: args{
+				operations: nil,
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoOperatorError,
+		},
+
+		{
+			name:   "when the operations don't have information",
+			fields: fields{},
+			args: args{
+				operations: &UpdateOperations{},
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoOperatorError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+
+			i := &IssueSchemeV2{
+				ID:          testCase.fields.ID,
+				Key:         testCase.fields.Key,
+				Self:        testCase.fields.Self,
+				Transitions: testCase.fields.Transitions,
+				Changelog:   testCase.fields.Changelog,
+				Fields:      testCase.fields.Fields,
+			}
+			got, err := i.MergeOperations(testCase.args.operations)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("MergeOperations() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("MergeOperations() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("MergeOperations() got = (%v), want (%v)", err, testCase.Err)
+			}
+
+		})
+	}
+}
diff --git a/pkg/infra/models/jira_issue_v3_test.go b/pkg/infra/models/jira_issue_v3_test.go
new file mode 100644
index 0000000..cca9143
--- /dev/null
+++ b/pkg/infra/models/jira_issue_v3_test.go
@@ -0,0 +1,189 @@
+package models
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestIssueScheme_MergeCustomFields(t *testing.T) {
+
+	var customFields = &CustomFields{}
+	customFields.Number("customfield_10043", 1000.3232)
+
+	type fields struct {
+		ID          string
+		Key         string
+		Self        string
+		Transitions []*IssueTransitionScheme
+		Changelog   *IssueChangelogScheme
+		Fields      *IssueFieldsScheme
+	}
+
+	type args struct {
+		fields *CustomFields
+	}
+
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    map[string]interface{}
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				fields: customFields,
+			},
+			want: map[string]interface{}{
+				"fields": map[string]interface{}{
+					"customfield_10043": 1000.3232,
+				},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the custom-fields are not provided",
+			fields: fields{},
+			args: args{
+				fields: nil,
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldError,
+		},
+
+		{
+			name:   "when the custom-field don't have information",
+			fields: fields{},
+			args: args{
+				fields: &CustomFields{},
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoCustomFieldError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			i := &IssueScheme{
+				ID:          testCase.fields.ID,
+				Key:         testCase.fields.Key,
+				Self:        testCase.fields.Self,
+				Transitions: testCase.fields.Transitions,
+				Changelog:   testCase.fields.Changelog,
+				Fields:      testCase.fields.Fields,
+			}
+			got, err := i.MergeCustomFields(testCase.args.fields)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("MergeCustomFields() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("MergeCustomFields() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("AddArrayOperation() got = (%v), want (%v)", err, testCase.Err)
+			}
+		})
+	}
+}
+
+func TestIssueScheme_MergeOperations(t *testing.T) {
+
+	var operations = &UpdateOperations{}
+	operations.AddArrayOperation("labels", map[string]string{"triaged": "remove"})
+
+	type fields struct {
+		ID          string
+		Key         string
+		Self        string
+		Transitions []*IssueTransitionScheme
+		Changelog   *IssueChangelogScheme
+		Fields      *IssueFieldsScheme
+	}
+	type args struct {
+		operations *UpdateOperations
+	}
+
+	testCases := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    map[string]interface{}
+		wantErr bool
+		Err     error
+	}{
+		{
+			name:   "when the parameters are correct",
+			fields: fields{},
+			args: args{
+				operations: operations,
+			},
+			want: map[string]interface{}{
+				"update": map[string]interface{}{
+					"labels": []map[string]interface{}{
+						{
+							"remove": "triaged",
+						},
+					},
+				},
+			},
+			wantErr: false,
+			Err:     nil,
+		},
+
+		{
+			name:   "when the operations are not provided",
+			fields: fields{},
+			args: args{
+				operations: nil,
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoOperatorError,
+		},
+
+		{
+			name:   "when the operations don't have information",
+			fields: fields{},
+			args: args{
+				operations: &UpdateOperations{},
+			},
+			want:    nil,
+			wantErr: true,
+			Err:     ErrNoOperatorError,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+
+			i := &IssueScheme{
+				ID:          testCase.fields.ID,
+				Key:         testCase.fields.Key,
+				Self:        testCase.fields.Self,
+				Transitions: testCase.fields.Transitions,
+				Changelog:   testCase.fields.Changelog,
+				Fields:      testCase.fields.Fields,
+			}
+			got, err := i.MergeOperations(testCase.args.operations)
+			if (err != nil) != testCase.wantErr {
+				t.Errorf("MergeOperations() error = %v, wantErr %v", err, testCase.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, testCase.want) {
+				t.Errorf("MergeOperations() got = %v, want %v", got, testCase.want)
+			}
+
+			if !reflect.DeepEqual(err, testCase.Err) {
+				t.Errorf("MergeOperations() got = (%v), want (%v)", err, testCase.Err)
+			}
+
+		})
+	}
+}