diff --git a/fixture/GET/group_memberships.json b/fixture/GET/group_memberships.json new file mode 100644 index 00000000..a75feee1 --- /dev/null +++ b/fixture/GET/group_memberships.json @@ -0,0 +1,25 @@ +{ + "group_memberships": [ + { + "url": "https://terraform-provider-zendesk.zendesk.com/api/v2/group_memberships/360002440594.json", + "id": 360002440594, + "user_id": 15439980, + "group_id": 98907558, + "default": false, + "created_at": "2018-11-23T16:05:12Z", + "updated_at": "2018-11-23T16:05:15Z" + }, + { + "url": "https://terraform-provider-zendesk.zendesk.com/api/v2/group_memberships/360002440595.json", + "id": 360002440595, + "user_id": 15439981, + "group_id": 98907557, + "default": false, + "created_at": "2018-11-23T16:05:12Z", + "updated_at": "2018-11-23T16:05:15Z" + } + ], + "next_page": null, + "previous_page": null, + "count": 1 +} diff --git a/fixture/GET/user_related.json b/fixture/GET/user_related.json new file mode 100644 index 00000000..692d1230 --- /dev/null +++ b/fixture/GET/user_related.json @@ -0,0 +1,8 @@ +{ + "user_related": { + "assigned_tickets": 5, + "ccd_tickets": 3, + "organization_subscriptions": 1, + "requested_tickets": 10 + } +} diff --git a/fixture/GET/view.json b/fixture/GET/view.json new file mode 100644 index 00000000..4fe6618c --- /dev/null +++ b/fixture/GET/view.json @@ -0,0 +1,91 @@ +{ + "view": { + "url": "https://terraform-provider-zendesk.zendesk.com/api/v2/views/360002440594.json", + "id": 360002440594, + "title": "Wonderful tickets", + "active": true, + "created_at": "2018-11-23T16:05:12Z", + "updated_at": "2018-11-23T16:05:15Z", + "position": 0, + "description": "This is a wonderful view of your tickets", + "execution": { + "group_by": "status", + "group_order": "asc", + "sort_by": "nice_id", + "sort_order": "desc", + "group": { + "id": "status", + "title": "Status", + "order": "asc" + }, + "sort": { + "id": "ticket_id", + "title": "ID", + "order": "desc" + }, + "columns": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "fields": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "custom_fields": [] + }, + "conditions": { + "all": [ + { + "field": "status", + "operator": "less_than", + "value": "solved" + }, + { + "field": "assignee_id", + "operator": "is", + "value": "current_user" + } + ], + "any": [] + }, + "restriction": null, + "watchable": true, + "raw_title": "{{zd.your_wonderful_tickets}}" + } +} diff --git a/fixture/GET/views.json b/fixture/GET/views.json new file mode 100644 index 00000000..e2dad6d6 --- /dev/null +++ b/fixture/GET/views.json @@ -0,0 +1,185 @@ +{ + "views": [ + { + "url": "https://terraform-provider-zendesk.zendesk.com/api/v2/views/360002440595.json", + "id": 360002440595, + "title": "Wonderful tickets", + "active": true, + "created_at": "2018-11-23T16:05:12Z", + "updated_at": "2018-11-23T16:05:15Z", + "position": 0, + "description": "This is a wonderful view of your tickets", + "execution": { + "group_by": "status", + "group_order": "asc", + "sort_by": "nice_id", + "sort_order": "desc", + "group": { + "id": "status", + "title": "Status", + "order": "asc" + }, + "sort": { + "id": "ticket_id", + "title": "ID", + "order": "desc" + }, + "columns": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "fields": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "custom_fields": [] + }, + "conditions": { + "all": [ + { + "field": "status", + "operator": "less_than", + "value": "solved" + }, + { + "field": "assignee_id", + "operator": "is", + "value": "current_user" + } + ], + "any": [] + }, + "restriction": null, + "watchable": true, + "raw_title": "{{zd.your_wonderful_tickets}}" + }, + { + "url": "https://terraform-provider-zendesk.zendesk.com/api/v2/views/360002440594.json", + "id": 360002440594, + "title": "OK tickets", + "active": true, + "created_at": "2018-11-23T16:05:12Z", + "updated_at": "2018-11-23T16:05:15Z", + "position": 0, + "description": "This is an ok view of your tickets", + "execution": { + "group_by": "status", + "group_order": "asc", + "sort_by": "nice_id", + "sort_order": "desc", + "group": { + "id": "status", + "title": "Status", + "order": "asc" + }, + "sort": { + "id": "ticket_id", + "title": "ID", + "order": "desc" + }, + "columns": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "fields": [ + { + "id": "subject", + "title": "Subject" + }, + { + "id": "requester", + "title": "Requester" + }, + { + "id": "created", + "title": "Requested" + }, + { + "id": "type", + "title": "Type" + }, + { + "id": "priority", + "title": "Priority" + } + ], + "custom_fields": [] + }, + "conditions": { + "all": [ + { + "field": "status", + "operator": "less_than", + "value": "solved" + }, + { + "field": "assignee_id", + "operator": "is", + "value": "current_user" + } + ], + "any": [] + }, + "restriction": null, + "watchable": true, + "raw_title": "{{zd.your_ok_tickets}}" + } + ], + "next_page": null, + "previous_page": null, + "count": 2 +} diff --git a/zendesk/api.go b/zendesk/api.go index 61728c50..c09989eb 100644 --- a/zendesk/api.go +++ b/zendesk/api.go @@ -10,6 +10,7 @@ type API interface { BrandAPI DynamicContentAPI GroupAPI + GroupMembershipAPI LocaleAPI MacroAPI TicketAPI @@ -25,6 +26,7 @@ type API interface { TagAPI TicketAuditAPI TicketCommentAPI + ViewAPI } var _ API = (*Client)(nil) diff --git a/zendesk/group_membership.go b/zendesk/group_membership.go new file mode 100644 index 00000000..590d0d78 --- /dev/null +++ b/zendesk/group_membership.go @@ -0,0 +1,65 @@ +package zendesk + +import ( + "context" + "encoding/json" + "time" +) + +type ( + // GroupMembership is struct for group membership payload + // https://developer.zendesk.com/api-reference/ticketing/groups/group_memberships/ + GroupMembership struct { + ID int64 `json:"id,omitempty"` + URL string `json:"url,omitempty"` + UserID int64 `json:"user_id"` + GroupID int64 `json:"group_id"` + Default bool `json:"default"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + } + + // GroupMembershipListOptions is a struct for options for group membership list + // ref: https://developer.zendesk.com/api-reference/ticketing/groups/group_memberships/ + GroupMembershipListOptions struct { + PageOptions + GroupID int64 `json:"group_id,omitempty" url:"group_id,omitempty"` + UserID int64 `json:"user_id,omitempty" url:"user_id,omitempty"` + } + + // GroupMembershipAPI is an interface containing group membership related methods + GroupMembershipAPI interface { + GetGroupMemberships(context.Context, *GroupMembershipListOptions) ([]GroupMembership, Page, error) + } +) + +// GetGroupMemberships gets the memberships of the specified group +// ref: https://developer.zendesk.com/api-reference/ticketing/groups/group_memberships/ +func (z *Client) GetGroupMemberships(ctx context.Context, opts *GroupMembershipListOptions) ([]GroupMembership, Page, error) { + var result struct { + GroupMemberships []GroupMembership `json:"group_memberships"` + Page + } + + tmp := opts + if tmp == nil { + tmp = new(GroupMembershipListOptions) + } + + u, err := addOptions("/group_memberships.json", tmp) + if err != nil { + return nil, Page{}, err + } + + body, err := z.get(ctx, u) + if err != nil { + return nil, Page{}, err + } + + if err := json.Unmarshal(body, &result); err != nil { + return nil, Page{}, err + } + + return result.GroupMemberships, result.Page, nil +} diff --git a/zendesk/group_membership_test.go b/zendesk/group_membership_test.go new file mode 100644 index 00000000..e6a48249 --- /dev/null +++ b/zendesk/group_membership_test.go @@ -0,0 +1,21 @@ +package zendesk + +import ( + "net/http" + "testing" +) + +func TestGetGroupMemberships(t *testing.T) { + mockAPI := newMockAPI(http.MethodGet, "group_memberships.json") + client := newTestClient(mockAPI) + defer mockAPI.Close() + + groupMemberships, _, err := client.GetGroupMemberships(ctx, &GroupMembershipListOptions{GroupID: 123}) + if err != nil { + t.Fatalf("Failed to get group memberships: %s", err) + } + + if len(groupMemberships) != 2 { + t.Fatalf("expected length of group memberships is 2, but got %d", len(groupMemberships)) + } +} diff --git a/zendesk/mock/client.go b/zendesk/mock/client.go index 3cb78439..5127e582 100644 --- a/zendesk/mock/client.go +++ b/zendesk/mock/client.go @@ -566,6 +566,22 @@ func (mr *ClientMockRecorder) GetGroup(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroup", reflect.TypeOf((*Client)(nil).GetGroup), arg0, arg1) } +// GetGroupMemberships mocks base method. +func (m *Client) GetGroupMemberships(arg0 context.Context, arg1 *zendesk.GroupMembershipListOptions) ([]zendesk.GroupMembership, zendesk.Page, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGroupMemberships", arg0, arg1) + ret0, _ := ret[0].([]zendesk.GroupMembership) + ret1, _ := ret[1].(zendesk.Page) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetGroupMemberships indicates an expected call of GetGroupMemberships. +func (mr *ClientMockRecorder) GetGroupMemberships(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupMemberships", reflect.TypeOf((*Client)(nil).GetGroupMemberships), arg0, arg1) +} + // GetGroups mocks base method. func (m *Client) GetGroups(arg0 context.Context) ([]zendesk.Group, zendesk.Page, error) { m.ctrl.T.Helper() @@ -628,6 +644,22 @@ func (mr *ClientMockRecorder) GetMacros(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMacros", reflect.TypeOf((*Client)(nil).GetMacros), arg0, arg1) } +// GetManyUsers mocks base method. +func (m *Client) GetManyUsers(arg0 context.Context, arg1 *zendesk.GetManyUsersOptions) ([]zendesk.User, zendesk.Page, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetManyUsers", arg0, arg1) + ret0, _ := ret[0].([]zendesk.User) + ret1, _ := ret[1].(zendesk.Page) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetManyUsers indicates an expected call of GetManyUsers. +func (mr *ClientMockRecorder) GetManyUsers(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetManyUsers", reflect.TypeOf((*Client)(nil).GetManyUsers), arg0, arg1) +} + // GetMultipleTickets mocks base method. func (m *Client) GetMultipleTickets(arg0 context.Context, arg1 []int64) ([]zendesk.Ticket, error) { m.ctrl.T.Helper() @@ -890,6 +922,21 @@ func (mr *ClientMockRecorder) GetTickets(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTickets", reflect.TypeOf((*Client)(nil).GetTickets), arg0, arg1) } +// GetTicketsFromView mocks base method. +func (m *Client) GetTicketsFromView(arg0 context.Context, arg1 int64) ([]zendesk.Ticket, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTicketsFromView", arg0, arg1) + ret0, _ := ret[0].([]zendesk.Ticket) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTicketsFromView indicates an expected call of GetTicketsFromView. +func (mr *ClientMockRecorder) GetTicketsFromView(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTicketsFromView", reflect.TypeOf((*Client)(nil).GetTicketsFromView), arg0, arg1) +} + // GetTrigger mocks base method. func (m *Client) GetTrigger(arg0 context.Context, arg1 int64) (zendesk.Trigger, error) { m.ctrl.T.Helper() @@ -952,6 +999,21 @@ func (mr *ClientMockRecorder) GetUserFields(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserFields", reflect.TypeOf((*Client)(nil).GetUserFields), arg0, arg1) } +// GetUserRelated mocks base method. +func (m *Client) GetUserRelated(arg0 context.Context, arg1 int64) (zendesk.UserRelated, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserRelated", arg0, arg1) + ret0, _ := ret[0].(zendesk.UserRelated) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserRelated indicates an expected call of GetUserRelated. +func (mr *ClientMockRecorder) GetUserRelated(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserRelated", reflect.TypeOf((*Client)(nil).GetUserRelated), arg0, arg1) +} + // GetUserTags mocks base method. func (m *Client) GetUserTags(arg0 context.Context, arg1 int64) ([]zendesk.Tag, error) { m.ctrl.T.Helper() @@ -983,6 +1045,37 @@ func (mr *ClientMockRecorder) GetUsers(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsers", reflect.TypeOf((*Client)(nil).GetUsers), arg0, arg1) } +// GetView mocks base method. +func (m *Client) GetView(arg0 context.Context, arg1 int64) (zendesk.View, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetView", arg0, arg1) + ret0, _ := ret[0].(zendesk.View) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetView indicates an expected call of GetView. +func (mr *ClientMockRecorder) GetView(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetView", reflect.TypeOf((*Client)(nil).GetView), arg0, arg1) +} + +// GetViews mocks base method. +func (m *Client) GetViews(arg0 context.Context) ([]zendesk.View, zendesk.Page, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetViews", arg0) + ret0, _ := ret[0].([]zendesk.View) + ret1, _ := ret[1].(zendesk.Page) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetViews indicates an expected call of GetViews. +func (mr *ClientMockRecorder) GetViews(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetViews", reflect.TypeOf((*Client)(nil).GetViews), arg0) +} + // ListTicketComments mocks base method. func (m *Client) ListTicketComments(arg0 context.Context, arg1 int64) ([]zendesk.TicketComment, error) { m.ctrl.T.Helper() diff --git a/zendesk/user.go b/zendesk/user.go index a369af31..410eb987 100644 --- a/zendesk/user.go +++ b/zendesk/user.go @@ -7,6 +7,9 @@ import ( "time" ) +// UserFields is a dictionary of custom user related fields +type UserFields map[string]interface{} + // User is zendesk user JSON payload format // https://developer.zendesk.com/rest_api/docs/support/users type User struct { @@ -18,35 +21,36 @@ type User struct { Alias string `json:"alias,omitempty"` ChatOnly bool `json:"chat_only,omitempty"` CustomRoleID int64 `json:"custom_role_id,omitempty"` - RoleType int64 `json:"role_type,omitempty"` + DefaultGroupID int64 `json:"default_group_id,omitempty"` Details string `json:"details,omitempty"` ExternalID string `json:"external_id,omitempty"` + IanaTimezone string `json:"iana_time_zone,omitempty"` Locale string `json:"locale,omitempty"` LocaleID int64 `json:"locale_id,omitempty"` Moderator bool `json:"moderator,omitempty"` Notes string `json:"notes,omitempty"` OnlyPrivateComments bool `json:"only_private_comments,omitempty"` OrganizationID int64 `json:"organization_id,omitempty"` - DefaultGroupID int64 `json:"default_group_id,omitempty"` Phone string `json:"phone,omitempty"` - SharedPhoneNumber bool `json:"shared_phone_number,omitempty"` Photo Attachment `json:"photo,omitempty"` RestrictedAgent bool `json:"restricted_agent,omitempty"` Role string `json:"role,omitempty"` + RoleType int64 `json:"role_type,omitempty"` Shared bool `json:"shared,omitempty"` SharedAgent bool `json:"shared_agent,omitempty"` + SharedPhoneNumber bool `json:"shared_phone_number,omitempty"` Signature string `json:"signature,omitempty"` Suspended bool `json:"suspended,omitempty"` Tags []string `json:"tags,omitempty"` TicketRestriction string `json:"ticket_restriction,omitempty"` Timezone string `json:"time_zone,omitempty"` TwoFactorAuthEnabled bool `json:"two_factor_auth_enabled,omitempty"` - //TODO: UserFields UserFields - Verified bool `json:"verified,omitempty"` - ReportCSV bool `json:"report_csv,omitempty"` - LastLoginAt time.Time `json:"last_login_at,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty"` + UserFields UserFields `json:"user_fields"` + Verified bool `json:"verified,omitempty"` + ReportCSV bool `json:"report_csv,omitempty"` + LastLoginAt time.Time `json:"last_login_at,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` } const ( @@ -79,12 +83,32 @@ func UserRoleText(role int) string { return userRoleText[role] } +// GetManyUsersOptions is options for GetManyUsers +// +// ref: https://developer.zendesk.com/api-reference/ticketing/users/users/#show-many-users +type GetManyUsersOptions struct { + ExternalIDs string `json:"external_ids,omitempty" url:"external_ids,omitempty"` + IDs string `json:"ids,omitempty" url:"ids,omitempty"` +} + +// UserRelated contains user related data +// +// ref: https://developer.zendesk.com/api-reference/ticketing/users/users/#show-user-related-information +type UserRelated struct { + AssignedTickets int64 `json:"assigned_tickets"` + RequestedTickets int64 `json:"requested_tickets"` + CCDTickets int64 `json:"ccd_tickets"` + OrganizationSubscriptions int64 `json:"organization_subscriptions"` +} + // UserAPI an interface containing all user related methods type UserAPI interface { + GetManyUsers(ctx context.Context, opts *GetManyUsersOptions) ([]User, Page, error) GetUsers(ctx context.Context, opts *UserListOptions) ([]User, Page, error) GetUser(ctx context.Context, userID int64) (User, error) CreateUser(ctx context.Context, user User) (User, error) UpdateUser(ctx context.Context, userID int64, user User) (User, error) + GetUserRelated(ctx context.Context, userID int64) (UserRelated, error) } // GetUsers fetch user list @@ -116,6 +140,38 @@ func (z *Client) GetUsers(ctx context.Context, opts *UserListOptions) ([]User, P return data.Users, data.Page, nil } +// GetManyUsers fetch user list +// https://developer.zendesk.com/api-reference/ticketing/users/users/#show-many-users +func (z *Client) GetManyUsers(ctx context.Context, opts *GetManyUsersOptions) ([]User, Page, error) { + var ( + data struct { + Users []User `json:"users"` + Page + } + ) + + tmp := opts + if tmp == nil { + tmp = new(GetManyUsersOptions) + } + + u, err := addOptions("/users/show_many.json", tmp) + if err != nil { + return nil, Page{}, err + } + + body, err := z.get(ctx, u) + if err != nil { + return nil, Page{}, err + } + + err = json.Unmarshal(body, &data) + if err != nil { + return nil, Page{}, err + } + return data.Users, data.Page, nil +} + //TODO: GetUsersByGroupID, GetUsersByOrganizationID // CreateUser creates new user @@ -178,3 +234,22 @@ func (z *Client) UpdateUser(ctx context.Context, userID int64, user User) (User, } return result.User, nil } + +// GetUserRelated retrieves user related user information +// ref: https://developer.zendesk.com/api-reference/ticketing/users/users/#show-user-related-information +func (z *Client) GetUserRelated(ctx context.Context, userID int64) (UserRelated, error) { + var data struct { + UserRelated UserRelated `json:"user_related"` + } + + body, err := z.get(ctx, fmt.Sprintf("/users/%d/related.json", userID)) + if err != nil { + return UserRelated{}, err + } + + if err := json.Unmarshal(body, &data); err != nil { + return UserRelated{}, err + } + + return data.UserRelated, nil +} diff --git a/zendesk/user_test.go b/zendesk/user_test.go index 33f481e5..b6944393 100644 --- a/zendesk/user_test.go +++ b/zendesk/user_test.go @@ -26,7 +26,22 @@ func TestGetUsers(t *testing.T) { } if len(users) != 2 { - t.Fatalf("expected length of userss is 2, but got %d", len(users)) + t.Fatalf("expected length of users is 2, but got %d", len(users)) + } +} + +func TestGetManyUsers(t *testing.T) { + mockAPI := newMockAPI(http.MethodGet, "users.json") + client := newTestClient(mockAPI) + defer mockAPI.Close() + + users, _, err := client.GetManyUsers(ctx, nil) + if err != nil { + t.Fatalf("Failed to get many users: %s", err) + } + + if len(users) != 2 { + t.Fatalf("expected length of many users is 2, but got %d", len(users)) } } @@ -126,3 +141,19 @@ func TestUpdateUserFailure(t *testing.T) { t.Fatal("Client did not return error when api failed") } } + +func TestGetUserRelated(t *testing.T) { + mockAPI := newMockAPIWithStatus(http.MethodGet, "user_related.json", http.StatusOK) + client := newTestClient(mockAPI) + defer mockAPI.Close() + + userRelated, err := client.GetUserRelated(ctx, 369531345753) + if err != nil { + t.Fatalf("Failed to get user related information: %s", err) + } + + expectedAssignedTickets := int64(5) + if userRelated.AssignedTickets != expectedAssignedTickets { + t.Fatalf("Returned user does not have the expected assigned tickets %d. It is %d", expectedAssignedTickets, userRelated.AssignedTickets) + } +} diff --git a/zendesk/view.go b/zendesk/view.go new file mode 100644 index 00000000..86be3ed4 --- /dev/null +++ b/zendesk/view.go @@ -0,0 +1,94 @@ +package zendesk + +import ( + "context" + "encoding/json" + "fmt" + "time" +) + +type ( + // View is struct for group membership payload + // https://developer.zendesk.com/api-reference/ticketing/business-rules/views/ + View struct { + ID int64 `json:"id,omitempty"` + Active bool `json:"active"` + Description string `json:"description"` + Position int64 `json:"position"` + Title string `json:"title"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + + // Conditions Conditions + // Execution Execution + // Restriction Restriction + } + + // ViewAPI encapsulates methods on view + ViewAPI interface { + GetView(context.Context, int64) (View, error) + GetViews(context.Context) ([]View, Page, error) + GetTicketsFromView(context.Context, int64) ([]Ticket, error) + } +) + +// GetViews gets all views +// ref: https://developer.zendesk.com/api-reference/ticketing/business-rules/views/#list-views +func (z *Client) GetViews(ctx context.Context) ([]View, Page, error) { + var result struct { + Views []View `json:"views"` + Page + } + + body, err := z.get(ctx, "/views.json") + + if err != nil { + return []View{}, Page{}, err + } + + if err := json.Unmarshal(body, &result); err != nil { + return []View{}, Page{}, err + } + + return result.Views, result.Page, nil +} + +// GetView gets a given view +// ref: https://developer.zendesk.com/api-reference/ticketing/business-rules/views/#show-view +func (z *Client) GetView(ctx context.Context, viewID int64) (View, error) { + var result struct { + View View `json:"view"` + } + + body, err := z.get(ctx, fmt.Sprintf("/views/%d.json", viewID)) + + if err != nil { + return View{}, err + } + + if err := json.Unmarshal(body, &result); err != nil { + return View{}, err + } + + return result.View, nil +} + +// GetTicketsFromView gets the tickets of the specified view +// ref: https://developer.zendesk.com/api-reference/ticketing/business-rules/views/#list-tickets-from-a-view +func (z *Client) GetTicketsFromView(ctx context.Context, viewID int64) ([]Ticket, error) { + var result struct { + Tickets []Ticket `json:"tickets"` + } + + body, err := z.get(ctx, fmt.Sprintf("/views/%d/tickets.json", viewID)) + + if err != nil { + return []Ticket{}, err + } + + if err := json.Unmarshal(body, &result); err != nil { + return []Ticket{}, err + } + + return result.Tickets, nil +} diff --git a/zendesk/view_test.go b/zendesk/view_test.go new file mode 100644 index 00000000..359904e7 --- /dev/null +++ b/zendesk/view_test.go @@ -0,0 +1,37 @@ +package zendesk + +import ( + "net/http" + "testing" +) + +func TestGetView(t *testing.T) { + mockAPI := newMockAPI(http.MethodGet, "view.json") + client := newTestClient(mockAPI) + defer mockAPI.Close() + + view, err := client.GetView(ctx, 123) + if err != nil { + t.Fatalf("Failed to get view: %s", err) + } + + expectedID := int64(360002440594) + if view.ID != expectedID { + t.Fatalf("Returned view does not have the expected ID %d. View ID is %d", expectedID, view.ID) + } +} + +func TestGetViews(t *testing.T) { + mockAPI := newMockAPI(http.MethodGet, "views.json") + client := newTestClient(mockAPI) + defer mockAPI.Close() + + views, _, err := client.GetViews(ctx) + if err != nil { + t.Fatalf("Failed to get views: %s", err) + } + + if len(views) != 2 { + t.Fatalf("expected length of views is 2, but got %d", len(views)) + } +} diff --git a/zendesk/zendesk.go b/zendesk/zendesk.go index 15476911..1226cd4d 100644 --- a/zendesk/zendesk.go +++ b/zendesk/zendesk.go @@ -245,3 +245,13 @@ func addOptions(s string, opts interface{}) (string, error) { u.RawQuery = qs.Encode() return u.String(), nil } + +// Get allows users to send requests not yet implemented +func (z *Client) Get(ctx context.Context, path string) ([]byte, error) { + return z.get(ctx, path) +} + +// Post allows users to send requests not yet implemented +func (z *Client) Post(ctx context.Context, path string, data interface{}) ([]byte, error) { + return z.post(ctx, path, data) +}