Skip to content

Commit

Permalink
Merge pull request #198 from weni-ai/feat/merge-users-zendesk
Browse files Browse the repository at this point in the history
Merge users zendesk
  • Loading branch information
Robi9 authored Dec 10, 2024
2 parents 43e8498 + 57cc750 commit 7887361
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 18 deletions.
10 changes: 8 additions & 2 deletions core/handlers/ticket_opened_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ func TestTicketOpened(t *testing.T) {
"message": "Queued. Thank you."
}`),
},
"https://nyaruka.zendesk.com/api/v2/users/search.json?external_id=b699a406-7e44-49be-9f01-1a82893e8a10": {
"https://nyaruka.zendesk.com/api/v2/users/search.json?query=b699a406-7e44-49be-9f01-1a82893e8a10": {
httpx.NewMockResponse(200, nil, `{"users": []}`),
},
"https://nyaruka.zendesk.com/api/v2/users/search.json?query=type:user details:\"b699a406-7e44-49be-9f01-1a82893e8a10\"": {
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
},
"https://nyaruka.zendesk.com/api/v2/users/35241/merge": {
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
},
"https://nyaruka.zendesk.com/api/v2/users.json": {
httpx.NewMockResponse(201, nil, `{
"user": {
Expand Down Expand Up @@ -110,7 +116,7 @@ func TestTicketOpened(t *testing.T) {
{ // and there's an HTTP log for that
SQL: "select count(*) from request_logs_httplog where ticketer_id = $1",
Args: []interface{}{testdata.Zendesk.ID},
Count: 3,
Count: 5,
},
{ // which doesn't include our API token
SQL: "select count(*) from request_logs_httplog where ticketer_id = $1 AND request like '%523562%'",
Expand Down
38 changes: 30 additions & 8 deletions services/tickets/zendesk/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -316,13 +317,15 @@ type User struct {
Organization struct {
Name string `json:"name"`
} `json:"organization,omitempty"`
ExternalID string `json:"external_id,omitempty"`
Identities []struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"identities,omitempty"`
Verified bool `json:"verified,omitempty"`
Role string `json:"role,omitempty"`
ExternalID string `json:"external_id,omitempty"`
Identities []Identity `json:"identities,omitempty"`
Verified bool `json:"verified,omitempty"`
Role string `json:"role,omitempty"`
}

type Identity struct {
Type string `json:"type"`
Value string `json:"value"`
}

// CreateUser creates a new user in zendesk
Expand All @@ -349,7 +352,7 @@ type SearchUserResponse struct {

// SearchUser returns the user with the given external ID or nil if it doesn't exist
func (c *RESTClient) SearchUser(externalID string) (*User, *httpx.Trace, error) {
endpoint := fmt.Sprintf("users/search.json?external_id=%s", externalID)
endpoint := fmt.Sprintf("users/search.json?query=%s", externalID)
var response SearchUserResponse
trace, err := c.get(endpoint, nil, &response)
if err != nil {
Expand All @@ -360,3 +363,22 @@ func (c *RESTClient) SearchUser(externalID string) (*User, *httpx.Trace, error)
}
return &response.Users[0], trace, nil
}

// MergeUser merge two users
func (c *RESTClient) MergeUser(userID int64, unmergedUserID int64) (*User, *httpx.Trace, error) {
endpoint := fmt.Sprintf("users/%s/merge", strconv.FormatInt(unmergedUserID, 10))

payload := struct {
User *User `json:"user"`
}{User: &User{ID: userID}}

response := &struct {
User *User `json:"user"`
}{}

trace, err := c.post(endpoint, payload, &response)
if err != nil {
return nil, trace, err
}
return response.User, trace, nil
}
2 changes: 1 addition & 1 deletion services/tickets/zendesk/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func TestSearchUser(t *testing.T) {
userUUID := "9a327960-b2ce-4659-bef7-87cbb837a154"

httpx.SetRequestor(httpx.NewMockRequestor(map[string][]httpx.MockResponse{
fmt.Sprintf("https://mocked_subdomain.zendesk.com/api/v2/users/search.json?external_id=%s", userUUID): {
fmt.Sprintf("https://mocked_subdomain.zendesk.com/api/v2/users/search.json?query=%s", userUUID): {
httpx.MockConnectionError,
httpx.NewMockResponse(400, nil, `{"description": "Something went wrong", "error": "Unknown"}`),
httpx.NewMockResponse(200, nil, `{"users": []}`),
Expand Down
39 changes: 37 additions & 2 deletions services/tickets/zendesk/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ func (s *service) Open(session flows.Session, topic *flows.Topic, body string, a
contactDisplay := session.Contact().Format(session.Environment())
contactUUID := string(session.Contact().UUID())

var phoneNumber string
urn := session.Contact().PreferredURN().URN()
if urn.Scheme() == "whatsapp" {
phoneNumber = string(session.Contact().PreferredURN().URN().Path())
}

user, trace, err := s.restClient.SearchUser(contactUUID)
if trace != nil {
logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor))
Expand All @@ -91,13 +97,19 @@ func (s *service) Open(session flows.Session, topic *flows.Topic, body string, a
return nil, err
}
if trace.Response.StatusCode == http.StatusNotFound || user == nil {
user := &User{
newUser := &User{
Name: contactDisplay,
ExternalID: contactUUID,
Verified: true,
Role: "end-user",
Identities: []Identity{},
}

if len(phoneNumber) > 0 {
newUser.Identities = append(newUser.Identities, Identity{Type: "phone_number", Value: phoneNumber})
}
_, trace, err = s.restClient.CreateUser(user)

user, trace, err = s.restClient.CreateUser(newUser)
if trace != nil {
logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor))
}
Expand All @@ -113,6 +125,12 @@ func (s *service) Open(session flows.Session, topic *flows.Topic, body string, a
Author: Author{
ExternalID: contactUUID,
Name: contactDisplay,
Fields: []FieldValue{
{
ID: "details",
Value: contactUUID,
},
},
},
AllowChannelback: true,
}
Expand Down Expand Up @@ -174,6 +192,23 @@ func (s *service) Open(session flows.Session, topic *flows.Topic, body string, a
if err := s.push(msg, logHTTP); err != nil {
return nil, err
}

unmergedUser, trace, err := s.restClient.SearchUser("type:user details:\"" + contactUUID + "\"")
if trace != nil {
logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor))
}
if err != nil && trace.Response.StatusCode != http.StatusNotFound {
return nil, err
}

_, trace, err = s.restClient.MergeUser(user.ID, unmergedUser.ID)
if trace != nil {
logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor))
}
if err != nil && trace.Response.StatusCode != http.StatusNotFound {
return nil, err
}

return ticket, nil
}

Expand Down
14 changes: 12 additions & 2 deletions services/tickets/zendesk/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,17 @@ func TestOpenAndForward(t *testing.T) {
uuids.SetGenerator(uuids.NewSeededGenerator(12345))
dates.SetNowSource(dates.NewSequentialNowSource(time.Date(2019, 10, 7, 15, 21, 30, 0, time.UTC)))
httpx.SetRequestor(httpx.NewMockRequestor(map[string][]httpx.MockResponse{
"https://nyaruka.zendesk.com/api/v2/users/search.json?external_id=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f": {
"https://nyaruka.zendesk.com/api/v2/users/search.json?query=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f": {
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
},
"https://nyaruka.zendesk.com/api/v2/users/search.json?query=type:user details:\"5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f\"": {
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
},
"https://nyaruka.zendesk.com/api/v2/users/35241/merge": {
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
httpx.NewMockResponse(200, nil, `{"users": [{"id": 35241, "name": "Dummy User"}], "count": 1, "next_page": "https://nyaruka.zendesk.com/api/v2/users.json?page=2"}`),
Expand Down Expand Up @@ -128,7 +138,7 @@ func TestOpenAndForward(t *testing.T) {
assert.Equal(t, "General", ticket.Topic().Name())
assert.Equal(t, fieldTicket, ticket.Body())
assert.Equal(t, "", ticket.ExternalID())
assert.Equal(t, 2, len(logger.Logs))
assert.Equal(t, 4, len(logger.Logs))
test.AssertSnapshot(t, "open_ticket", logger.Logs[0].Request)

dbTicket := models.NewTicket(ticket.UUID(), testdata.Org1.ID, testdata.Cathy.ID, testdata.Zendesk.ID, "", testdata.DefaultTopic.ID, "Where are my cookies?", models.NilUserID, map[string]interface{}{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ Authorization: Bearer ****************
Content-Type: application/json
Accept-Encoding: gzip

{"instance_push_id":"1234-abcd","request_id":"sesame:1570461704000000000","external_resources":[{"external_id":"ca5607f0-cba8-4c94-9cd5-c4fbc24aa767","message":"It's urgent","thread_id":"59d74b86-3e2f-4a93-aece-b05d2fdcde0c","created_at":"2019-10-07T15:21:43Z","author":{"external_id":"6393abc0-283d-4c9b-a1b3-641a035c34bf","name":"Cathy"},"allow_channelback":true,"file_urls":["https:///api/v2/file/0123/attachment1.jpg"]}]}
{"instance_push_id":"1234-abcd","request_id":"sesame:1570461708000000000","external_resources":[{"external_id":"ca5607f0-cba8-4c94-9cd5-c4fbc24aa767","message":"It's urgent","thread_id":"59d74b86-3e2f-4a93-aece-b05d2fdcde0c","created_at":"2019-10-07T15:21:47Z","author":{"external_id":"6393abc0-283d-4c9b-a1b3-641a035c34bf","name":"Cathy"},"allow_channelback":true,"file_urls":["https:///api/v2/file/0123/attachment1.jpg"]}]}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GET /api/v2/users/search.json?external_id=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f HTTP/1.1
GET /api/v2/users/search.json?query=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f HTTP/1.1
Host: nyaruka.zendesk.com
User-Agent: Go-http-client/1.1
Authorization: Bearer ****************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GET /api/v2/users/search.json?external_id=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f HTTP/1.1
GET /api/v2/users/search.json?query=5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f HTTP/1.1
Host: nyaruka.zendesk.com
User-Agent: Go-http-client/1.1
Authorization: Bearer ****************
Expand Down

0 comments on commit 7887361

Please sign in to comment.