Skip to content

Commit c35cbd5

Browse files
authored
Merge pull request #2 from SvenLie/feature/attachmenets
Add attachment function; closing #1
2 parents 24dcfef + b5489cb commit c35cbd5

File tree

2 files changed

+127
-13
lines changed

2 files changed

+127
-13
lines changed

docker-compose.dev.yml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: "3.7"
2+
3+
services:
4+
imap2jira:
5+
build:
6+
context: .dev
7+
dockerfile: Dockerfile
8+
container_name: imap2jira
9+
restart: always
10+
working_dir: /go/src/app
11+
env_file:
12+
- .env
13+
volumes:
14+
- ./service:/go/src/app
15+
- ./structure_new_issue.json:/go/src/app/structure_new_issue.json
16+
- ./structure_add_comment.json:/go/src/app/structure_add_comment.json

service/cmd/server/main.go

+111-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"bytes"
45
"encoding/base64"
6+
"encoding/json"
57
"errors"
68
"github.com/emersion/go-imap"
79
"github.com/emersion/go-imap/client"
@@ -12,13 +14,18 @@ import (
1214
"io"
1315
"io/ioutil"
1416
"log"
17+
"mime/multipart"
1518
"net/http"
1619
"os"
1720
"os/signal"
1821
"regexp"
1922
"strings"
2023
)
2124

25+
type AddIssueResponse struct {
26+
Key string `json:"key"`
27+
}
28+
2229
func main() {
2330
cronInterval := os.Getenv("CRON")
2431
c := cron.New()
@@ -32,6 +39,27 @@ func main() {
3239

3340
}
3441

42+
func jsonEscape(i string) string {
43+
b, err := json.Marshal(i)
44+
if err != nil {
45+
log.Println(err)
46+
}
47+
s := string(b)
48+
return s[1:len(s)-1]
49+
}
50+
51+
func getAddIssueResponse(resp *http.Response) AddIssueResponse {
52+
bodyBytes, err := ioutil.ReadAll(resp.Body)
53+
if err != nil {
54+
log.Fatal(err)
55+
}
56+
57+
response := AddIssueResponse{}
58+
json.Unmarshal(bodyBytes, &response)
59+
60+
return response
61+
}
62+
3563
func printErrorFromApi(resp *http.Response) {
3664
bodyBytes, err := ioutil.ReadAll(resp.Body)
3765
if err != nil {
@@ -45,16 +73,14 @@ func printErrorFromApi(resp *http.Response) {
4573
func getMailBody(p *mail.Part) string {
4674
sanitizePolicy := bluemonday.UGCPolicy()
4775
body, _ := ioutil.ReadAll(p.Body)
48-
plainTextBody := strings.Replace(string(body), "\n", "\\n", -1)
49-
plainTextBody = strings.Replace(plainTextBody, "\r", "\\r", -1)
5076

5177
regex, err := regexp.Compile(`[^\w] && [\\]`)
5278
if err != nil {
5379
log.Fatal(err)
5480
}
55-
plainTextBody = regex.ReplaceAllString(plainTextBody, " ")
81+
plainTextBody := regex.ReplaceAllString(string(body), " ")
5682

57-
return sanitizePolicy.Sanitize(plainTextBody)
83+
return jsonEscape(sanitizePolicy.Sanitize(plainTextBody))
5884
}
5985

6086
func makePostRequest(endpoint string, body string) *http.Response {
@@ -70,7 +96,46 @@ func makePostRequest(endpoint string, body string) *http.Response {
7096
if err != nil {
7197
log.Fatal(err)
7298
}
73-
defer resp.Body.Close()
99+
100+
return resp
101+
}
102+
103+
func makePostRequestWithFile(endpoint string, filename string) *http.Response {
104+
jiraUrl := os.Getenv("JIRA_URL")
105+
jiraUser := os.Getenv("JIRA_USER")
106+
jiraPassword := os.Getenv("JIRA_PASSWORD")
107+
108+
clt := &http.Client{}
109+
110+
file, err := os.Open(filename)
111+
if err != nil {
112+
log.Fatal(err)
113+
}
114+
fileContents, err := ioutil.ReadAll(file)
115+
if err != nil {
116+
log.Fatal(err)
117+
}
118+
119+
body := new(bytes.Buffer)
120+
writer := multipart.NewWriter(body)
121+
part, err := writer.CreateFormFile("file", filename)
122+
if err != nil {
123+
log.Fatal(err)
124+
}
125+
part.Write(fileContents)
126+
err = writer.Close()
127+
if err != nil {
128+
log.Fatal(err)
129+
}
130+
131+
req, err := http.NewRequest("POST", jiraUrl+endpoint, body)
132+
req.Header.Add("X-Atlassian-Token", "no-check")
133+
req.Header.Add("Content-Type", writer.FormDataContentType())
134+
req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(jiraUser+":"+jiraPassword)))
135+
resp, err := clt.Do(req)
136+
if err != nil {
137+
log.Fatal(err)
138+
}
74139

75140
return resp
76141
}
@@ -171,6 +236,11 @@ func run() {
171236

172237
isMessageWithIssueNumber, _ := regexp.MatchString("^.*\\[.*-\\d+]$", subject)
173238

239+
issueNumber := ""
240+
if isMessageWithIssueNumber {
241+
issueNumber = subject[strings.LastIndex(subject, "[")+1 : strings.LastIndex(subject, "]")]
242+
}
243+
174244
mr, err := mail.CreateReader(r)
175245
if err != nil {
176246
log.Fatal(err)
@@ -192,9 +262,14 @@ func run() {
192262
log.Fatal(err)
193263
}
194264

195-
switch p.Header.(type) {
265+
switch h := p.Header.(type) {
196266
case *mail.InlineHeader:
197267
sanitizedBody := getMailBody(p)
268+
contentType, _, _ := h.ContentType()
269+
270+
if contentType != "text/plain" {
271+
continue
272+
}
198273

199274
if isMessageWithIssueNumber {
200275
content, err := ioutil.ReadFile("/go/src/app/structure_add_comment.json")
@@ -206,8 +281,6 @@ func run() {
206281
jsonString = strings.Replace(jsonString, "%SUMMARY%", subject+" ("+sender.Name+" <"+sender.Address+">)", 1)
207282
jsonString = strings.Replace(jsonString, "%DESCRIPTION%", strings.TrimSpace(sanitizedBody), 1)
208283

209-
issueNumber := subject[strings.LastIndex(subject, "[")+1 : strings.LastIndex(subject, "]")]
210-
211284
resp := makeGetRequest("/rest/api/" + jiraApiVersion + "/issue/" + issueNumber)
212285

213286
if resp.StatusCode != 200 {
@@ -216,11 +289,13 @@ func run() {
216289
resp := makePostRequest("/rest/api/"+jiraApiVersion+"/issue/"+issueNumber+"/comment", jsonString)
217290

218291
if resp.StatusCode != 201 {
292+
println("Error while adding comment")
219293
printErrorFromApi(resp)
220294
} else {
221295
setMailAsSeenForService(c, currentUid)
222-
log.Println("Success add comment")
296+
log.Println("Success add comment for issue " + issueNumber)
223297
}
298+
defer resp.Body.Close()
224299
}
225300

226301
} else {
@@ -234,19 +309,42 @@ func run() {
234309
jsonString = strings.Replace(jsonString, "%SUMMARY%", subject, 1)
235310
jsonString = strings.Replace(jsonString, "%DESCRIPTION%", strings.TrimSpace(sanitizedBody), 1)
236311

237-
log.Println(jsonString)
238-
239312
resp := makePostRequest("/rest/api/"+jiraApiVersion+"/issue", jsonString)
240313

241314
if resp.StatusCode != 201 {
315+
println("Error while adding issue")
242316
printErrorFromApi(resp)
243317
} else {
318+
issueNumber = getAddIssueResponse(resp).Key
244319
setMailAsSeenForService(c, currentUid)
245-
log.Println("Success add issue")
320+
log.Println("Success add issue " + issueNumber)
246321
}
247322
}
323+
case *mail.AttachmentHeader:
324+
filename, _ := h.Filename()
325+
log.Println("Found attachment \"" + filename + "\" for issue number " + issueNumber)
326+
file, err := os.Create("/tmp/" + filename)
327+
if err != nil {
328+
log.Fatal(err)
329+
}
330+
_, err = io.Copy(file, p.Body)
331+
if err != nil {
332+
log.Fatal(err)
333+
}
334+
335+
if issueNumber == "" {
336+
log.Fatal("Error, no issue number for attachment found")
337+
}
338+
339+
resp := makePostRequestWithFile("/rest/api/"+jiraApiVersion+"/issue/"+issueNumber+"/attachments", "/tmp/"+filename)
340+
if resp.StatusCode != 200 {
341+
println("Error while adding attachment for issue " + issueNumber)
342+
printErrorFromApi(resp)
343+
} else {
344+
log.Println("Success add attachment for issue " + issueNumber)
345+
}
346+
defer resp.Body.Close()
248347
}
249-
break
250348
}
251349
}
252350

0 commit comments

Comments
 (0)