feat(inboxes): deepen channel config parity
This commit is contained in:
@@ -2,6 +2,7 @@ package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -220,7 +221,7 @@ func (h *InboxHandler) Delete(c *gin.Context) {
|
||||
|
||||
func bindCreateInboxRequest(c *gin.Context, req *service.CreateInboxRequest) error {
|
||||
if strings.Contains(c.ContentType(), "json") {
|
||||
return c.ShouldBindJSON(req)
|
||||
return bindCreateInboxJSON(c, req)
|
||||
}
|
||||
values, err := inboxFormValues(c)
|
||||
if err != nil {
|
||||
@@ -232,7 +233,7 @@ func bindCreateInboxRequest(c *gin.Context, req *service.CreateInboxRequest) err
|
||||
|
||||
func bindUpdateInboxRequest(c *gin.Context, req *service.UpdateInboxRequest) error {
|
||||
if strings.Contains(c.ContentType(), "json") {
|
||||
return c.ShouldBindJSON(req)
|
||||
return bindUpdateInboxJSON(c, req)
|
||||
}
|
||||
values, err := inboxFormValues(c)
|
||||
if err != nil {
|
||||
@@ -268,6 +269,70 @@ func inboxFormValues(c *gin.Context) (map[string][]string, error) {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func bindCreateInboxJSON(c *gin.Context, req *service.CreateInboxRequest) error {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(body, req); err != nil {
|
||||
return err
|
||||
}
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
hoistInboxChannelJSONFields(&req.Channel, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func bindUpdateInboxJSON(c *gin.Context, req *service.UpdateInboxRequest) error {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(body, req); err != nil {
|
||||
return err
|
||||
}
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
hoistInboxChannelJSONFields(&req.Channel, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func hoistInboxChannelJSONFields(channel *map[string]any, raw map[string]any) {
|
||||
for key, value := range raw {
|
||||
if !isInboxChannelConfigKey(key) || value == nil {
|
||||
continue
|
||||
}
|
||||
if *channel == nil {
|
||||
*channel = map[string]any{}
|
||||
}
|
||||
if _, exists := (*channel)[key]; !exists {
|
||||
(*channel)[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isInboxChannelConfigKey(key string) bool {
|
||||
switch key {
|
||||
case "website_url", "widget_color", "welcome_title", "welcome_tagline", "reply_time",
|
||||
"pre_chat_form_enabled", "pre_chat_form_options", "continuity_via_email", "hmac_mandatory",
|
||||
"allowed_domains", "selected_feature_flags", "webhook_url", "additional_attributes",
|
||||
"email", "forward_to_email", "imap_login", "imap_password", "imap_address", "imap_port",
|
||||
"imap_enabled", "imap_enable_ssl", "imap_authentication", "smtp_login", "smtp_password",
|
||||
"smtp_address", "smtp_port", "smtp_enabled", "smtp_domain", "smtp_enable_ssl_tls",
|
||||
"smtp_enable_starttls_auto", "smtp_openssl_verify_mode", "smtp_authentication", "provider",
|
||||
"provider_config", "phone_number", "message_templates", "account_sid", "auth_token", "api_key_sid",
|
||||
"api_key_secret", "messaging_service_sid", "medium", "content_templates", "twiml_app_sid",
|
||||
"voice_enabled", "line_channel_id", "line_channel_secret", "line_channel_token", "bot_token":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func applyCreateInboxForm(req *service.CreateInboxRequest, values map[string][]string) {
|
||||
req.Channel = map[string]any{}
|
||||
for key, list := range values {
|
||||
|
||||
@@ -243,6 +243,120 @@ func TestInboxHandler_ChatwootCreateUpdateRequestBinding(t *testing.T) {
|
||||
require.Len(t, inboxParityObject(t, showUpdated)["working_hours"].([]any), 7)
|
||||
}
|
||||
|
||||
func TestInboxHandler_ChatwootChannelSpecificConfigDepth(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open("file:inbox_handler_channel_depth?mode=memory&cache=shared"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
sqlDB, dbErr := db.DB()
|
||||
if dbErr == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
})
|
||||
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.Inbox{}, &model.WorkingHour{}))
|
||||
|
||||
account := &model.Account{Name: "Inbox Channel Depth", Locale: "en", Active: true}
|
||||
require.NoError(t, db.Create(account).Error)
|
||||
router := setupInboxParityRouter(db)
|
||||
|
||||
emailCreate := inboxParityRequest(t, router, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/inboxes/", account.ID), map[string]any{
|
||||
"name": "Email Support",
|
||||
"channel": map[string]any{
|
||||
"type": "email",
|
||||
"email": "support@example.com",
|
||||
"forward_to_email": "support+forward@example.test",
|
||||
"imap_enabled": true,
|
||||
"imap_login": "imap-user",
|
||||
"imap_password": "imap-secret",
|
||||
"imap_address": "imap.example.com",
|
||||
"imap_port": 993,
|
||||
"imap_enable_ssl": true,
|
||||
"imap_authentication": "plain",
|
||||
"smtp_enabled": true,
|
||||
"smtp_login": "smtp-user",
|
||||
"smtp_password": "smtp-secret",
|
||||
"smtp_address": "smtp.example.com",
|
||||
"smtp_port": 587,
|
||||
"smtp_domain": "example.com",
|
||||
"smtp_enable_starttls_auto": true,
|
||||
"smtp_enable_ssl_tls": false,
|
||||
"smtp_openssl_verify_mode": "none",
|
||||
"smtp_authentication": "login",
|
||||
},
|
||||
})
|
||||
require.Equal(t, http.StatusOK, emailCreate.Code, emailCreate.Body.String())
|
||||
emailData := inboxParityObject(t, emailCreate)
|
||||
require.Equal(t, "Channel::Email", emailData["channel_type"])
|
||||
require.Equal(t, "support@example.com", emailData["email"])
|
||||
require.Equal(t, "support+forward@example.test", emailData["forward_to_email"])
|
||||
require.Equal(t, true, emailData["forwarding_enabled"])
|
||||
require.Equal(t, "imap.example.com", emailData["imap_address"])
|
||||
require.Equal(t, float64(993), emailData["imap_port"])
|
||||
require.Equal(t, "smtp.example.com", emailData["smtp_address"])
|
||||
require.Equal(t, float64(587), emailData["smtp_port"])
|
||||
|
||||
emailID := uint(emailData["id"].(float64))
|
||||
smtpUpdate := inboxParityRequest(t, router, http.MethodPatch, fmt.Sprintf("/api/v1/accounts/%d/inboxes/%d", account.ID, emailID), map[string]any{
|
||||
"smtp_enabled": true,
|
||||
"smtp_address": "smtp2.example.com",
|
||||
"smtp_port": 465,
|
||||
"smtp_login": "smtp2-user",
|
||||
"smtp_password": "smtp2-secret",
|
||||
"smtp_domain": "example.org",
|
||||
"smtp_enable_ssl_tls": true,
|
||||
"smtp_enable_starttls_auto": false,
|
||||
"smtp_openssl_verify_mode": "peer",
|
||||
"smtp_authentication": "login",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, smtpUpdate.Code, smtpUpdate.Body.String())
|
||||
smtpData := inboxParityObject(t, smtpUpdate)
|
||||
require.Equal(t, "smtp2.example.com", smtpData["smtp_address"])
|
||||
require.Equal(t, float64(465), smtpData["smtp_port"])
|
||||
require.Equal(t, true, smtpData["smtp_enable_ssl_tls"])
|
||||
require.Equal(t, false, smtpData["smtp_enable_starttls_auto"])
|
||||
|
||||
whatsappCreate := inboxParityRequest(t, router, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/inboxes/", account.ID), map[string]any{
|
||||
"name": "WhatsApp",
|
||||
"channel": map[string]any{
|
||||
"type": "whatsapp",
|
||||
"phone_number": "+1555010000",
|
||||
"provider": "whatsapp_cloud",
|
||||
"provider_config": map[string]any{
|
||||
"api_key": "wa-key",
|
||||
"phone_number_id": "phone-id",
|
||||
"business_account_id": "waba-id",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.Equal(t, http.StatusOK, whatsappCreate.Code, whatsappCreate.Body.String())
|
||||
whatsappData := inboxParityObject(t, whatsappCreate)
|
||||
require.Equal(t, "Channel::Whatsapp", whatsappData["channel_type"])
|
||||
require.Equal(t, "+1555010000", whatsappData["phone_number"])
|
||||
providerConfig := whatsappData["provider_config"].(map[string]any)
|
||||
require.Equal(t, "wa-key", providerConfig["api_key"])
|
||||
require.Equal(t, "phone-id", providerConfig["phone_number_id"])
|
||||
require.NotEmpty(t, providerConfig["webhook_verify_token"])
|
||||
|
||||
lineCreate := inboxParityRequest(t, router, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/inboxes/", account.ID), map[string]any{
|
||||
"name": "LINE",
|
||||
"channel": map[string]any{
|
||||
"type": "line",
|
||||
"line_channel_id": "line-id",
|
||||
"line_channel_secret": "line-secret",
|
||||
"line_channel_token": "line-token",
|
||||
},
|
||||
})
|
||||
require.Equal(t, http.StatusOK, lineCreate.Code, lineCreate.Body.String())
|
||||
lineData := inboxParityObject(t, lineCreate)
|
||||
require.Equal(t, "Channel::Line", lineData["channel_type"])
|
||||
require.Equal(t, "line-id", lineData["line_channel_id"])
|
||||
require.Equal(t, "line-secret", lineData["line_channel_secret"])
|
||||
require.Equal(t, "line-token", lineData["line_channel_token"])
|
||||
}
|
||||
|
||||
func setupInboxParityRouter(db *gorm.DB) *gin.Engine {
|
||||
inboxSvc := service.NewInboxService(repository.NewInboxRepo(db), nil, nil, nil, nil, nil, nil)
|
||||
handler := NewInboxHandler(inboxSvc)
|
||||
|
||||
@@ -82,15 +82,45 @@ func serializeInbox(inbox *model.Inbox) map[string]any {
|
||||
payload["auth_token"] = configValue(config, "auth_token")
|
||||
payload["account_sid"] = configValue(config, "account_sid")
|
||||
payload["api_key_sid"] = configValue(config, "api_key_sid")
|
||||
payload["voice_enabled"] = configValue(config, "voice_enabled")
|
||||
payload["voice_configured"] = configValue(config, "twiml_app_sid") != nil
|
||||
payload["has_api_key_secret"] = configValue(config, "api_key_secret") != nil
|
||||
payload["voice_call_webhook_url"] = configValue(config, "voice_call_webhook_url")
|
||||
payload["voice_status_webhook_url"] = configValue(config, "voice_status_webhook_url")
|
||||
case "Channel::Email":
|
||||
payload["email"] = configValue(config, "email")
|
||||
payload["forwarding_enabled"] = false
|
||||
payload["forwarding_enabled"] = configValue(config, "forward_to_email") != nil
|
||||
payload["forward_to_email"] = configValue(config, "forward_to_email")
|
||||
payload["imap_login"] = configValue(config, "imap_login")
|
||||
payload["imap_password"] = configValue(config, "imap_password")
|
||||
payload["imap_address"] = configValue(config, "imap_address")
|
||||
payload["imap_port"] = configValue(config, "imap_port")
|
||||
payload["imap_enabled"] = configValue(config, "imap_enabled")
|
||||
payload["imap_enable_ssl"] = configValue(config, "imap_enable_ssl")
|
||||
payload["imap_authentication"] = configValue(config, "imap_authentication")
|
||||
payload["smtp_login"] = configValue(config, "smtp_login")
|
||||
payload["smtp_password"] = configValue(config, "smtp_password")
|
||||
payload["smtp_address"] = configValue(config, "smtp_address")
|
||||
payload["smtp_port"] = configValue(config, "smtp_port")
|
||||
payload["smtp_enabled"] = configValue(config, "smtp_enabled")
|
||||
payload["smtp_domain"] = configValue(config, "smtp_domain")
|
||||
payload["smtp_enable_ssl_tls"] = configValue(config, "smtp_enable_ssl_tls")
|
||||
payload["smtp_enable_starttls_auto"] = configValue(config, "smtp_enable_starttls_auto")
|
||||
payload["smtp_openssl_verify_mode"] = configValue(config, "smtp_openssl_verify_mode")
|
||||
payload["smtp_authentication"] = configValue(config, "smtp_authentication")
|
||||
case "Channel::Whatsapp":
|
||||
payload["phone_number"] = configValue(config, "phone_number")
|
||||
payload["message_templates"] = configValue(config, "message_templates")
|
||||
payload["provider_config"] = configValue(config, "provider_config")
|
||||
payload["reauthorization_required"] = configValue(config, "reauthorization_required")
|
||||
payload["voice_enabled"] = configValue(config, "voice_enabled")
|
||||
case "Channel::Line":
|
||||
payload["line_channel_id"] = configValue(config, "line_channel_id")
|
||||
payload["line_channel_secret"] = configValue(config, "line_channel_secret")
|
||||
payload["line_channel_token"] = configValue(config, "line_channel_token")
|
||||
case "Channel::Sms":
|
||||
payload["phone_number"] = configValue(config, "phone_number")
|
||||
payload["provider_config"] = configValue(config, "provider_config")
|
||||
}
|
||||
|
||||
return payload
|
||||
|
||||
@@ -13,8 +13,11 @@ package v1
|
||||
// - GET /api/v1/accounts/:id/channels/twilio_channel → list Twilio SMS channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -49,9 +52,12 @@ func NewTwilioChannelHandler(
|
||||
// CreateTwilioSMSChannelRequest is the DTO for creating a Twilio SMS channel.
|
||||
type CreateTwilioSMSChannelRequest struct {
|
||||
AccountSID string `json:"account_sid" validate:"required"`
|
||||
APIKeySID string `json:"api_key_sid"`
|
||||
AuthToken string `json:"auth_token" validate:"required"`
|
||||
PhoneNumber string `json:"phone_number" validate:"required"`
|
||||
MessagingServiceSID string `json:"messaging_service_sid"`
|
||||
Medium string `json:"medium"`
|
||||
Name string `json:"name"`
|
||||
InboxName string `json:"inbox_name"`
|
||||
}
|
||||
|
||||
@@ -66,19 +72,25 @@ func (h *TwilioChannelHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var req CreateTwilioSMSChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
applogger.L().Errorf("Failed to bind Twilio SMS channel create request: %v", err)
|
||||
req, bindErr := bindTwilioCreateRequest(c)
|
||||
if bindErr != nil {
|
||||
applogger.L().Errorf("Failed to bind Twilio SMS channel create request: %v", bindErr)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||
return
|
||||
}
|
||||
if req.AccountSID == "" || req.AuthToken == "" || (req.PhoneNumber == "" && req.MessagingServiceSID == "") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
medium := firstNonEmptyString(req.Medium, "sms")
|
||||
phoneNumber := twilioPhoneNumberForMedium(req.PhoneNumber, medium)
|
||||
|
||||
// Create the channel record first (without InboxID)
|
||||
ch := &channelmodel.ChannelTwilioSMS{
|
||||
AccountSID: req.AccountSID,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
PhoneNumber: phoneNumber,
|
||||
MessagingServiceSID: req.MessagingServiceSID,
|
||||
AccountID: uint(accountID),
|
||||
}
|
||||
@@ -92,12 +104,23 @@ func (h *TwilioChannelHandler) Create(c *gin.Context) {
|
||||
// Create inbox for the Twilio SMS channel
|
||||
inboxName := req.InboxName
|
||||
if inboxName == "" {
|
||||
inboxName = req.PhoneNumber
|
||||
inboxName = req.Name
|
||||
}
|
||||
if inboxName == "" {
|
||||
inboxName = firstNonEmptyString(phoneNumber, req.MessagingServiceSID)
|
||||
}
|
||||
inboxReq := service.CreateInboxRequest{
|
||||
Name: inboxName,
|
||||
ChannelType: "twilio_sms",
|
||||
Enabled: true,
|
||||
Channel: map[string]any{
|
||||
"account_sid": req.AccountSID,
|
||||
"auth_token": req.AuthToken,
|
||||
"api_key_sid": req.APIKeySID,
|
||||
"phone_number": phoneNumber,
|
||||
"messaging_service_sid": req.MessagingServiceSID,
|
||||
"medium": medium,
|
||||
},
|
||||
}
|
||||
|
||||
inbox, err := h.inboxSvc.Create(ctx, uint(accountID), inboxReq)
|
||||
@@ -117,10 +140,44 @@ func (h *TwilioChannelHandler) Create(c *gin.Context) {
|
||||
applogger.L().Errorf("Failed to update Twilio SMS channel with inbox_id: %v", err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"channel": ch,
|
||||
"inbox": inbox,
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeInbox(inbox))
|
||||
}
|
||||
|
||||
func twilioPhoneNumberForMedium(phoneNumber, medium string) string {
|
||||
if medium == "whatsapp" && phoneNumber != "" && !strings.HasPrefix(phoneNumber, "whatsapp:") {
|
||||
return "whatsapp:" + phoneNumber
|
||||
}
|
||||
return phoneNumber
|
||||
}
|
||||
|
||||
func firstNonEmptyString(values ...string) string {
|
||||
for _, value := range values {
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func bindTwilioCreateRequest(c *gin.Context) (CreateTwilioSMSChannelRequest, error) {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
return CreateTwilioSMSChannelRequest{}, err
|
||||
}
|
||||
var req CreateTwilioSMSChannelRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
return CreateTwilioSMSChannelRequest{}, err
|
||||
}
|
||||
if req.AccountSID != "" || req.AuthToken != "" {
|
||||
return req, nil
|
||||
}
|
||||
var wrapped struct {
|
||||
TwilioChannel CreateTwilioSMSChannelRequest `json:"twilio_channel"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &wrapped); err != nil {
|
||||
return CreateTwilioSMSChannelRequest{}, err
|
||||
}
|
||||
return wrapped.TwilioChannel, nil
|
||||
}
|
||||
|
||||
// Get retrieves a Twilio SMS channel by ID.
|
||||
@@ -213,13 +270,13 @@ func (h *TwilioChannelHandler) Update(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": ch.ID,
|
||||
"account_id": ch.AccountID,
|
||||
"inbox_id": ch.InboxID,
|
||||
"account_sid": ch.AccountSID,
|
||||
"phone_number": ch.PhoneNumber,
|
||||
"id": ch.ID,
|
||||
"account_id": ch.AccountID,
|
||||
"inbox_id": ch.InboxID,
|
||||
"account_sid": ch.AccountSID,
|
||||
"phone_number": ch.PhoneNumber,
|
||||
"messaging_service_sid": ch.MessagingServiceSID,
|
||||
"message": "Twilio SMS channel updated successfully",
|
||||
"message": "Twilio SMS channel updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -291,4 +348,4 @@ func (h *TwilioChannelHandler) List(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"channels": channels})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
||||
|
||||
"github.com/gochat/gochat/internal/channel/whatsapp"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
@@ -79,6 +79,7 @@ func setupTwilioTestRouter(handler *TwilioChannelHandler) *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
ag := r.Group("/api/v1/accounts/:id")
|
||||
ag.POST("/channels/twilio_channel", handler.Create)
|
||||
ag.POST("/twilio_sms_channels", handler.Create)
|
||||
ag.GET("/twilio_sms_channels", handler.List)
|
||||
ig := ag.Group("/inboxes/:inbox_id/twilio_sms_channels")
|
||||
@@ -95,32 +96,37 @@ func TestTwilioChannel_Create_Success(t *testing.T) {
|
||||
router := setupTwilioTestRouter(handler)
|
||||
accountID := twilioAccountID(db)
|
||||
|
||||
body := CreateTwilioSMSChannelRequest{
|
||||
AccountSID: "ACtest123",
|
||||
AuthToken: "authtoken_secret",
|
||||
PhoneNumber: "+15551234567",
|
||||
InboxName: "Support SMS",
|
||||
body := map[string]any{
|
||||
"twilio_channel": map[string]any{
|
||||
"account_sid": "ACtest123",
|
||||
"api_key_sid": "SKtest123",
|
||||
"auth_token": "authtoken_secret",
|
||||
"phone_number": "+15551234567",
|
||||
"messaging_service_sid": "MG123",
|
||||
"medium": "sms",
|
||||
"name": "Support SMS",
|
||||
},
|
||||
}
|
||||
b, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/"+accountID+"/twilio_sms_channels", bytes.NewReader(b))
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/"+accountID+"/channels/twilio_channel", bytes.NewReader(b))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
channelData, ok := resp["channel"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "ACtest123", channelData["account_sid"])
|
||||
assert.Equal(t, "+15551234567", channelData["phone_number"])
|
||||
|
||||
// Verify inbox was created
|
||||
inboxData, ok := resp["inbox"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "Support SMS", inboxData["name"])
|
||||
assert.Equal(t, "Support SMS", resp["name"])
|
||||
assert.Equal(t, "Channel::TwilioSms", resp["channel_type"])
|
||||
assert.Equal(t, "ACtest123", resp["account_sid"])
|
||||
assert.Equal(t, "SKtest123", resp["api_key_sid"])
|
||||
assert.Equal(t, "+15551234567", resp["phone_number"])
|
||||
assert.Equal(t, "MG123", resp["messaging_service_sid"])
|
||||
assert.Equal(t, "sms", resp["medium"])
|
||||
require.NotContains(t, resp, "channel")
|
||||
require.NotContains(t, resp, "inbox")
|
||||
}
|
||||
|
||||
func TestTwilioChannel_Create_InvalidAccountID(t *testing.T) {
|
||||
@@ -357,10 +363,10 @@ func TestTwilioChannel_List_Success(t *testing.T) {
|
||||
// Seed multiple channels
|
||||
for i := 0; i < 3; i++ {
|
||||
ch := &channelmodel.ChannelTwilioSMS{
|
||||
AccountSID: "AClist" + strconv.Itoa(i),
|
||||
PhoneNumber: "+1555" + strconv.Itoa(i) + "00000",
|
||||
AccountID: accountID,
|
||||
InboxID: uint(i + 100), // unique inbox IDs to satisfy uniqueIndex constraint
|
||||
AccountSID: "AClist" + strconv.Itoa(i),
|
||||
PhoneNumber: "+1555" + strconv.Itoa(i) + "00000",
|
||||
AccountID: accountID,
|
||||
InboxID: uint(i + 100), // unique inbox IDs to satisfy uniqueIndex constraint
|
||||
}
|
||||
require.NoError(t, db.Create(ch).Error)
|
||||
}
|
||||
@@ -403,4 +409,4 @@ func TestTwilioChannel_List_Empty(t *testing.T) {
|
||||
channels, ok := resp["channels"].([]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Len(t, channels, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +323,24 @@ func buildInitialInboxChannelConfig(channelType string, channel map[string]any)
|
||||
mergeInboxChannelConfig(config, channel)
|
||||
switch channelType {
|
||||
case "web_widget":
|
||||
if _, ok := config["allowed_domains"]; !ok {
|
||||
config["allowed_domains"] = ""
|
||||
}
|
||||
if _, ok := config["continuity_via_email"]; !ok {
|
||||
config["continuity_via_email"] = true
|
||||
}
|
||||
if _, ok := config["hmac_mandatory"]; !ok {
|
||||
config["hmac_mandatory"] = false
|
||||
}
|
||||
if _, ok := config["pre_chat_form_enabled"]; !ok {
|
||||
config["pre_chat_form_enabled"] = false
|
||||
}
|
||||
if _, ok := config["reply_time"]; !ok {
|
||||
config["reply_time"] = "in_a_few_minutes"
|
||||
}
|
||||
if _, ok := config["selected_feature_flags"]; !ok {
|
||||
config["selected_feature_flags"] = []string{"attachments", "emoji_picker", "end_conversation"}
|
||||
}
|
||||
if _, ok := config["website_token"]; !ok {
|
||||
if token, err := widgetGenerateToken(12); err == nil {
|
||||
config["website_token"] = token
|
||||
@@ -338,6 +356,12 @@ func buildInitialInboxChannelConfig(channelType string, channel map[string]any)
|
||||
config["widget_color"] = "#1f93ff"
|
||||
}
|
||||
case "api":
|
||||
if _, ok := config["additional_attributes"]; !ok {
|
||||
config["additional_attributes"] = map[string]any{}
|
||||
}
|
||||
if _, ok := config["hmac_mandatory"]; !ok {
|
||||
config["hmac_mandatory"] = false
|
||||
}
|
||||
if _, ok := config["identifier"]; !ok {
|
||||
if token, err := widgetGenerateToken(12); err == nil {
|
||||
config["identifier"] = token
|
||||
@@ -347,6 +371,36 @@ func buildInitialInboxChannelConfig(channelType string, channel map[string]any)
|
||||
if _, ok := config["hmac_token"]; !ok {
|
||||
config["hmac_token"] = generateInboxSecret()
|
||||
}
|
||||
case "email":
|
||||
if _, ok := config["imap_authentication"]; !ok {
|
||||
config["imap_authentication"] = "plain"
|
||||
}
|
||||
if _, ok := config["imap_enable_ssl"]; !ok {
|
||||
config["imap_enable_ssl"] = true
|
||||
}
|
||||
if _, ok := config["smtp_authentication"]; !ok {
|
||||
config["smtp_authentication"] = "login"
|
||||
}
|
||||
if _, ok := config["smtp_enable_starttls_auto"]; !ok {
|
||||
config["smtp_enable_starttls_auto"] = true
|
||||
}
|
||||
if _, ok := config["smtp_enable_ssl_tls"]; !ok {
|
||||
config["smtp_enable_ssl_tls"] = false
|
||||
}
|
||||
if _, ok := config["smtp_openssl_verify_mode"]; !ok {
|
||||
config["smtp_openssl_verify_mode"] = "none"
|
||||
}
|
||||
case "whatsapp":
|
||||
providerConfig, _ := config["provider_config"].(map[string]any)
|
||||
if providerConfig == nil {
|
||||
providerConfig = map[string]any{}
|
||||
config["provider_config"] = providerConfig
|
||||
}
|
||||
if mapString(config, "provider") == "whatsapp_cloud" {
|
||||
if _, ok := providerConfig["webhook_verify_token"]; !ok {
|
||||
providerConfig["webhook_verify_token"] = generateInboxSecret()
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(config, "type")
|
||||
return config
|
||||
|
||||
Reference in New Issue
Block a user