Skip to content

Commit

Permalink
Merge pull request #75 from Ilhasoft/wenichats-integration
Browse files Browse the repository at this point in the history
add wenichats ticketer integration
  • Loading branch information
rasoro authored Oct 7, 2022
2 parents 3018118 + 168735e commit 40fa388
Show file tree
Hide file tree
Showing 15 changed files with 1,423 additions and 10 deletions.
1 change: 1 addition & 0 deletions cmd/mailroom/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
_ "github.com/nyaruka/mailroom/services/tickets/mailgun"
_ "github.com/nyaruka/mailroom/services/tickets/rocketchat"
_ "github.com/nyaruka/mailroom/services/tickets/twilioflex"
_ "github.com/nyaruka/mailroom/services/tickets/wenichats"
_ "github.com/nyaruka/mailroom/services/tickets/zendesk"
_ "github.com/nyaruka/mailroom/web/contact"
_ "github.com/nyaruka/mailroom/web/docs"
Expand Down
18 changes: 18 additions & 0 deletions core/models/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,3 +607,21 @@ func insertTestSession(t *testing.T, ctx context.Context, rt *runtime.Runtime, o

return session
}

func TestSelectContactMessages(t *testing.T) {
ctx, _, db, _ := testsuite.Get()
defer testsuite.Reset(testsuite.ResetData)

thisMoment := time.Now()

testdata.InsertIncomingMsg(db, testdata.Org1, testdata.TwilioChannel, testdata.Cathy, "in 1", models.MsgStatusHandled)
testdata.InsertOutgoingMsg(db, testdata.Org1, testdata.TwilioChannel, testdata.Cathy, "out 1", []utils.Attachment{"image/jpeg:hi.jpg"}, models.MsgStatusSent, false)
testdata.InsertOutgoingMsg(db, testdata.Org1, testdata.TwilioChannel, testdata.Cathy, "out 2", nil, models.MsgStatusSent, false)
testdata.InsertOutgoingMsg(db, testdata.Org2, testdata.Org2Channel, testdata.Org2Contact, "out 3", nil, models.MsgStatusSent, false)

msgs, err := models.SelectContactMessages(ctx, db, int(testdata.Cathy.ID), thisMoment)

// shoud only return messages for testdata.Cathy
assert.NoError(t, err)
assert.Equal(t, 3, len(msgs))
}
Binary file modified mailroom_test.dump
Binary file not shown.
22 changes: 12 additions & 10 deletions runtime/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ type Config struct {
FCMKey string `help:"the FCM API key used to notify Android relayers to sync"`
MailgunSigningKey string `help:"the signing key used to validate requests from mailgun"`

InstanceName string `help:"the unique name of this instance used for analytics"`
LogLevel string `help:"the logging level courier should use"`
UUIDSeed int `help:"seed to use for UUID generation in a testing environment"`
Version string `help:"the version of this mailroom install"`
TimeoutTime int `help:"the amount of time to between every timeout queued"`
InstanceName string `help:"the unique name of this instance used for analytics"`
LogLevel string `help:"the logging level courier should use"`
UUIDSeed int `help:"seed to use for UUID generation in a testing environment"`
Version string `help:"the version of this mailroom install"`
TimeoutTime int `help:"the amount of time to between every timeout queued"`
WenichatsServiceURL string `help:"wenichats external api url for ticketer service integration"`
}

// NewDefaultConfig returns a new default configuration object
Expand Down Expand Up @@ -118,11 +119,12 @@ func NewDefaultConfig() *Config {
AWSAccessKeyID: "",
AWSSecretAccessKey: "",

InstanceName: hostname,
LogLevel: "error",
UUIDSeed: 0,
Version: "Dev",
TimeoutTime: 15,
InstanceName: hostname,
LogLevel: "error",
UUIDSeed: 0,
Version: "Dev",
TimeoutTime: 15,
WenichatsServiceURL: "https://chats-engine.dev.cloud.weni.ai/v1/external",
}
}

Expand Down
243 changes: 243 additions & 0 deletions services/tickets/wenichats/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package wenichats

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"

"github.com/nyaruka/gocommon/httpx"
"github.com/nyaruka/gocommon/jsonx"
"github.com/pkg/errors"
)

type baseClient struct {
httpClient *http.Client
httpRetries *httpx.RetryConfig
authToken string
baseURL string
}

func newBaseClient(httpClient *http.Client, httpRetries *httpx.RetryConfig, baseURL, authToken string) baseClient {

return baseClient{
httpClient: httpClient,
httpRetries: httpRetries,
authToken: authToken,
baseURL: baseURL,
}
}

type errorResponse struct {
Detail string `json:"detail"`
}

func (c *baseClient) request(method, url string, params *url.Values, payload, response interface{}) (*httpx.Trace, error) {
pjson, err := json.Marshal(payload)
if err != nil {
return nil, err
}
data := strings.NewReader(string(pjson))
req, err := httpx.NewRequest(method, url, data, nil)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+c.authToken)

if params != nil {
req.URL.RawQuery = params.Encode()
}

trace, err := httpx.DoTrace(c.httpClient, req, c.httpRetries, nil, -1)
if err != nil {
return trace, err
}

if trace.Response.StatusCode >= 400 {
response := &errorResponse{}
err = jsonx.Unmarshal(trace.ResponseBody, response)
if err != nil {
return trace, errors.Wrap(err, "couldn't parse error response")
}
return trace, errors.New(response.Detail)
}

if response != nil {
err = json.Unmarshal(trace.ResponseBody, response)
return trace, errors.Wrap(err, "couldn't parse response body")
}

return trace, nil
}

func (c *baseClient) post(url string, payload, response interface{}) (*httpx.Trace, error) {
return c.request("POST", url, nil, payload, response)
}

func (c *baseClient) get(url string, params *url.Values, response interface{}) (*httpx.Trace, error) {
return c.request("GET", url, params, nil, response)
}

func (c *baseClient) patch(url string, params *url.Values, payload, response interface{}) (*httpx.Trace, error) {
return c.request("PATCH", url, nil, payload, response)
}

type Client struct {
baseClient
}

func NewClient(httpClient *http.Client, httpRetries *httpx.RetryConfig, baseURL, authToken string) *Client {
return &Client{
baseClient: newBaseClient(httpClient, httpRetries, baseURL, authToken),
}
}

func (c *Client) CreateRoom(room *RoomRequest) (*RoomResponse, *httpx.Trace, error) {
url := c.baseURL + "/rooms/"
response := &RoomResponse{}
trace, err := c.post(url, room, response)
if err != nil {
return nil, trace, err
}
return response, trace, nil
}

func (c *Client) UpdateRoom(roomUUID string, room *RoomRequest) (*RoomResponse, *httpx.Trace, error) {
url := fmt.Sprintf("%s/rooms/%s/", c.baseURL, roomUUID)
response := &RoomResponse{}
trace, err := c.patch(url, nil, room, response)
if err != nil {
return nil, trace, err
}
return response, trace, nil
}

func (c *Client) CloseRoom(roomUUID string) (*RoomResponse, *httpx.Trace, error) {
url := fmt.Sprintf("%s/rooms/%s/close/", c.baseURL, roomUUID)
response := &RoomResponse{}
trace, err := c.patch(url, nil, nil, response)
if err != nil {
return nil, trace, err
}
return response, trace, nil
}

func (c *Client) CreateMessage(msg *MessageRequest) (*MessageResponse, *httpx.Trace, error) {
url := fmt.Sprintf("%s/msgs/", c.baseURL)
response := &MessageResponse{}
trace, err := c.post(url, msg, response)
if err != nil {
return nil, trace, err
}
return response, trace, nil
}

func (c *Client) GetQueues(params *url.Values) (*QueuesResponse, *httpx.Trace, error) {
url := fmt.Sprintf("%s/queues/", c.baseURL)
response := &QueuesResponse{}
trace, err := c.get(url, params, response)
if err != nil {
return nil, trace, err
}
return response, trace, nil
}

type RoomRequest struct {
QueueUUID string `json:"queue_uuid,omitempty"`
UserEmail string `json:"user_email,omitempty"`
SectorUUID string `json:"sector_uuid,omitempty"`
Contact *Contact `json:"contact,omitempty"`
CreatedOn *time.Time `json:"created_on,omitempty"`
CustomFields map[string]interface{} `json:"custom_fields,omitempty"`
CallbackURL string `json:"callback_url,omitempty"`
}

type Contact struct {
ExternalID string `json:"external_id,omitempty"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Phone string `json:"phone,omitempty"`
CustomFields map[string]interface{} `json:"custom_fields,omitempty"`
}

type RoomResponse struct {
UUID string `json:"uuid"`
User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
} `json:"user"`
Contact struct {
ExternalID string `json:"external_id"`
Name string `json:"name"`
Email string `json:"email"`
Status string `json:"status"`
Phone string `json:"phone"`
CustomFields map[string]interface{} `json:"custom_fields"`
CreatedOn time.Time `json:"created_on"`
} `json:"contact"`
Queue struct {
UUID string `json:"uuid"`
CreatedOn time.Time `json:"created_on"`
ModifiedOn time.Time `json:"modified_on"`
Name string `json:"name"`
Sector string `json:"sector"`
} `json:"queue"`
CreatedOn time.Time `json:"created_on"`
ModifiedOn time.Time `json:"modified_on"`
IsActive bool `json:"is_active"`
CustomFields map[string]interface{} `json:"custom_fields"`
CallbackURL string `json:"callback_url"`
}

type MessageRequest struct {
Room string `json:"room"`
Text string `json:"text"`
CreatedOn time.Time `json:"created_on"`
Direction string `json:"direction"`
Attachments []Attachment `json:"attachments"`
}

type MessageResponse struct {
UUID string `json:"uuid"`
User interface{} `json:"user"`
Room string `json:"room"`
Contact struct {
UUID string `json:"uuid"`
Name string `json:"name"`
Email string `json:"email"`
Status string `json:"status"`
Phone string `json:"phone"`
CustomFields struct {
} `json:"custom_fields"`
CreatedOn time.Time `json:"created_on"`
} `json:"contact"`
Text string `json:"text"`
Seen bool `json:"seen"`
Media []Attachment `json:"media"`
CreatedOn string `json:"created_on"`
}

type Attachment struct {
ContentType string `json:"content_type"`
URL string `json:"url"`
}

type baseResponse struct {
Count int `json:"count"`
Next string `json:"next"`
Previous string `json:"previous"`
}

type QueuesResponse struct {
baseResponse
Results []Queue `json:"results"`
}

type Queue struct {
UUID string `json:"uuid"`
Name string `json:"name"`
}
Loading

0 comments on commit 40fa388

Please sign in to comment.