Skip to content

Commit

Permalink
Issue #122 custom_fields support for tickets
Browse files Browse the repository at this point in the history
  • Loading branch information
daharon committed Nov 20, 2019
1 parent ad03611 commit d2f1492
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 24 deletions.
57 changes: 57 additions & 0 deletions fixture/GET/ticket_custom_field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"ticket": {
"url": "https://d3v-terraform-provider.zendesk.com/api/v2/tickets/4.json",
"id": 4,
"external_id": null,
"via": {
"channel": "web",
"source": {
"from": {},
"to": {},
"rel": null
}
},
"created_at": "2019-06-03T02:34:52Z",
"updated_at": "2019-06-03T02:35:05Z",
"type": null,
"subject": "Ticket containing a custom field",
"raw_subject": "Ticket containing a custom field",
"description": "Testing custom field value in a ticket.",
"priority": null,
"status": "solved",
"recipient": null,
"requester_id": 377922500012,
"submitter_id": 377922500012,
"assignee_id": 377922500012,
"organization_id": 360363695492,
"group_id": 360004077472,
"collaborator_ids": [],
"follower_ids": [],
"email_cc_ids": [],
"forum_topic_id": null,
"problem_id": null,
"has_incidents": false,
"is_public": true,
"due_at": null,
"tags": [],
"custom_fields": [
{
"id": 360005657120,
"value": "Custom field value for testing"
},
{
"id": 360005657121,
"value": ["list", "of", "values"]
}
],
"satisfaction_rating": null,
"sharing_agreement_ids": [],
"fields": [],
"followup_ids": [],
"ticket_form_id": 360000389592,
"brand_id": 360002256672,
"satisfaction_probability": null,
"allow_channelback": false,
"allow_attachments": true
}
}
86 changes: 62 additions & 24 deletions zendesk/ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,71 @@ import (
"time"
)

type CustomField struct {
ID int64 `json:"id"`
// Valid types are string or []string.
Value interface{} `json:"value"`
}

// Custom Unmarshal function required because a custom field's value can be
// a string or array of strings.
func (cf *CustomField) UnmarshalJSON(data []byte) error {
var temp map[string]interface{}
if err := json.Unmarshal(data, &temp); err != nil {
return err
}

cf.ID = int64(temp["id"].(float64))

switch v := temp["value"].(type) {
case string:
cf.Value = v
case []interface{}:
var list []string

for _, v := range temp["value"].([]interface{}) {
if s, ok := v.(string); ok {
list = append(list, s)
} else {
return fmt.Errorf("%T is an invalid type for custom field value", v)
}
}

cf.Value = list
default:
return fmt.Errorf("%T is an invalid type for custom field value", v)
}

return nil
}

type Ticket struct {
ID int64 `json:"id,omitempty"`
URL string `json:"url,omitempty"`
ExternalID string `json:"external_id,omitempty"`
Type string `json:"type,omitempty"`
Subject string `json:"subject,omitempty"`
RawSubject string `json:"raw_subject,omitempty"`
Description string `json:"description,omitempty"`
Priority string `json:"priority,omitempty"`
Status string `json:"status,omitempty"`
Recipient string `json:"recipient,omitempty"`
RequesterID int64 `json:"requester_id,omitempty"`
SubmitterID int64 `json:"submitter_id,omitempty"`
AssigneeID int64 `json:"assignee_id,omitempty"`
OrganizationID int64 `json:"organization_id,omitempty"`
GroupID int64 `json:"group_id,omitempty"`
CollaboratorIDs []int64 `json:"collaborator_ids,omitempty"`
FollowerIDs []int64 `json:"follower_ids,omitempty"`
EmailCCIDs []int64 `json:"email_cc_ids,omitempty"`
ForumTopicID int64 `json:"forum_topic_id,omitempty"`
ProblemID int64 `json:"problem_id,omitempty"`
HasIncidents bool `json:"has_incidents,omitempty"`
DueAt time.Time `json:"due_at,omitempty"`
Tags []string `json:"tags,omitempty"`
ID int64 `json:"id,omitempty"`
URL string `json:"url,omitempty"`
ExternalID string `json:"external_id,omitempty"`
Type string `json:"type,omitempty"`
Subject string `json:"subject,omitempty"`
RawSubject string `json:"raw_subject,omitempty"`
Description string `json:"description,omitempty"`
Priority string `json:"priority,omitempty"`
Status string `json:"status,omitempty"`
Recipient string `json:"recipient,omitempty"`
RequesterID int64 `json:"requester_id,omitempty"`
SubmitterID int64 `json:"submitter_id,omitempty"`
AssigneeID int64 `json:"assignee_id,omitempty"`
OrganizationID int64 `json:"organization_id,omitempty"`
GroupID int64 `json:"group_id,omitempty"`
CollaboratorIDs []int64 `json:"collaborator_ids,omitempty"`
FollowerIDs []int64 `json:"follower_ids,omitempty"`
EmailCCIDs []int64 `json:"email_cc_ids,omitempty"`
ForumTopicID int64 `json:"forum_topic_id,omitempty"`
ProblemID int64 `json:"problem_id,omitempty"`
HasIncidents bool `json:"has_incidents,omitempty"`
DueAt time.Time `json:"due_at,omitempty"`
Tags []string `json:"tags,omitempty"`
CustomFields []CustomField `json:"custom_fields,omitempty"`

// TODO: Via #123
// TODO: CustomFields #122

SatisfactionRating struct {
ID int64 `json:"id"`
Expand Down
64 changes: 64 additions & 0 deletions zendesk/ticket_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package zendesk

import (
"encoding/json"
"net/http"
"sort"
"testing"
)

Expand Down Expand Up @@ -44,6 +46,68 @@ func TestGetTicket(t *testing.T) {
}
}

// Test the CustomField unmarshalling fails on an invalid value.
// In this case a float64 as CustomField.Value should cause an error.
func TestGetTicketWithInvalidCustomField(t *testing.T) {
// Test with a number value.
invalidCustomFieldJson := `{ "id": 360005657120, "value": 123.456 }`
var customField CustomField
err := json.Unmarshal([]byte(invalidCustomFieldJson), &customField)
if err == nil {
t.Fatalf("Expected an error when parsing a custom field of type number.")
}

// Test with an array of numbers.
invalidCustomFieldJson = `{ "id": 360005657120, "value": [123, 456] }`
err = json.Unmarshal([]byte(invalidCustomFieldJson), &customField)
if err == nil {
t.Fatalf("Expected an error when parsing a custom field of type [number, ...].")
}
}

func TestGetTicketWithCustomFields(t *testing.T) {
mockAPI := newMockAPI(http.MethodGet, "ticket_custom_field.json")
client := newTestClient(mockAPI)
defer mockAPI.Close()

ticket, err := client.GetTicket(ctx, 4)
if err != nil {
t.Fatalf("Failed to get ticket: %s", err)
}

expectedID := int64(4)
if ticket.ID != expectedID {
t.Fatalf("Returned ticket does not have the expected ID %d. Ticket id is %d", expectedID, ticket.ID)
}
if ticket.CustomFields == nil || len(ticket.CustomFields) == 0 {
t.Fatalf("Returned ticket does not have the expected custom fields.")
}
for _, cf := range ticket.CustomFields {
switch cf.Value.(type) {
case string:
expectedCustomFieldValue := "Custom field value for testing"
if cf.Value != expectedCustomFieldValue {
t.Fatalf("Returned custom field value is not the expected value %s", cf.Value)
}
case []string:
expectedCustomFieldValue := []string{"list", "of", "values"}
sort.Strings(expectedCustomFieldValue)
// FIXME: This comparison of array contents was necessary because reflect.DeepEqual(cf.Value.([]string), expectedCustomFieldValue) would not work.
if len(cf.Value.([]string)) != len(expectedCustomFieldValue) {
t.Fatalf("Expected length comparison failed")
}
for _, v := range cf.Value.([]string) {
i := sort.SearchStrings(expectedCustomFieldValue, v)
if i >= len(expectedCustomFieldValue) || expectedCustomFieldValue[i] != v {
t.Fatalf("Expected to find %s in custom fields", v)
}
}
default:
t.Fatalf("Invalid value type in custom field: %v.", cf)
}
}
}

func TestGetMultipleTicket(t *testing.T) {
mockAPI := newMockAPI(http.MethodGet, "ticket_show_many.json")
client := newTestClient(mockAPI)
Expand Down

0 comments on commit d2f1492

Please sign in to comment.