diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19971101..cf57db64 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/.github/workflows/test.yml b/.github/workflows/test.yml index 0ee32300..dc9d3921 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/go.mod b/go.mod index 36fd3f4e..8929210d 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 1c6346ea..6b0106b2 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/pkg/infra/models/agile_sprint.go b/pkg/infra/models/agile_sprint.go index 5e82bf21..520a8000 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 eebc22b3..1791d972 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 dba2014a..a02d04c6 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_customFields_test.go b/pkg/infra/models/jira_customFields_test.go new file mode 100644 index 00000000..06a78f05 --- /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_field_context_option.go b/pkg/infra/models/jira_field_context_option.go index c2201b65..58a3425f 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 8ff5e56c..5f6a15b6 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 00000000..0da4fc42 --- /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_custom_fields_test.go b/pkg/infra/models/jira_issue_custom_fields_test.go new file mode 100644 index 00000000..596d2389 --- /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.go b/pkg/infra/models/jira_issue_operations.go index b4082f79..ca8663b7 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_operations_test.go b/pkg/infra/models/jira_issue_operations_test.go new file mode 100644 index 00000000..22cd330f --- /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.go b/pkg/infra/models/jira_issue_v2.go index f0b2219b..967c76fc 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_v2_test.go b/pkg/infra/models/jira_issue_v2_test.go new file mode 100644 index 00000000..aa019dac --- /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.go b/pkg/infra/models/jira_issue_v3.go index d88de17e..b15c9e28 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_issue_v3_test.go b/pkg/infra/models/jira_issue_v3_test.go new file mode 100644 index 00000000..cca91436 --- /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) + } + + }) + } +} diff --git a/pkg/infra/models/jira_version.go b/pkg/infra/models/jira_version.go index 48df004a..f16039b8 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 77995eba..f0cebdc2 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{}