forked from Teamwork/nylas-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
file.go
136 lines (116 loc) · 3.16 KB
/
file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package nylas
import (
"context"
"errors"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"net/textproto"
"path/filepath"
"strings"
"golang.org/x/sync/errgroup"
)
// File represents a file in the Nylas system.
type File struct {
ID string `json:"id"`
Object string `json:"object"`
AccountID string `json:"account_id"`
ContentType string `json:"content_type"`
Filename string `json:"filename"`
Size int `json:"size"`
}
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}
// File returns a files metadata by id.
// See: https://docs.nylas.com/reference#get-metadata
func (c *Client) File(ctx context.Context, id string) (File, error) {
req, err := c.newUserRequest(ctx, http.MethodGet, "/files/"+id, nil)
if err != nil {
return File{}, err
}
var resp File
return resp, c.do(req, &resp)
}
// UploadFile uploads a file to be used as an attachment.
// See: https://docs.nylas.com/reference#upload
func (c *Client) UploadFile(
ctx context.Context, filename string, file io.Reader,
) (File, error) {
g, ctx := errgroup.WithContext(ctx)
req, err := c.newUserRequest(ctx, http.MethodPost, "/files", nil)
if err != nil {
return File{}, err
}
r, w := io.Pipe()
m := multipart.NewWriter(w)
req.Body = r
req.Header.Set("Content-Type", m.FormDataContentType())
g.Go(func() (err error) {
defer w.Close() // nolint: errcheck
defer m.Close() // nolint: errcheck
contentType := mime.TypeByExtension(filepath.Ext(filename))
if contentType == "" {
contentType = "application/octet-stream" // fallback
}
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="file"; filename="%s"`,
escapeQuotes(filename)))
h.Set("Content-Type", contentType)
part, err := m.CreatePart(h)
if err != nil {
return err
}
_, err = io.Copy(part, file)
return err
})
var resp []File
g.Go(func() error {
if err := c.do(req, &resp); err != nil {
return err
} else if len(resp) == 0 {
return errors.New("no file returned")
}
return nil
})
if err := g.Wait(); err != nil {
return File{}, err
}
return resp[0], nil
}
// DownloadFile downloads a file attachment.
//
// If the returned error is nil, you are expected to read the io.ReadCloser to
// EOF and close.
//
// See: https://docs.nylas.com/reference#filesiddownload
func (c *Client) DownloadFile(ctx context.Context, id string) (io.ReadCloser, error) {
endpoint := fmt.Sprintf("/files/%s/download", id)
req, err := c.newUserRequest(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}
resp, err := c.c.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode > 299 {
defer resp.Body.Close() // nolint: errcheck
return nil, NewError(resp)
}
return resp.Body, nil
}
// DeleteFile removes an existing file identified by the specified file ID.
// See: https://docs.nylas.com/reference#files-delete
func (c *Client) DeleteFile(ctx context.Context, id string) error {
endpoint := fmt.Sprintf("/files/%s", id)
req, err := c.newUserRequest(ctx, http.MethodDelete, endpoint, nil)
if err != nil {
return err
}
return c.do(req, nil)
}