I am writing golang client with youtrack REST I have written the most most path of API. But faced problem with attaching files to an Issue. So, here there is small and good doc https://www.jetbrains.com/help/youtrack/devportal/api-usecase-attach-files.html
The using the commands from this and other doc pages are worked with curl(via terminal) I am newby in golang, but have to write in this language.
func createForm(form map[string]string) (string, io.Reader, error) {
body := new(bytes.Buffer)
mp := multipart.NewWriter(body)
defer mp.Close()
for key, val := range form {
if strings.HasPrefix(val, "@") {
val = val[1:]
file, err := os.Open(val)
if err != nil {
return "", nil, err
}
defer file.Close()
part, err := mp.CreateFormFile(key, val)
if err != nil {
return "", nil, err
}
io.Copy(part, file)
} else {
mp.WriteField(key, val)
}
}
return mp.FormDataContentType(), body, nil
}
func AttachFileToIssue(path string, issueID string) {
form := map[string]string{"image": "@image.jpeg", "key": "KEY"}
_, body, err := createForm(form)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", youTrackUrl+"/api/issues/"+issueID+"/attachments?fields=id,name", body)
if err != nil {
// handle err
}
req.Header.Set("Authorization", "Bearer "+youTrackToken)
req.Header.Set("Content-Type", "multipart/form-data")
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
}
fmt.Println(resp)
if resp.StatusCode == http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
bodyString := string(bodyBytes)
fmt.Println(bodyString)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
}
}(resp.Body)
}
The error code is:
{400 Bad Request 400 HTTP/1.1 1 1 map[Access-Control-Expose-Headers:[Location] Cache-Control:[no-cache, no-store, no-transform, must-revalidate] Content-Length:[110] Content-Type:[application/json;charset=utf-8] Date:[Sun, 10 Jul 2022 10:28:24 GMT] Referrer-Policy:[same-origin] Server:[YouTrack] X-Content-Type-Options:[nosniff] X-Frame-Options:[SAMEORIGIN] X-Xss-Protection:[1; mode=block]] 0xc0001cc100 110 [] false false map[] 0xc000254000 <nil>}
I used wireshark to check what is wrong, the problem is with mime-part, something missing.
The curl REQUEST:
curl -v -i -F upload=@/Users/jetbrains/Downloads/youtrack.txt \
-F upload=@/Users/jetbrains/Downloads/youtrack_new.jpeg \
-H 'Authorization: Bearer perm:cm9vdA==.MjZGZWI=.WB02vjX0cM2ltLTJXUE3VOWHpJYYNx' \
-H 'Content-Type: multipart/form-data' \
-X POST 'https://example.youtrack.cloud/api/issues/99-500/attachments?fields=id,name'
Ana Bartasheva is right!
The solution:
func createForm(form map[string]string) (string, io.Reader, error) {
body := new(bytes.Buffer)
mp := multipart.NewWriter(body)
defer func(mp *multipart.Writer) {
err := mp.Close()
if err != nil {
}
}(mp)
var file *os.File
for key, val := range form {
if strings.HasPrefix(val, "@") {
val = val[1:]
file, err := os.Open(val)
if err != nil {
return "", nil, err
}
part, err := mp.CreateFormFile(key, val)
if err != nil {
return "", nil, err
}
_, err = io.Copy(part, file)
if err != nil {
return "", nil, err
}
} else {
err := mp.WriteField(key, val)
if err != nil {
return "", nil, err
}
}
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
}
}(file)
return mp.FormDataContentType(), body, nil
}
func AttachFileToIssue(path string, issueID string) string {
form := map[string]string{"path": "@" + path}
mp, body, err := createForm(form)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", youTrackUrl+"/api/issues/"+issueID+"/attachments?fields=id,name", body)
if err != nil {
// handle err
}
req.Header.Set("Authorization", "Bearer "+youTrackToken)
req.Header.Set("Content-Type", mp)
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
}
if resp.StatusCode == http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
bodyString := string(bodyBytes)
return bodyString
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
}
}(resp.Body)
return ""
}