feat(GODT-3015): Add simple algorithm to deal with multiple attachment for bug report.

This commit is contained in:
Romain Le Jeune
2023-10-20 10:14:20 +00:00
parent aebe7baed0
commit b52706a3ca
6 changed files with 141 additions and 79 deletions

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 github.com/Masterminds/semver/v3 v3.2.0
github.com/ProtonMail/gluon v0.17.1-0.20231009084701-3af0474b0b3c github.com/ProtonMail/gluon v0.17.1-0.20231009084701-3af0474b0b3c
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/ProtonMail/go-proton-api v0.4.1-0.20231018070752-2449db500edd github.com/ProtonMail/go-proton-api v0.4.1-0.20231020080353-2b3fddd7f72d
github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton
github.com/PuerkitoBio/goquery v1.8.1 github.com/PuerkitoBio/goquery v1.8.1
github.com/abiosoft/ishell v2.0.0+incompatible github.com/abiosoft/ishell v2.0.0+incompatible

4
go.sum
View File

@ -34,8 +34,8 @@ github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7 h1:+j+Kd/
github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4= github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/go-proton-api v0.4.1-0.20231018070752-2449db500edd h1:/Z3nwzsVeSwTFkrIKG7BLD24P4cxpaJBn8mzbSe2kPg= github.com/ProtonMail/go-proton-api v0.4.1-0.20231020080353-2b3fddd7f72d h1:w+DiszDUNfGz64zEmmOnetV+YQssrLcOr3NkofW/Nog=
github.com/ProtonMail/go-proton-api v0.4.1-0.20231018070752-2449db500edd/go.mod h1:ZmvQMA8hanLiD1tFsvu9+qGBcuxbIRfch/4z/nqBhXA= github.com/ProtonMail/go-proton-api v0.4.1-0.20231020080353-2b3fddd7f72d/go.mod h1:ZmvQMA8hanLiD1tFsvu9+qGBcuxbIRfch/4z/nqBhXA=
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI= github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk= github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton h1:wuAxBUU9qF2wyDVJprn/2xPDx000eol5gwlKbOUYY88= github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton h1:wuAxBUU9qF2wyDVJprn/2xPDx000eol5gwlKbOUYY88=

View File

@ -19,6 +19,7 @@ package bridge
import ( import (
"context" "context"
"errors"
"io" "io"
"github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/go-proton-api"
@ -33,65 +34,133 @@ const (
DefaultMaxSessionCountForBugReport = 10 DefaultMaxSessionCountForBugReport = 10
) )
func (bridge *Bridge) ReportBug(ctx context.Context, osType, osVersion, title, description, username, email, client string, attachLogs bool) error { type ReportBugReq struct {
var account = username OSType string
OSVersion string
Title string
Description string
Username string
Email string
EmailClient string
IncludeLogs bool
}
if info, err := bridge.QueryUserInfo(username); err == nil { func (bridge *Bridge) ReportBug(ctx context.Context, report *ReportBugReq) error {
account = info.Username if info, err := bridge.QueryUserInfo(report.Username); err == nil {
report.Username = info.Username
} else if userIDs := bridge.GetUserIDs(); len(userIDs) > 0 { } else if userIDs := bridge.GetUserIDs(); len(userIDs) > 0 {
if err := bridge.vault.GetUser(userIDs[0], func(user *vault.User) { if err := bridge.vault.GetUser(userIDs[0], func(user *vault.User) {
account = user.Username() report.Username = user.Username()
}); err != nil { }); err != nil {
return err return err
} }
} }
var attachment []proton.ReportBugAttachment var attachments []proton.ReportBugAttachment
if report.IncludeLogs {
if attachLogs { logs, err := bridge.CollectLogs()
logsPath, err := bridge.locator.ProvideLogsPath()
if err != nil { if err != nil {
return err return err
} }
attachments = append(attachments, logs)
}
buffer, err := logging.ZipLogsForBugReport(logsPath, DefaultMaxSessionCountForBugReport, DefaultMaxBugReportZipSize) var firstAtt proton.ReportBugAttachment
if err != nil { if len(attachments) > 0 && report.IncludeLogs {
firstAtt = attachments[0]
}
attachmentType := proton.AttachmentTypeSync
if len(attachments) > 1 {
attachmentType = proton.AttachmentTypeAsync
}
token, err := bridge.createTicket(ctx, report, attachmentType, firstAtt)
if err != nil || token == "" {
return err return err
} }
body, err := io.ReadAll(buffer) safe.RLock(func() {
if err != nil {
return err
}
attachment = append(attachment, proton.ReportBugAttachment{
Name: "logs.zip",
Filename: "logs.zip",
MIMEType: "application/zip",
Body: body,
})
}
safe.Lock(func() {
for _, user := range bridge.users { for _, user := range bridge.users {
user.ReportBugSent() user.ReportBugSent()
} }
}, bridge.usersLock) }, bridge.usersLock)
_, err := bridge.api.ReportBug(ctx, proton.ReportBugReq{ // if we have a token we can append more attachment to the bugReport
OS: osType, for i, att := range attachments {
OSVersion: osVersion, if i == 0 && report.IncludeLogs {
continue
}
err := bridge.appendComment(ctx, token, att)
if err != nil {
return err
}
}
return err
}
Title: "[Bridge] Bug - " + title, func (bridge *Bridge) CollectLogs() (proton.ReportBugAttachment, error) {
Description: description, logsPath, err := bridge.locator.ProvideLogsPath()
if err != nil {
return proton.ReportBugAttachment{}, err
}
Client: client, buffer, err := logging.ZipLogsForBugReport(logsPath, DefaultMaxSessionCountForBugReport, DefaultMaxBugReportZipSize)
if err != nil {
return proton.ReportBugAttachment{}, err
}
body, err := io.ReadAll(buffer)
if err != nil {
return proton.ReportBugAttachment{}, err
}
return proton.ReportBugAttachment{
Name: "logs.zip",
Filename: "logs.zip",
MIMEType: "application/zip",
Body: body,
}, nil
}
func (bridge *Bridge) createTicket(ctx context.Context, report *ReportBugReq,
asyncAttach proton.AttachmentType, att proton.ReportBugAttachment) (string, error) {
var attachments []proton.ReportBugAttachment
attachments = append(attachments, att)
res, err := bridge.api.ReportBug(ctx, proton.ReportBugReq{
OS: report.OSType,
OSVersion: report.OSVersion,
Title: "[Bridge] Bug - " + report.Title,
Description: report.Description,
Client: report.EmailClient,
ClientType: proton.ClientTypeEmail, ClientType: proton.ClientTypeEmail,
ClientVersion: constants.AppVersion(bridge.curVersion.Original()), ClientVersion: constants.AppVersion(bridge.curVersion.Original()),
Username: account, Username: report.Username,
Email: email, Email: report.Email,
}, attachment...)
return err AsyncAttachments: asyncAttach,
}, attachments...)
if err != nil || asyncAttach != proton.AttachmentTypeAsync {
return "", err
}
if asyncAttach == proton.AttachmentTypeAsync && res.Token == nil {
return "", errors.New("no token returns for AsyncAttachments")
}
return *res.Token, nil
}
func (bridge *Bridge) appendComment(ctx context.Context, token string, att proton.ReportBugAttachment) error {
var attachments []proton.ReportBugAttachment
attachments = append(attachments, att)
return bridge.api.ReportBugAttachement(ctx, proton.ReportBugAttachmentReq{
Product: proton.ClientTypeEmail,
Body: "Comment adding attachment: " + att.Filename,
Token: token,
}, attachments...)
} }

View File

@ -339,18 +339,17 @@ func (s *Service) ReportBug(_ context.Context, report *ReportBugRequest) (*empty
defer async.HandlePanic(s.panicHandler) defer async.HandlePanic(s.panicHandler)
defer func() { _ = s.SendEvent(NewReportBugFinishedEvent()) }() defer func() { _ = s.SendEvent(NewReportBugFinishedEvent()) }()
reportReq := bridge.ReportBugReq{
if err := s.bridge.ReportBug( OSType: report.OsType,
context.Background(), OSVersion: report.OsVersion,
report.OsType, Title: report.Title,
report.OsVersion, Description: report.Description,
report.Title, Username: report.Address,
report.Description, Email: report.Address,
report.Address, EmailClient: report.EmailClient,
report.Address, IncludeLogs: report.IncludeLogs,
report.EmailClient, }
report.IncludeLogs, if err := s.bridge.ReportBug(context.Background(), &reportReq); err != nil {
); err != nil {
s.log.WithError(err).Error("Failed to report bug") s.log.WithError(err).Error("Failed to report bug")
_ = s.SendEvent(NewReportBugErrorEvent()) _ = s.SendEvent(NewReportBugErrorEvent())
return return

View File

@ -155,34 +155,29 @@ func (s *scenario) theUserSetSMTPModeToSSL() error {
} }
type testBugReport struct { type testBugReport struct {
OSType string `json:"OS"` request bridge.ReportBugReq
OSVersion string `json:"OSVersion"`
Title string `json:"Title"`
Description string `json:"Description"`
Username string `json:"Username"`
Email string `json:"Email"`
Client string `json:"Client"`
Attachment bool `json:"Attachment"`
bridge *bridge.Bridge bridge *bridge.Bridge
} }
func newTestBugReport(bridge *bridge.Bridge) *testBugReport { func newTestBugReport(br *bridge.Bridge) *testBugReport {
return &testBugReport{ request := bridge.ReportBugReq{
OSType: "osType", OSType: "osType",
OSVersion: "osVersion", OSVersion: "osVersion",
Title: "title", Title: "title",
Description: "description", Description: "description",
Username: "username", Username: "username",
Email: "email", Email: "email",
Client: "client", EmailClient: "client",
Attachment: false, IncludeLogs: false,
bridge: bridge, }
return &testBugReport{
request: request,
bridge: br,
} }
} }
func (r *testBugReport) report() error { func (r *testBugReport) report() error {
return r.bridge.ReportBug(context.Background(), r.OSType, r.OSVersion, r.Title, r.Description, r.Username, r.Email, r.Client, r.Attachment) return r.bridge.ReportBug(context.Background(), &r.request)
} }
func (s *scenario) theUserReportsABug() error { func (s *scenario) theUserReportsABug() error {
@ -194,25 +189,25 @@ func (s *scenario) theUserReportsABugWithSingleHeaderChange(key, value string) e
switch key { switch key {
case "osType": case "osType":
bugReport.OSType = value bugReport.request.OSType = value
case "osVersion": case "osVersion":
bugReport.OSVersion = value bugReport.request.OSVersion = value
case "Title": case "Title":
bugReport.Title = value bugReport.request.Title = value
case "Description": case "Description":
bugReport.Description = value bugReport.request.Description = value
case "Username": case "Username":
bugReport.Username = value bugReport.request.Username = value
case "Email": case "Email":
bugReport.Email = value bugReport.request.Email = value
case "Client": case "Client":
bugReport.Client = value bugReport.request.EmailClient = value
case "Attachment": case "Attachment":
att, err := strconv.ParseBool(value) att, err := strconv.ParseBool(value)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse bug report attachment preferences: %w", err) return fmt.Errorf("failed to parse bug report attachment preferences: %w", err)
} }
bugReport.Attachment = att bugReport.request.IncludeLogs = att
default: default:
return fmt.Errorf("Wrong header (\"%s\") is being checked", key) return fmt.Errorf("Wrong header (\"%s\") is being checked", key)
} }
@ -222,10 +217,9 @@ func (s *scenario) theUserReportsABugWithSingleHeaderChange(key, value string) e
func (s *scenario) theUserReportsABugWithDetails(value *godog.DocString) error { func (s *scenario) theUserReportsABugWithDetails(value *godog.DocString) error {
bugReport := newTestBugReport(s.t.bridge) bugReport := newTestBugReport(s.t.bridge)
if err := json.Unmarshal([]byte(value.Content), &bugReport); err != nil { if err := json.Unmarshal([]byte(value.Content), &bugReport.request); err != nil {
return fmt.Errorf("cannot parse bug report details: %w", err) return fmt.Errorf("cannot parse bug report details: %w", err)
} }
return bugReport.report() return bugReport.report()
} }

View File

@ -41,7 +41,7 @@ Feature: The user reports a problem
"Description": "Testing Description", "Description": "Testing Description",
"Username": "[user:user]", "Username": "[user:user]",
"Email": "[user:user]@[domain]", "Email": "[user:user]@[domain]",
"Client": "Apple Mail" "EmailClient": "Apple Mail"
} }
""" """
Then the header in the "POST" multipart request to "/core/v4/reports/bug" has "Title" set to "[Bridge] Bug - Testing Title" Then the header in the "POST" multipart request to "/core/v4/reports/bug" has "Title" set to "[Bridge] Bug - Testing Title"