-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstash_submit.go
134 lines (118 loc) · 4.03 KB
/
stash_submit.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
package deviantart
import (
"bytes"
"fmt"
"io"
"io/fs"
"mime/multipart"
"strings"
"github.com/google/go-querystring/query"
)
type StashSubmitParams struct {
// The title of the submission.
Title string `url:"title,omitempty"`
// Additional information about the submission provided by the author.
Description string `url:"artist_comments,omitempty"`
// An array of tags describing the submission. Letters, numbers and
// underscore only.
Tags []string `url:"tags,brackets,omitempty"`
// A link to the original, in case the artwork has already been posted
// elsewhere. This field can be restricted with a whitelist by editing your
// deviantART app.
OriginalURL string `url:"original_url,omitempty"`
// Is the submission being worked on currently. You can use this flag to
// warn users that the item is being edited and may change if they reload.
// Note this does NOT provide any type of locking.
IsDirty bool `url:"is_dirty,omitempty"`
// The id of an existing Sta.sh submission. This can be used to overwrite
// files and /metadata of existing submissions. If you make a new API call
// containing files, the files that were previously associated with the
// artwork will be replaced by the new ones.
ItemID int64 `url:"itemid,omitempty"`
// The name of the stack to create and place the new submission in. Applies
// to new submissions only. (Ignored if `stackid` is set).
Stack string `url:"stack,omitempty"`
// The id of the stack to create and place the new submission in. Applies to
// new submissions only.
StackID int64 `url:"stackid,omitempty"`
}
type SubmitResponse struct {
StatusResponse
ItemID int64 `json:"itemid"`
Stack string `json:"stack,omitempty"`
StackID int64 `json:"stackid,omitempty"`
}
// Submit submits files to Sta.sh or modify existing files.
//
// It can receive files in any format. Some formats like JPG, PNG, GIF, HTML or
// plain text can be viewed directly on Sta.sh and DeviantArt. Other file types
// are made available for download and may have a preview image.
//
// To connect to this endpoint OAuth2 Access Token from the Authorization Code
// Grant is required.
//
// The following scopes are required to access this resource:
//
// - stash
func (s *StashService) Submit(params *StashSubmitParams, files ...fs.File) (SubmitResponse, error) {
var (
success SubmitResponse
failure Error
)
provider, err := newMultipartBodyProvider(params, files...)
if err != nil {
return SubmitResponse{}, fmt.Errorf("prepare submit data: %w", err)
}
_, err = s.sling.New().Post("submit").BodyProvider(provider).Receive(&success, &failure)
if err := relevantError(err, failure); err != nil {
return SubmitResponse{}, fmt.Errorf("unable to submit file to sta.sh: %w", err)
}
return success, nil
}
type multipartBodyProvider struct {
reader io.Reader
contentType string
}
func newMultipartBodyProvider(params *StashSubmitParams, files ...fs.File) (*multipartBodyProvider, error) {
values, err := query.Values(params)
if err != nil {
return nil, fmt.Errorf("encode params: %w", err)
}
if len(files) == 0 {
// Fallback to form body provider.
return &multipartBodyProvider{
reader: strings.NewReader(values.Encode()),
contentType: "application/x-www-form-urlencoded",
}, nil
}
buf := new(bytes.Buffer)
mp := multipart.NewWriter(buf)
for _, file := range files {
fs, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("get file stat: %w", err)
}
part, err := mp.CreateFormFile(fs.Name(), fs.Name()) // TODO: Is it correct?
if err != nil {
return nil, fmt.Errorf("create from file: %w", err)
}
io.Copy(part, file)
}
for key, vals := range values {
for _, val := range vals {
mp.WriteField(key, val)
}
}
return &multipartBodyProvider{
reader: buf,
contentType: mp.FormDataContentType(),
}, nil
}
// ContentType returns the Content-Type of the body.
func (p *multipartBodyProvider) ContentType() string {
return p.contentType
}
// Body returns the io.Reader body.
func (p *multipartBodyProvider) Body() (io.Reader, error) {
return p.reader, nil
}