Repository: nukosuke/go-zendesk (go)

Issue:
Add support for attachment
API reference: https://developer.zendesk.com/rest_api/docs/support/attachments

Pull request description:
Attachment api
close #85 

Patch:
diff --git a/README.md b/README.md
index a71f47f..0833b34 100644
--- a/README.md
+++ b/README.md
@@ -45,10 +45,6 @@ func main() {
 }
 ```
 
-## Authors
-- [nukosuke](https://github.com/nukosuke)
-- [tamccall](https://github.com/tamccall)
-
 ## License
 
 MIT License.
diff --git a/fixture/GET/attachment.json b/fixture/GET/attachment.json
new file mode 100644
index 0000000..5984a64
--- /dev/null
+++ b/fixture/GET/attachment.json
@@ -0,0 +1,11 @@
+{
+  "attachment": {
+    "id":           498483,
+    "name":         "myfile.dat",
+    "content_url":  "https://company.zendesk.com/attachments/myfile.dat",
+    "content_type": "application/binary",
+    "size":         2532,
+    "thumbnails":   [],
+    "url":          "https://company.zendesk.com/api/v2/attachments/498483.json"
+  }
+}
\ No newline at end of file
diff --git a/zendesk/attachment.go b/zendesk/attachment.go
index 6629fcc..b145fab 100644
--- a/zendesk/attachment.go
+++ b/zendesk/attachment.go
@@ -2,6 +2,7 @@ package zendesk
 
 import (
 	"encoding/json"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -11,13 +12,13 @@ import (
 // Attachment is struct for attachment payload
 // https://developer.zendesk.com/rest_api/docs/support/attachments.html
 type Attachment struct {
-	ID          int64   `json:"id"`
-	FileName    string  `json:"file_name"`
-	ContentURL  string  `json:"content_url"`
-	ContentType string  `json:"content_type"`
-	Size        int64   `json:"size"`
-	Thumbnails  []Photo `json:"thumbnails"`
-	Inline      bool    `json:"inline"`
+	ID          int64   `json:"id,omitempty"`
+	FileName    string  `json:"file_name,omitempty"`
+	ContentURL  string  `json:"content_url,omitempty"`
+	ContentType string  `json:"content_type,omitempty"`
+	Size        int64   `json:"size,omitempty"`
+	Thumbnails  []Photo `json:"thumbnails,omitempty"`
+	Inline      bool    `json:"inline,omitempty"`
 }
 
 // Photo is thumbnail which is included in attachment
@@ -153,6 +154,8 @@ func (wr *writer) Close() (Upload, error) {
 // AttachmentAPI an interface containing all of the attachment related zendesk methods
 type AttachmentAPI interface {
 	UploadAttachment(filename string, token string) UploadWriter
+	DeleteUpload(token string) error
+	GetAttachment(id int64) (Attachment, error)
 }
 
 // UploadAttachment returns a writer that can be used to create a zendesk attachment
@@ -164,3 +167,29 @@ func (z *Client) UploadAttachment(filename string, token string) UploadWriter {
 		token:    token,
 	}
 }
+
+// DeleteUpload deletes a previously uploaded file
+// ref: https://developer.zendesk.com/rest_api/docs/support/attachments#delete-upload
+func (z *Client) DeleteUpload(token string) error {
+	return z.Delete(fmt.Sprintf("/uploads/%s.json", token))
+}
+
+// GetAttachment returns the current state of an uploaded attachment
+// ref: https://developer.zendesk.com/rest_api/docs/support/attachments#show-attachment
+func (z *Client) GetAttachment(id int64) (Attachment, error) {
+	var result struct {
+		Attachment Attachment `json:"attachment"`
+	}
+
+	body, err := z.Get(fmt.Sprintf("/attachments/%d.json", id))
+	if err != nil {
+		return Attachment{}, err
+	}
+
+	err = json.Unmarshal(body, &result)
+	if err != nil {
+		return Attachment{}, err
+	}
+
+	return result.Attachment, nil
+}
diff --git a/zendesk/mock/client.go b/zendesk/mock/client.go
index 7d57085..ad09fcb 100644
--- a/zendesk/mock/client.go
+++ b/zendesk/mock/client.go
@@ -179,6 +179,35 @@ func (mr *ClientMockRecorder) DeleteTicketForm(arg0 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTicketForm", reflect.TypeOf((*Client)(nil).DeleteTicketForm), arg0)
 }
 
+// DeleteUpload mocks base method
+func (m *Client) DeleteUpload(arg0 string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DeleteUpload", arg0)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DeleteUpload indicates an expected call of DeleteUpload
+func (mr *ClientMockRecorder) DeleteUpload(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUpload", reflect.TypeOf((*Client)(nil).DeleteUpload), arg0)
+}
+
+// GetAttachment mocks base method
+func (m *Client) GetAttachment(arg0 int64) (zendesk.Attachment, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetAttachment", arg0)
+	ret0, _ := ret[0].(zendesk.Attachment)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetAttachment indicates an expected call of GetAttachment
+func (mr *ClientMockRecorder) GetAttachment(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachment", reflect.TypeOf((*Client)(nil).GetAttachment), arg0)
+}
+
 // GetBrand mocks base method
 func (m *Client) GetBrand(arg0 int64) (zendesk.Brand, error) {
 	m.ctrl.T.Helper()


Test patch:
diff --git a/zendesk/attachment_test.go b/zendesk/attachment_test.go
index 1edc6f6..225935b 100644
--- a/zendesk/attachment_test.go
+++ b/zendesk/attachment_test.go
@@ -47,3 +47,32 @@ func TestWrite(t *testing.T) {
 		t.Fatalf("Check sum of the written file does not match the expected checksum")
 	}
 }
+
+func TestDeleteUpload(t *testing.T) {
+	mockAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusNoContent)
+		w.Write(nil)
+	}))
+
+	c := newTestClient(mockAPI)
+	err := c.DeleteUpload("foobar")
+	if err != nil {
+		t.Fatalf("Failed to delete ticket field: %s", err)
+	}
+}
+
+func TestGetAttachment(t *testing.T) {
+	mockAPI := newMockAPI(http.MethodGet, "attachment.json")
+	client := newTestClient(mockAPI)
+	defer mockAPI.Close()
+
+	attachment, err := client.GetAttachment(123)
+	if err != nil {
+		t.Fatalf("Failed to get attachment: %s", err)
+	}
+
+	expectedID := int64(498483)
+	if attachment.ID != expectedID {
+		t.Fatalf("Returned attachment does not have the expected ID %d. Attachment id is %d", expectedID, attachment.ID)
+	}
+}