Skip to content

Commit

Permalink
updated client and support no used Id
Browse files Browse the repository at this point in the history
Signed-off-by: Guillem Bonet <guillem@mysterium.network>
  • Loading branch information
Guillembonet committed Dec 28, 2021
1 parent b121442 commit e0875c9
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
build/
vendor/
.env
.vscode/
91 changes: 91 additions & 0 deletions client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ func (f *FeedbackAPI) CreateGithubIssue(request CreateGithubIssueRequest) (respo
return parseCreateGithubIssueResult(resp)
}

// CreateIntercomIssue creates a intercom issue
func (f *FeedbackAPI) CreateIntercomIssue(request CreateIntercomIssueRequest) (response *CreateIntercomIssueResult, err error) {
multipartReq, err := newCreateIntercomIssueRequest(f.apiURL("/intercom"), request)
if err != nil {
// For now not using fmt.Errorf with %w for compatibility with go <1.13
return nil, errors.Wrap(err, "could not create multipart request")
}

resp, err := f.http.Do(multipartReq)
if err != nil {
return nil, errors.Wrap(err, "failed request to feedback service")
}
return parseCreateIntercomIssueResult(resp)
}

func newCreateGithubIssueRequest(uri string, req CreateGithubIssueRequest) (multipartReq *http.Request, err error) {
fileContent, err := ioutil.ReadFile(req.Filepath)
if err != nil {
Expand Down Expand Up @@ -88,6 +103,40 @@ func newCreateGithubIssueRequest(uri string, req CreateGithubIssueRequest) (mult
return request, err
}

func newCreateIntercomIssueRequest(uri string, req CreateIntercomIssueRequest) (multipartReq *http.Request, err error) {
fileContent, err := ioutil.ReadFile(req.Filepath)
if err != nil {
return nil, errors.Wrap(err, "could not read input file")
}
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", req.Filepath)
if err != nil {
return nil, errors.Wrap(err, "could not add file to request")
}
_, err = part.Write(fileContent)
if err != nil {
return nil, errors.Wrap(err, "could not write file part")
}

_ = writer.WriteField("userId", req.UserId)
_ = writer.WriteField("description", req.Description)
_ = writer.WriteField("email", req.Email)
_ = writer.WriteField("nodeIdentity", req.UserId)
_ = writer.WriteField("userType", req.Description)
_ = writer.WriteField("ipType", req.Email)
_ = writer.WriteField("ip", req.Email)
_ = writer.Close()

request, err := http.NewRequest("POST", uri, body)
if err != nil {
return nil, errors.Wrap(err, "could not create request")
}

request.Header.Set("Content-Type", writer.FormDataContentType())
return request, err
}

// CreateGithubIssueRequest create github issue request
type CreateGithubIssueRequest struct {
UserId string `json:"userId"`
Expand All @@ -96,6 +145,19 @@ type CreateGithubIssueRequest struct {
Filepath string `json:"file"`
}

// CreateIntercomIssueRequest create intercom issue request
type CreateIntercomIssueRequest struct {
UserId string `json:"userId"`
Description string `json:"description"`
Email string `json:"email"`
Filepath string `json:"file"`
NodeIdentity string `json:"nodeIdentity"`
UserType string `json:"userType"`
NodeCountry string `json:"nodeCountry"`
IpType string `json:"ipType"`
Ip string `json:"ip"`
}

// CreateGithubIssueResult represents create github issue response (either successful or error)
type CreateGithubIssueResult struct {
Response *GithubIssueCreated
Expand All @@ -104,6 +166,13 @@ type CreateGithubIssueResult struct {
Success bool
}

// CreateIntercomIssueResult represents create intercom issue response (either successful or error)
type CreateIntercomIssueResult struct {
Errors *ErrorResponse
HTTPResponse *http.Response
Success bool
}

// GithubIssueCreated represents successful response (issue created)
type GithubIssueCreated struct {
IssueId string `json:"issueId"`
Expand Down Expand Up @@ -137,3 +206,25 @@ func parseCreateGithubIssueResult(httpRes *http.Response) (*CreateGithubIssueRes
}
return res, nil
}

func parseCreateIntercomIssueResult(httpRes *http.Response) (*CreateIntercomIssueResult, error) {
res := &CreateIntercomIssueResult{HTTPResponse: httpRes}
resJSON, err := ioutil.ReadAll(httpRes.Body)
if err != nil {
res.Success = false
res.Errors = singleErrorResponse("could not parse feedback response: " + err.Error())
return res, err
}
if res.HTTPResponse.StatusCode >= 400 {
res.Success = false
res.Errors = &ErrorResponse{[]Error{}}
err = json.Unmarshal(resJSON, res.Errors)
if err != nil {
res.Errors = singleErrorResponse("could not parse feedback response: " + err.Error())
return res, err
}
} else {
res.Success = true
}
return res, nil
}
7 changes: 3 additions & 4 deletions feedback/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ type CreateIntercomIssueRequest struct {
// required: true
Description string `json:"description"`
// in: formData
// required: false
Email string `json:"email"`
// in: formData
// required: true
Expand Down Expand Up @@ -293,7 +294,7 @@ func (e *Endpoint) CreateIntercomIssue(c *gin.Context) {
// return
// }

issueId, err := e.intercomReporter.ReportIssue(&IntercomReport{
err = e.intercomReporter.ReportIssue(&IntercomReport{
UserId: form.UserId,
NodeIdentity: form.NodeIdentity,
UserType: form.UserType,
Expand All @@ -312,9 +313,7 @@ func (e *Endpoint) CreateIntercomIssue(c *gin.Context) {
}

log.Infof("Created intercom conversation from request %+v", form)
c.JSON(http.StatusOK, &CreateGithubIssueResponse{
IssueId: issueId,
})
c.Status(http.StatusCreated)
}

// RegisterRoutes registers feedback API routes
Expand Down
164 changes: 98 additions & 66 deletions feedback/intercom.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"text/template"
"time"
Expand All @@ -34,6 +33,11 @@ import (

const (
INTERCOM_BASE_API = "https://api.intercom.io"
USER_ROLE_KEY = "user_role"
NODE_IDENTITY_KEY = "node_identity"
NODE_COUNTRY_KEY = "node_country"
IP_TYPE_KEY = "ip_type"
IP_KEY = "ip"
)

// IntercomReporter reports issues to Intercom
Expand Down Expand Up @@ -86,7 +90,7 @@ Logs:
`

// ReportIssue creates a issue message for the user in intercom
func (rep *IntercomReporter) ReportIssue(report *IntercomReport) (issueId string, err error) {
func (rep *IntercomReporter) ReportIssue(report *IntercomReport) error {
templateOpts := struct {
Description,
Timestamp,
Expand All @@ -97,91 +101,118 @@ func (rep *IntercomReporter) ReportIssue(report *IntercomReport) (issueId string
LogURL: report.LogURL.String(),
}
var body bytes.Buffer
err = rep.messageTemplate.Execute(&body, templateOpts)
err := rep.messageTemplate.Execute(&body, templateOpts)
if err != nil {
return "", fmt.Errorf("could not generate message body with report (%+v): %w", templateOpts, err)
return fmt.Errorf("could not generate message body with report (%+v): %w", templateOpts, err)
}

// try update visitor
err = rep.updateVisitor(report.UserId, &updateVisitorRequest{
Email: report.Email,
CustomAttributes: updateVisitorRequestCustomAttributes{
NodeIdentity: report.NodeIdentity,
IsConsumer: (strings.ToLower(report.UserType) == "consumer"),
NodeCountry: report.NodeCountry,
IpType: report.IpType,
Ip: report.Ip,
},
})
if err != nil {
log.Warn().Msg("could not update visitor")
}
visitorUpdated := (err == nil)

// try update contact
contactUpdated := false
if !visitorUpdated {
contact, err := rep.client.Contacts.FindByUserID(report.UserId)
if report.UserId != "" {
// try update visitor (will become lead)
err = rep.updateVisitor(report.UserId, &updateVisitorRequest{
Email: report.Email,
CustomAttributes: updateVisitorRequestCustomAttributes{
NodeIdentity: report.NodeIdentity,
IsConsumer: (strings.ToLower(report.UserType) == "consumer"),
NodeCountry: report.NodeCountry,
IpType: report.IpType,
Ip: report.Ip,
},
})
if err != nil {
log.Warn().Msg("could not update contact")
log.Warn().Msgf("could not update visitor %s\n", report.UserId)
}
if err == nil {
contact.Email = report.Email
contact.CustomAttributes["node_identity"] = report.NodeIdentity
contact.CustomAttributes["node_country"] = report.NodeCountry
contact.CustomAttributes["is_consumer"] = (strings.ToLower(report.UserType) == "consumer")
contact.CustomAttributes["ip_type"] = report.IpType
contact.CustomAttributes["ip"] = report.Ip
_, err := rep.client.Contacts.Update(&contact)
visitorUpdated := (err == nil)

// try update contact
contactUpdated := false
if !visitorUpdated {
contact, err := rep.client.Contacts.FindByUserID(report.UserId)
if err != nil {
return "", fmt.Errorf("could not update contact (%s): %w", contact.ID, err)
log.Warn().Msgf("could not update contact %s\n", report.UserId)
}
if err == nil {
contact.Email = report.Email
contact.CustomAttributes[NODE_IDENTITY_KEY] = report.NodeIdentity
contact.CustomAttributes[NODE_COUNTRY_KEY] = report.NodeCountry
contact.CustomAttributes[USER_ROLE_KEY] = report.UserType
contact.CustomAttributes[IP_TYPE_KEY] = report.IpType
contact.CustomAttributes[IP_KEY] = report.Ip
_, err := rep.client.Contacts.Update(&contact)
if err != nil {
return fmt.Errorf("could not update contact (%s): %w", contact.ID, err)
}
contactUpdated = true
}
contactUpdated = true
}
}
// try update user
userUpdated := false
if !visitorUpdated && !contactUpdated {
user, err := rep.client.Users.FindByUserID(report.UserId)
if err != nil {
log.Warn().Msg("could not update user")
}
if err == nil {
user.Email = report.Email
user.CustomAttributes["node_identity"] = report.NodeIdentity
user.CustomAttributes["node_country"] = report.NodeCountry
user.CustomAttributes["is_consumer"] = (strings.ToLower(report.UserType) == "consumer")
user.CustomAttributes["ip_type"] = report.IpType
user.CustomAttributes["ip"] = report.Ip
_, err := rep.client.Users.Save(&user)
// try update user
userUpdated := false
if !visitorUpdated && !contactUpdated {
user, err := rep.client.Users.FindByUserID(report.UserId)
if err != nil {
return "", fmt.Errorf("could not update user (%s): %w", user.ID, err)
log.Warn().Msgf("could not update user %s\n", report.UserId)
}
if err == nil {
user.Email = report.Email
user.CustomAttributes[NODE_IDENTITY_KEY] = report.NodeIdentity
user.CustomAttributes[NODE_COUNTRY_KEY] = report.NodeCountry
user.CustomAttributes[USER_ROLE_KEY] = report.UserType
user.CustomAttributes[IP_TYPE_KEY] = report.IpType
user.CustomAttributes[IP_KEY] = report.Ip
_, err := rep.client.Users.Save(&user)
if err != nil {
return fmt.Errorf("could not update user (%s): %w", user.ID, err)
}
userUpdated = true
}
userUpdated = true
}
}

if !visitorUpdated && !contactUpdated && !userUpdated {
return "", fmt.Errorf("could not update visitor, contact or user (%s): %w", report.UserId, err)
if !visitorUpdated && !contactUpdated && !userUpdated {
return fmt.Errorf("could not update visitor, contact or user (%s): %w", report.UserId, err)
}

userType := "contact"
if userUpdated {
userType = "user"
}

err = rep.createConversation(&createConversationRequest{
From: createConversationRequestFrom{
UserType: userType,
UserId: &report.UserId,
},
Body: body.String(),
})
if err != nil {
return fmt.Errorf("could not create conversation for user (%s): %w", report.UserId, err)
}
return nil
}

userType := "contact"
if userUpdated {
userType = "user"
contact, err := rep.client.Contacts.Create(&intercom.Contact{
Email: report.Email,
CustomAttributes: map[string]interface{}{
NODE_IDENTITY_KEY: report.NodeIdentity,
NODE_COUNTRY_KEY: report.NodeCountry,
USER_ROLE_KEY: report.UserType,
IP_TYPE_KEY: report.IpType,
IP_KEY: report.Ip,
},
})
if err != nil {
return fmt.Errorf("could not create contact: %w", err)
}

err = rep.createConversation(&createConversationRequest{
From: createConversationRequestFrom{
UserType: userType,
UserId: report.UserId,
UserType: "contact",
Id: &contact.ID,
},
Body: body.String(),
})
if err != nil {
return "", fmt.Errorf("could not create conversation for user (%s): %w", report.UserId, err)
return fmt.Errorf("could not create conversation for user with id (%s): %w", contact.ID, err)
}

return strconv.Itoa(123), nil
return nil
}

type updateVisitorRequestCustomAttributes struct {
Expand Down Expand Up @@ -221,8 +252,9 @@ func (rep *IntercomReporter) updateVisitor(userId string, updateVisitorRequest *
}

type createConversationRequestFrom struct {
UserType string `json:"type"`
UserId string `json:"user_id"`
UserType string `json:"type"`
UserId *string `json:"user_id"`
Id *string `json:"id"`
}

type createConversationRequest struct {
Expand Down

0 comments on commit e0875c9

Please sign in to comment.