Skip to content

Commit

Permalink
zendesk: add pagination to ListComments endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AChelikani committed Mar 30, 2023
1 parent b3a7d08 commit bf964f4
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 53 deletions.
7 changes: 6 additions & 1 deletion fixture/GET/ticket_comments.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,10 @@
"attachments": [],
"created_at": "2019-06-03T02:23:47Z"
}
]
],
"meta": {
"has_more": true,
"after_cursor": "xxx",
"before_cursor": "yyy"
}
}
12 changes: 6 additions & 6 deletions zendesk/mock/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 48 additions & 28 deletions zendesk/ticket_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// TicketCommentAPI is an interface containing all ticket comment related API methods
type TicketCommentAPI interface {
CreateTicketComment(ctx context.Context, ticketID int64, ticketComment TicketComment) (TicketComment, error)
ListTicketComments(ctx context.Context, ticketID int64) ([]TicketComment, error)
ListTicketComments(ctx context.Context, ticketID int64, opts *ListTicketCommentsOptions) (*ListTicketCommentsResult, error)
MakeCommentPrivate(ctx context.Context, ticketID int64, ticketCommentID int64) error
}

Expand All @@ -33,13 +33,6 @@ type TicketComment struct {
Via *Via `json:"via,omitempty"`
}

// RedactTicketCommentRequest contains the body of the RedactTicketComment PUT request
type RedactTicketCommentRequest struct {
TicketID int64 `json:"ticket_id"` // Required
HTMLBody string `json:"html_body,omitempty"`
ExternalAttachmentUrls []string `json:"external_attachment_urls,omitempty"`
}

// NewPublicTicketComment generates and returns a new TicketComment
func NewPublicTicketComment(body string, authorID int64) TicketComment {
public := true
Expand Down Expand Up @@ -89,25 +82,65 @@ func (z *Client) CreateTicketComment(ctx context.Context, ticketID int64, ticket
return result, err
}

type listTicketCommentsSort string

const (
// TicketCommentCreatedAtAsc defines ASC sort val.
TicketCommentCreatedAtAsc listTicketCommentsSort = "created_at"

// TicketCommentCreatedAtDesc defines DESC sort val.
TicketCommentCreatedAtDesc listTicketCommentsSort = "-created_at"

// ListTicketCommentsMaxPageSize contains the max page size.
ListTicketCommentsMaxPageSize int = 100
)

// ListTicketCommentOptions contains all the options supported by ListTicketComments endpoint.
type ListTicketCommentsOptions struct {
CursorPagination

Include string `url:"include,omitempty"`
IncludeInlineImages string `url:"include_inline_images,omitempty"`
Sort listTicketCommentsSort `url:"sort,omitempty"`
}

// ListTicketCommentsResult contains the resulting ticket comments
// and cursor pagination metadata.
type ListTicketCommentsResult struct {
TicketComments []TicketComment `json:"comments"`
Meta CursorPaginationMeta `json:"meta"`
}

// ListTicketComments gets a list of comment for a specified ticket
//
// ref: https://developer.zendesk.com/rest_api/docs/support/ticket_comments#list-comments
func (z *Client) ListTicketComments(ctx context.Context, ticketID int64) ([]TicketComment, error) {
var result struct {
TicketComments []TicketComment `json:"comments"`
func (z *Client) ListTicketComments(
ctx context.Context,
ticketID int64,
opts *ListTicketCommentsOptions,
) (*ListTicketCommentsResult, error) {
url := fmt.Sprintf("/tickets/%d/comments.json", ticketID)

var err error
if opts != nil {
url, err = addOptions(url, opts)
if err != nil {
return nil, err
}
}

body, err := z.get(ctx, fmt.Sprintf("/tickets/%d/comments.json", ticketID))
body, err := z.get(ctx, url)
if err != nil {
return []TicketComment{}, err
return nil, err
}

var result ListTicketCommentsResult
err = json.Unmarshal(body, &result)
if err != nil {
return []TicketComment{}, err
return nil, err
}

return result.TicketComments, err
return &result, err
}

// MakeCommentPrivate converts an existing ticket comment to an internal note that is not publicly viewable.
Expand All @@ -118,16 +151,3 @@ func (z *Client) MakeCommentPrivate(ctx context.Context, ticketID int64, ticketC
_, err := z.put(ctx, path, nil)
return err
}

// RedactTicketComment permanently removes words, strings, or attachments from a ticket comment
//
// ref: https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_comments/#redact-ticket-comment-in-agent-workspace
func (z *Client) RedactTicketComment(
ctx context.Context,
ticketCommentID int64,
body RedactTicketCommentRequest,
) error {
path := fmt.Sprintf("/api/v2/comment_redactions/%d.json", ticketCommentID)
_, err := z.put(ctx, path, body)
return err
}
33 changes: 15 additions & 18 deletions zendesk/ticket_comment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,26 @@ func TestListTicketComments(t *testing.T) {
client := newTestClient(mockAPI)
defer mockAPI.Close()

ticketComments, err := client.ListTicketComments(ctx, 2)
result, err := client.ListTicketComments(ctx, 2, nil)
if err != nil {
t.Fatalf("Failed to list ticket comments: %s", err)
}

expectedLength := 2
if len(ticketComments) != expectedLength {
t.Fatalf("Returned ticket comments does not have the expected length %d. Ticket comments length is %d", expectedLength, len(ticketComments))
if len(result.TicketComments) != expectedLength {
t.Fatalf("Returned ticket comments does not have the expected length %d. Ticket comments length is %d", expectedLength, len(result.TicketComments))
}

expectedPaginationMeta := CursorPaginationMeta{
HasMore: true,
AfterCursor: "xxx",
BeforeCursor: "yyy",
}

if result.Meta != expectedPaginationMeta {
t.Fatalf(`Failed to return correct cursor options.
Expected: %+v
Received: %+v`, expectedPaginationMeta, result.Meta)
}
}

Expand Down Expand Up @@ -100,18 +112,3 @@ func TestMakeCommentPrivate(t *testing.T) {
})
}
}

func TestRedactTicketComment(t *testing.T) {
mockAPI := newMockAPI(http.MethodPut, "redact_ticket_comment.json")
client := newTestClient(mockAPI)
defer mockAPI.Close()

err := client.RedactTicketComment(ctx, 123, RedactTicketCommentRequest{
TicketID: 100,
HTMLBody: "<div class=\"zd-comment\" dir=\"auto\">My ID number is <redact>847564</redact>!</div>",
})

if err != nil {
t.Fatalf("Failed to redact ticket comment: %s", err)
}
}
27 changes: 27 additions & 0 deletions zendesk/zendesk.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ type (
Put(ctx context.Context, path string, data interface{}) ([]byte, error)
Delete(ctx context.Context, path string) error
}

// CursorPagination contains options for using cursor pagination.
// Cursor pagination is preferred where possible.
CursorPagination struct {
// PageSize sets the number of results per page.
// Most endpoints support up to 100 records per page.
PageSize int `url:"page[size],omitempty"`

// PageAfter provides the "next" cursor.
PageAfter string `url:"page[after],omitempty"`

// PageBefore provides the "previous" cursor.
PageBefore string `url:"page[before],omitempty"`
}

// CursorPaginationMeta contains information concerning how to fetch
// next and previous results, and if next results exist.
CursorPaginationMeta struct {
// HasMore is true if more results exist in the endpoint.
HasMore bool `json:"has_more,omitempty"`

// AfterCursor contains the cursor of the next result set.
AfterCursor string `json:"after_cursor,omitempty"`

// BeforeCursor contains the cursor of the previous result set.
BeforeCursor string `json:"before_cursor,omitempty"`
}
)

// NewClient creates new Zendesk API client
Expand Down

0 comments on commit bf964f4

Please sign in to comment.